diff --git a/.bazelrc b/.bazelrc index ce13ad993..bb2613f3a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -51,3 +51,9 @@ build:g++ --cxxopt=-Wno-class-memaccess build:g++ --cxxopt=-Wno-deprecated-declarations # For string_fortified build:g++ --cxxopt=-Wno-stringop-truncation + +# C++17 is required to build ZetaSQL, hence `-cxxopt=-std=c++17`. On MacOS +# `--host_cxxopt=-std=c++17` is also needed. +build --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 +run --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 +test --cxxopt=-std=c++17 --host_cxxopt=-std=c++17 diff --git a/.bazelversion b/.bazelversion index 6abaeb2f9..f22d756da 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -6.2.0 +6.5.0 diff --git a/Dockerfile b/Dockerfile index 031d0f1e0..252c6810c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get -qq install -y default-jre default-jdk RUN apt-get update && apt-get -qq install curl tar build-essential wget \ python python3 zip unzip -ENV BAZEL_VERSION=6.2.0 +ENV BAZEL_VERSION=6.5.0 # Install bazel from source RUN mkdir -p bazel && \ @@ -38,10 +38,31 @@ RUN add-apt-repository ppa:ubuntu-toolchain-r/test && \ --slave /usr/bin/g++ g++ /usr/bin/g++-11 && \ update-alternatives --set gcc /usr/bin/gcc-11 + +# To support fileNames with non-ascii characters +RUN apt-get -qq install locales && locale-gen en_US.UTF-8 +ENV LANG en_US.UTF-8 + COPY . /zetasql +# Create a new user zetasql to avoid running as root. +RUN useradd -ms /bin/bash zetasql +RUN chown -R zetasql:zetasql /zetasql +USER zetasql + ENV BAZEL_ARGS="--config=g++" +# Pre-build the binary for execute_query so that users can try out zetasql +# directly. Users can modify the target in the docker file or enter the +# container and build other targets as needed. RUN cd zetasql && \ CC=/usr/bin/gcc CXX=/usr/bin/g++ \ - bazel build ${BAZEL_ARGS} ... + bazel build ${BAZEL_ARGS} -c opt //zetasql/tools/execute_query:execute_query + +# Create a shortcut for execute_query. +ENV HOME=/home/zetasql +RUN mkdir -p $HOME/bin +RUN ln -s /zetasql/bazel-bin/zetasql/tools/execute_query/execute_query $HOME/bin/execute_query +ENV PATH=$PATH:$HOME/bin + +WORKDIR /zetasql diff --git a/README.md b/README.md index cbbf00d24..91feeb2ac 100644 --- a/README.md +++ b/README.md @@ -1,79 +1,198 @@ ## ZetaSQL - Analyzer Framework for SQL -ZetaSQL defines a language (grammar, types, data model, and semantics) as well -as a parser and analyzer. It is not itself a database or query engine. Instead -it is intended to be used by multiple engines wanting to provide consistent -behavior for all semantic analysis, name resolution, type checking, implicit -casting, etc. Specific query engines may not implement all features in the -ZetaSQL language and may give errors if specific features are not supported. For -example, engine A may not support any updates and engine B may not support -analytic functions. - -[ZetaSQL Language Guide](docs/README.md) - -[ZetaSQL ResolvedAST API](docs/resolved_ast.md) - -[ZetaSQL BigQuery Analysis Example](https://github.com/GoogleCloudPlatform/professional-services/tree/main/tools/zetasql-helper) - -## Status of Project and Roadmap - -This codebase is being open sourced in multiple phases: - -1. Parser and Analyzer **Complete** -2. Reference Implementation **In Progress** - - Base capability **Complete** - - Function library **In Progress** -3. Compliance Tests **Complete** - - includes framework for validating compliance of arbitrary engines -4. Misc tooling - - Improved Formatter **Complete** +ZetaSQL defines a SQL language (grammar, types, data model, semantics, and +function library) and +implements parsing and analysis for that language as a reusable component. +ZetaSQL is not itself a database or query engine. Instead, +it's intended to be used by multiple engines, to provide consistent +language and behavior (name resolution, type checking, implicit +casting, etc.). Specific query engines may implement a subset of features, +giving errors for unuspported features. +ZetaSQL's compliance test suite can be used to validate query engine +implementations are correct and consistent. + +ZetaSQL implements the ZetaSQL language, which is used across several of +Google's SQL products, both publicly and internally, including BigQuery, +Spanner, F1, BigTable, Dremel, Procella, and others. + +ZetaSQL and ZetaSQL have been described in these publications: + +* (CDMS 2022) [ZetaSQL: A SQL Language as a Component](https://cdmsworkshop.github.io/2022/Slides/Fri_C2.5_DavidWilhite.pptx) (Slides) +* (SIGMOD 2017) [Spanner: Becoming a SQL System](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/46103.pdf) -- See section 6. +* (VLDB 2024) [SQL Has Problems. We Can Fix Them: Pipe Syntax in SQL](https://research.google/pubs/pub1005959/) -- Describes ZetaSQL's new pipe query syntax. + +Some other documentation: + +* [ZetaSQL Language Reference](docs/README.md) +* [ZetaSQL Resolved AST](docs/resolved_ast.md), documenting the intermediate representation produced by the ZetaSQL analyzer. +* [ZetaSQL Toolkit](https://github.com/GoogleCloudPlatform/zetasql-toolkit), a project using ZetaSQL to analyze and understand queries against BigQuery, and other ZetaSQL engines. + +## Project Overview + +The main components and APIs are in these directories under `zetasql/`: + +* `zetasql/public`: Most public APIs are here. +* `zetasql/resolved_ast`: Defines the [Resolved AST](docs/resolved_ast.md), which the analyzer produces. +* `zetasql/parser`: The grammar and parser implementation. (Semi-public, since the parse trees are not a stable API.) +* `zetasql/analyzer`: The internal implementation of query analysis. +* `zetasql/reference_impl`: The reference implementation for executing queries. +* `zetasql/compliance`: Compliance test framework and compliance tests. +* `zetasql/public/functions`: Function implementations for engines to use. +* `zetasql/tools/execute_query`: Interactive query execution for debugging. +* `zetasql/java/com/google/zetasql`: Java APIs, implemented by calling a local RPC server. Multiplatform support is planned for the following platforms: - Linux (Ubuntu 20.04 is our reference platform, but others may work). - gcc-9+ is required, recent versions of clang may work. - MacOS (Experimental) - - Windows (version TDB) We do not provide any guarantees of API stability and *cannot accept contributions*. +## Running Queries with `execute_query` + +The `execute_query` tool can parse, analyze and run SQL +queries using the reference implementation. + +See [Execute Query](execute_query.md) for more details on using the tool. + +You can run it using binaries from +[Releases](https://github.com/google/zetasql/releases), or build it using the +instructions below. + +There are some runnable example queries in +[tpch examples](../zetasql/examples/tpch/README.md). + +### Getting and Running `execute_query` +#### Pre-built Binaries + +ZetaSQL provides pre-built binaries for `execute_query` for Linux and MacOS on +the [Releases](https://github.com/google/zetasql/releases) page. You can run +the downloaded binary like: + +```bash +./execute_query_linux --web +``` + +Note the prebuilt binaries require GCC-9+ and tzdata. If you run into dependency +issues, you can try running `execute_query` with Docker. See the +[Run with Docker](#run-with-docker) section. + +#### Running from a bazel build + +You can build `execute_query` with Bazel from source and run it by: + +```bash +bazel run zetasql/tools/execute_query:execute_query -- --web +``` + +#### Run with Docker + +You can run `execute_query` using Docker. First download the pre-built Docker +image `zetasql` or build your own from Dockerfile. See the instructions in the +[Build With Docker](#build-with-docker) section. + +Assuming your Docker image name is MyZetaSQLImage, run: -## Flags -ZetaSQL uses the Abseil [Flags](https://abseil.io/blog/20190509-flags) library -to handle commandline flags. Unless otherwise documented, all flags are for -debugging purposes only and may change, stop working or be removed at any time. +```bash +sudo docker run --init -it -h=$(hostname) -p 8080:8080 MyZetasqlImage execute_query --web +``` +Argument descriptions: + +* `--init`: Allows `execute_query` to handle signals properly. +* `-it`: Runs the container in interactive mode. +* `-h=$(hostname)`: Makes the hostname of the container the same as that of the + host. +* `-p 8080:8080`: Sets up port forwarding. + +`-h=$(hostname)` and `-p 8080:8080` together make the URL address of the +web server accessible from the host machine. + +Alternatively, you can run this to start a bash shell, and then run +`execute_query` inside: + +```bash +sudo docker run --init -it -h=$(hostname) -p 8080:8080 MyZetasqlImage + +# Inside the container bash shell +execute_query --web +``` ## How to Build -ZetaSQL uses [bazel](https://bazel.build) for building and dependency -resolution. After installing bazel (check .bazelversion for the specific version -of bazel we test with, but other versions may work), simply run: +### Build with Bazel + +ZetaSQL uses [Bazel](https://bazel.build) for building and dependency +resolution. Instructions for installing Bazel can be found in +https://bazel.build/install. The Bazel version that ZetaSQL uses is specified in +the `.bazelversion` file. + +Besides Bazel, the following dependencies are also needed: + +* GCC-9+ or equivalent Clang +* tzdata + +`tzdata` provides the support for time zone information. It is generally +available on MacOS. If you run Linux and it is not pre-installed, you can +install it with `apt-get install tzdata`. + +Once the dependencies are installed, you can build or run ZetaSQL targets as +needed, for example: + +```bash +# Build everything. +bazel build ... + +# Build and run the execute_query tool. +bazel run //zetasql/tools/execute_query:execute_query -- --web + +# The built binary can be found under bazel-bin and run directly. +bazel-bin/tools/execute_query:execute_query --web + +# Build and run a test. +bazel test //zetasql/parser:parser_set_test +``` + +Some Mac users may experience build issues due to the Python error +`ModuleNotFoundError: no module named 'google.protobuf'`. To resolve it, run +`pip install protobuf==` to install python protobuf. The protobuf +version can be found in the `zetasql_deps_step_2.bzl` file. + +### Build with Docker + +ZetaSQL also provides a `Dockerfile` which configures all the dependencies so +that users can build ZetaSQL more easily across different platforms. + +To build the Docker image locally (called MyZetaSQLImage here), run: + +```bash +sudo docker build . -t MyZetaSQLImage -f Dockerfile +``` -```bazel build ...``` +Alternatively, ZetaSQL provides pre-built Docker images named `zetasql`. See the +[Releases](https://github.com/google/zetasql/releases) page. You can load the +downloaded image by: -If your Mac build fails due the python error - `ModuleNotFoundError: no module named 'google.protobuf'`, run - `pip install protobuf==` to install python protobuf first. The - protobuf version can be found in the zetasql_deps_step_2.bzl file. +```bash +sudo docker load -i /path/to/the/downloaded/zetasql_docker.tar +``` -## How to add as a Dependency in bazel -See the (WORKSPACE) file, as it is a little unusual. +To run builds or other commands inside the Docker environment, run this command +to open a bash shell inside the container: -### With docker - TODO: Add docker build instructions. +```bash +# Start a bash shell running inside the Docker container. +sudo docker run -it MyZetaSQLImage +``` -## Example Usage -A very basic command line tool is available to run simple queries with the -reference implementation: -```bazel run //zetasql/tools/execute_query:execute_query -- "select 1 + 1;"``` +Then you can run the commands from the [Build with Bazel](#build-with-bazel) +section above. -The reference implementation is not yet completely released and currently -supports only a subset of functions and types. ## Differential Privacy -For questions, documentation and examples of ZetaSQLs implementation of +For questions, documentation, and examples of ZetaSQL's implementation of Differential Privacy, please check out (https://github.com/google/differential-privacy). diff --git a/bazel/boost.BUILD b/bazel/boost.BUILD new file mode 100644 index 000000000..1c757ee4d --- /dev/null +++ b/bazel/boost.BUILD @@ -0,0 +1,53 @@ +# Copyright 2024 Google LLC +# +# Licensed 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 +# +# 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. +# + +licenses(["notice"]) # Apache v2.0 + + +load("@rules_foreign_cc//foreign_cc:defs.bzl", "boost_build") + +filegroup( + name = "all_srcs", + srcs = glob(["**"]), + visibility = ["//visibility:private"], +) + +boost_build( + name = "boost", + bootstrap_options = ["--without-icu"], + lib_source = ":all_srcs", + out_static_libs = select({ + "//conditions:default": [ + "libboost_atomic.a", + "libboost_filesystem.a", + "libboost_program_options.a", + "libboost_regex.a", + "libboost_system.a", + "libboost_thread.a", + ], + }), + user_options = [ + "-j4", + "--with-filesystem", + "--with-program_options", + "--with-regex", + "--with-system", + "--with-thread", + "variant=release", + "link=static", + "threading=multi", + ], + visibility = ["//visibility:public"], +) diff --git a/bazel/civetweb.BUILD b/bazel/civetweb.BUILD new file mode 100644 index 000000000..5cc6271d9 --- /dev/null +++ b/bazel/civetweb.BUILD @@ -0,0 +1,59 @@ +# Copyright 2024 Google LLC +# +# Licensed 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 +# +# 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. +# + +licenses(["notice"]) # Apache v2.0 + +package( + default_visibility = ["//visibility:public"], +) + +load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") + + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +cmake( + name = "civetweb", + cache_entries = { + "CMAKE_C_FLAGS": "-fPIC", + "CMAKE_CXX_FLAGS": "-fPIC", + }, + generate_args = [ + "-DCIVETWEB_ENABLE_CXX=ON", + "-DBUILD_SHARED_LIBS=OFF", + ], + lib_source = ":all", + out_static_libs = select({ + "@bazel_tools//src/conditions:darwin": [ + "libcivetweb.a", + "libcivetweb-cpp.a" + ], + "//conditions:default": [ + "libcivetweb.a", + "libcivetweb-cpp.a", + ]}), + # We have to disable LTO here because otherwise we hit an internal error in gold linker: + # "/usr/bin/ld.gold: internal error in set_xindex" when civetweb outputs static libs rather + # than shared libs. + # + # "-ldl" is needed because otherwise we can run into link errors like "error: undefined + # reference to 'dlopen'" on some platforms. + linkopts = ["-fno-lto", "-ldl"], + # TODO: Remove this tag once civetweb have all dependencies without accessing network. + tags = ["requires-network"], +) diff --git a/bazel/grpc_extra_deps.patch b/bazel/grpc_extra_deps.patch new file mode 100644 index 000000000..4b326f2b6 --- /dev/null +++ b/bazel/grpc_extra_deps.patch @@ -0,0 +1,15 @@ +diff --git bazel/grpc_extra_deps.bzl bazel/grpc_extra_deps.bzl +index 4d8afa3131..b42224501f 100644 +--- bazel/grpc_extra_deps.bzl ++++ bazel/grpc_extra_deps.bzl +@@ -52,10 +52,6 @@ def grpc_extra_deps(ignore_version_differences = False): + + api_dependencies() + +- go_rules_dependencies() +- go_register_toolchains(version = "1.18") +- gazelle_dependencies() +- + # Pull-in the go 3rd party dependencies for protoc_gen_validate, which is + # needed for building C++ xDS protos + go_third_party() diff --git a/bazel/mstch.BUILD b/bazel/mstch.BUILD new file mode 100644 index 000000000..cd3e8e694 --- /dev/null +++ b/bazel/mstch.BUILD @@ -0,0 +1,39 @@ +# Copyright 2024 Google LLC +# +# Licensed 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 +# +# 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. +# + +licenses(["notice"]) # Apache v2.0 + + +load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake") + +package( + default_visibility = ["//visibility:public"], +) + +filegroup( + name = "all", + srcs = glob(["**"]), +) + +cmake( + name = "mstch", + deps = ["@boost"], + cache_entries = { + "CMAKE_C_FLAGS": "-fPIC", + "CMAKE_CXX_FLAGS": "-fPIC", + }, + lib_source = ":all", + out_static_libs = ["libmstch.a"], +) diff --git a/bazel/protobuf-v3.6.1.3.patch b/bazel/protobuf-v3.6.1.3.patch deleted file mode 100644 index 654953e7c..000000000 --- a/bazel/protobuf-v3.6.1.3.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- protobuf.bzl -+++ protobuf.bzl -@@ -105,7 +105,7 @@ def _proto_gen_impl(ctx): - inputs += [plugin] - - if args: -- ctx.action( -+ ctx.actions.run( - inputs=inputs, - outputs=ctx.outputs.outs, - arguments=args + import_flags + [s.path for s in srcs], -@@ -130,7 +130,7 @@ proto_gen = rule( - "protoc": attr.label( - cfg = "host", - executable = True, -- single_file = True, -+ allow_single_file = True, - mandatory = True, - ), - "plugin": attr.label( diff --git a/bazel/textmapper.bzl b/bazel/textmapper.bzl index 23c26070b..bc77bd1b1 100644 --- a/bazel/textmapper.bzl +++ b/bazel/textmapper.bzl @@ -71,5 +71,4 @@ def tm_syntax( visibility = visibility, compatible_with = compatible_with, tools = [textmapper], - exec_compatible_with = ["@platforms//os:linux", "@platforms//os:macos"], ) diff --git a/bazel/zetasql_deps_step_1.bzl b/bazel/zetasql_deps_step_1.bzl index 66727b694..e18aeea54 100644 --- a/bazel/zetasql_deps_step_1.bzl +++ b/bazel/zetasql_deps_step_1.bzl @@ -27,33 +27,35 @@ def zetasql_deps_step_1(add_bazel_version = True): if add_bazel_version: zetasql_bazel_version() - # Install newer versions of io_bazel_rules_go and gazelle to import Textmapper from github - # Make sure this is installed first. Otherwise, grpc_deps.bzl would attempt to use older - # versions, which have some bugs when downloading the go toolchains. http_archive( name = "io_bazel_rules_go", - sha256 = "278b7ff5a826f3dc10f04feaf0b70d48b68748ccd512d7f98bf442077f043fe3", + integrity = "sha256-M6zErg9wUC20uJPJ/B3Xqb+ZjCPn/yxFF3QdQEmpdvg=", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.41.0/rules_go-v0.41.0.zip", - "https://github.com/bazelbuild/rules_go/releases/download/v0.41.0/rules_go-v0.41.0.zip", + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.48.0/rules_go-v0.48.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.48.0/rules_go-v0.48.0.zip", ], ) - http_archive( name = "bazel_gazelle", - sha256 = "d3fa66a39028e97d76f9e2db8f1b0c11c099e8e01bf363a923074784e451f809", + integrity = "sha256-MpOL2hbmcABjA1R5Bj2dJMYO2o15/Uc5Vj9Q0zHLMgk=", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.33.0/bazel-gazelle-v0.33.0.tar.gz", - "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.33.0/bazel-gazelle-v0.33.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.35.0/bazel-gazelle-v0.35.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.35.0/bazel-gazelle-v0.35.0.tar.gz", ], ) + http_archive( + name = "bazel_features", + sha256 = "5d7e4eb0bb17aee392143cd667b67d9044c270a9345776a5e5a3cccbc44aa4b3", + strip_prefix = "bazel_features-1.13.0", + url = "https://github.com/bazel-contrib/bazel_features/releases/download/v1.13.0/bazel_features-v1.13.0.tar.gz", + ) http_archive( name = "platforms", - sha256 = "5308fc1d8865406a49427ba24a9ab53087f17f5266a7aabbfc28823f3916e1ca", + sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.6/platforms-0.0.6.tar.gz", - "https://github.com/bazelbuild/platforms/releases/download/0.0.6/platforms-0.0.6.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz", + "https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz", ], ) if not native.existing_rule("bazel_skylib"): @@ -65,7 +67,13 @@ def zetasql_deps_step_1(add_bazel_version = True): "https://github.com/bazelbuild/bazel-skylib/releases/download/1.0.2/bazel-skylib-1.0.2.tar.gz", ], ) - + if not native.existing_rule("rules_proto"): + http_archive( + name = "rules_proto", + sha256 = "6fb6767d1bef535310547e03247f7518b03487740c11b6c6adb7952033fe1295", + strip_prefix = "rules_proto-6.0.2", + url = "https://github.com/bazelbuild/rules_proto/releases/download/6.0.2/rules_proto-6.0.2.tar.gz", + ) if not native.existing_rule("rules_foreign_cc"): http_archive( name = "rules_foreign_cc", diff --git a/bazel/zetasql_deps_step_2.bzl b/bazel/zetasql_deps_step_2.bzl index e529c691c..b449b359d 100644 --- a/bazel/zetasql_deps_step_2.bzl +++ b/bazel/zetasql_deps_step_2.bzl @@ -16,14 +16,18 @@ """ Step 2 to load ZetaSQL dependencies. """ -load("@bazel_gazelle//:deps.bzl", "go_repository") +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies", "go_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@rules_bison//bison:bison.bzl", "bison_register_toolchains") +load("@rules_flex//flex:flex.bzl", "flex_register_toolchains") # Followup from zetasql_deps_step_1.bzl load("@rules_foreign_cc//foreign_cc:repositories.bzl", "rules_foreign_cc_dependencies") load("@rules_m4//m4:m4.bzl", "m4_register_toolchains") -load("@rules_flex//flex:flex.bzl", "flex_register_toolchains") -load("@rules_bison//bison:bison.bzl", "bison_register_toolchains") +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies") +load("@rules_proto//proto:setup.bzl", "rules_proto_setup") +load("@rules_proto//proto:toolchains.bzl", "rules_proto_toolchains") def _load_deps_from_step_1(): rules_foreign_cc_dependencies() @@ -107,10 +111,10 @@ def zetasql_deps_step_2( # http_archive( name = "com_google_absl", - # Commit from 2023-03-16 - url = "https://github.com/abseil/abseil-cpp/archive/0697762c62cdb51ead8d9c2f0d299c5d4a4ff9db.tar.gz", - sha256 = "3439843ac7d7b9cc354dd6735b6790fa7589b73429bbda77976e0db61e92f1fd", - strip_prefix = "abseil-cpp-0697762c62cdb51ead8d9c2f0d299c5d4a4ff9db", + # Commit from 2024-05-31 + url = "https://github.com/abseil/abseil-cpp/archive/d06b82773e2306a99a8971934fb5845d5c04a170.tar.gz", + sha256 = "fd4c78078d160951f2317229511340f3e92344213bc145939995eea9ff9b9e48", + strip_prefix = "abseil-cpp-d06b82773e2306a99a8971934fb5845d5c04a170", ) # required by many python libraries @@ -173,12 +177,12 @@ py_library( # Abseil generally just does daily (or even subdaily) releases. None are # special, so just periodically update as necessary. # - # https://github.com/abseil/abseil-cpp/commits/master + # https://github.com/google/riegeli/commits/master # pick a recent release. # Hit the 'clipboard with a left arrow' icon to copy the commit hex # COMMIT= - # PREFIX=abseil-cpp- - # REPO=https://github.com/abseil/abseil-cpp/archive + # PREFIX=riegeli- + # REPO=https://github.com/google/riegeli/archive # URL=${REPO}/${COMMIT}.tar.gz # wget $URL # SHA256=$(sha256sum ${COMMIT}.tar.gz | cut -f1 -d' ') @@ -190,10 +194,10 @@ py_library( # http_archive( name = "com_google_riegeli", - # Commit from 2022-02-16 - url = "https://github.com/google/riegeli/archive/934428f44a6d120cb6c065315c788aa3a1be6b66.tar.gz", - sha256 = "a54dafa634db87723db106bc44ef365b1b442d8862aafbeb5f1d2e922049e587", - strip_prefix = "riegeli-934428f44a6d120cb6c065315c788aa3a1be6b66", + # Commit from 2024-07-31 + url = "https://github.com/google/riegeli/archive/31c4dd1295a48aa59ec0d669e42ed42861ffa3ad.tar.gz", + sha256 = "b811ddccc42321ecc4d8416fdf1f74fd122b074886b33daba2b6095706723068", + strip_prefix = "riegeli-31c4dd1295a48aa59ec0d669e42ed42861ffa3ad", ) if evaluator_deps: # Differential Privacy @@ -236,23 +240,13 @@ py_library( strip_prefix = "farmhash-816a4ae622e964763ca0862d9dbd19324a1eaf45", ) if analyzer_deps: - # We need to override protobuf's native dep, because they patch the - # the repo in an unhelpful way. - if not native.existing_rule("upb"): - http_archive( - name = "upb", - urls = ["https://github.com/protocolbuffers/upb/archive/0ea9f73be35e35db242ccc65aa9c87487b792324.tar.gz"], - sha256 = "046b5f134523eaad9265a41a2ec0701cc45973841070af2772e3578a9f3bfed0", - strip_prefix = "upb-0ea9f73be35e35db242ccc65aa9c87487b792324", - ) - # Protobuf if not native.existing_rule("com_google_protobuf"): http_archive( name = "com_google_protobuf", - urls = ["https://github.com/protocolbuffers/protobuf/archive/refs/tags/v4.23.3.tar.gz"], - sha256 = "21fcb4b0df6a8e6279e5843af8c9f2245919cf0d3ec2021c76fccc4fc4bf9aca", - strip_prefix = "protobuf-4.23.3", + urls = ["https://github.com/protocolbuffers/protobuf/releases/download/v25.2/protobuf-25.2.tar.gz"], + sha256 = "8ff511a64fc46ee792d3fe49a5a1bcad6f7dc50dfbba5a28b0e5b979c17f9871", + strip_prefix = "protobuf-25.2", ) if testing_deps: @@ -266,12 +260,18 @@ py_library( ) if analyzer_deps: # gRPC + rules_proto_dependencies() + rules_proto_setup() + rules_proto_toolchains() + if not native.existing_rule("com_github_grpc_grpc"): http_archive( name = "com_github_grpc_grpc", - urls = ["https://github.com/grpc/grpc/archive/v1.56.0.tar.gz"], - sha256 = "e034992a0b464042021f6d440f2090acc2422c103a322b0844e3921ccea981dc", - strip_prefix = "grpc-1.56.0", + urls = ["https://github.com/grpc/grpc/archive/refs/tags/v1.61.2.tar.gz"], + sha256 = "86f8773434c4b8a4b64c67c91a19a90991f0da0ba054bbeb299dc1bc95fad1e9", + strip_prefix = "grpc-1.61.2", + # from https://github.com/google/gvisor/blob/master/tools/grpc_extra_deps.patch + patches = ["@com_google_zetasql//bazel:grpc_extra_deps.patch"], ) if analyzer_deps: @@ -408,9 +408,12 @@ alias( m4_register_toolchains(version = "1.4.18") flex_register_toolchains(version = "2.6.4") bison_register_toolchains(version = "3.3.2") + go_rules_dependencies() + go_register_toolchains(version = "1.21.6") + gazelle_dependencies() go_repository( name = "com_github_inspirer_textmapper", - commit = "e0aa14dc6db169c7afdf6908e810b5d12bbae2db", + commit = "8fdc73e6bd65dc4478b9d6526fe6c282f9c8d25b", importpath = "github.com/inspirer/textmapper", ) @@ -433,3 +436,31 @@ alias( urls = ["https://github.com/unicode-org/icu/releases/download/release-65-1/icu4c-65_1-src.tgz"], patches = ["@com_google_zetasql//bazel:icu4c-64_2.patch"], ) + + http_archive( + name = "civetweb", + strip_prefix = "civetweb-1.16", + sha256 = "9f98e60ef418562ae57d6c8e64fb1b2d2b726201b7baee23b043d15538c81dac", + urls = [ + "https://github.com/civetweb/civetweb/archive/v1.16.zip", + ], + build_file = "@com_google_zetasql//bazel:civetweb.BUILD", + ) + + http_archive( + name = "mstch", + strip_prefix = "mstch-1.0.2", + sha256 = "a06980c2031cd9222b6356a2f3674064c6aa923c25a15a8acf2652769f3e6628", + urls = [ + "https://github.com/no1msd/mstch/archive/1.0.2.zip", + ], + build_file = "@com_google_zetasql//bazel:mstch.BUILD", + ) + + http_archive( + name = "boost", + build_file = "@com_google_zetasql//bazel:boost.BUILD", + sha256 = "be0d91732d5b0cc6fbb275c7939974457e79b54d6f07ce2e3dfdd68bef883b0b", + strip_prefix = "boost_1_85_0", + url = "https://archives.boost.io/release/1.85.0/source/boost_1_85_0.tar.gz", + ) diff --git a/docs/aggregate-function-calls.md b/docs/aggregate-function-calls.md new file mode 100644 index 000000000..a1412f4e8 --- /dev/null +++ b/docs/aggregate-function-calls.md @@ -0,0 +1,318 @@ + + + + +# Aggregate function calls + +An aggregate function is a function that summarizes the rows of a group into a +single value. When an aggregate function is used with the `OVER` clause, it +becomes a window function, which computes values over a group of rows and then +returns a single result for each row. + +## Aggregate function call syntax + +```sql +function_name( + [ DISTINCT ] + function_arguments + [ { IGNORE | RESPECT } NULLS ] + [ HAVING { MAX | MIN } having_expression ] + [ ORDER BY key [ { ASC | DESC } ] [, ... ] ] + [ LIMIT n ] +) +[ OVER over_clause ] +``` + +**Description** + +Each aggregate function supports all or a subset of the +aggregate function call syntax. You can use the following syntax to build an +aggregate function: + ++ `DISTINCT`: Each distinct value of + `expression` is aggregated only once into the result. ++ `IGNORE NULLS` or `RESPECT NULLS`: If `IGNORE NULLS` is + specified, the `NULL` values are excluded from the result. If + `RESPECT NULLS` is specified, the `NULL` values are included in the + result. If + neither is specified, the `NULL` values are included in the + result. ++ `HAVING MAX` or `HAVING MIN`: Restricts the set of rows that the + function aggregates by a maximum or minimum value. + For details, see [HAVING MAX and HAVING MIN clause][max_min_clause]. ++ `ORDER BY`: Specifies the order of the values. + + + For each sort key, the default sort direction is `ASC`. + + + `NULL` is the minimum possible value, so `NULL`s appear first + in `ASC` sorts and last in `DESC` sorts. + + + If you're using floating point data types, see + [Floating point semantics][floating-point-semantics] + on ordering and grouping. + + + The `ORDER BY` clause is supported only for aggregate functions that + depend on the order of their input. For those functions, if the + `ORDER BY` clause is omitted, the output is nondeterministic. + + + This `ORDER BY` clause can't be used if the `OVER` clause is used. + + + If `DISTINCT` is also specified, then + the sort key must be the same as `expression`. ++ `LIMIT`: Specifies the maximum number of `expression` inputs in the + result. + + + If the input is an `ARRAY` value, the limit applies to the number of input + arrays, not the number of elements in the arrays. An empty array counts + as `1`. A `NULL` array is not counted. + + + If the input is a `STRING` value, the limit applies to the number of input + strings, not the number of characters or bytes in the inputs. An empty + string counts as `1`. A `NULL` string is not counted. + + + The limit `n` must be a constant `INT64`. ++ `OVER`: If the aggregate function is also a window function, use this clause + to define a window of rows around the row being evaluated. For each row, + the aggregate function result is computed using the selected window of rows as + input. If this clause is used, aggregate function + clauses (i.e. + `DISTINCT`) can't be used. To learn more about the `OVER` clause, + see [Window function calls][window-function-calls]. + +**Details** + +The clauses in an aggregate function call are applied in the following order: + ++ `OVER` ++ `DISTINCT` ++ `IGNORE NULLS` or `RESPECT NULLS` ++ `HAVING MAX` or `HAVING MIN` ++ `ORDER BY` ++ `LIMIT` ++ `CLAMPED BETWEEN` + +When used in conjunction with a `GROUP BY` clause, the groups summarized +typically have at least one row. When the associated `SELECT` statement has +no `GROUP BY` clause or when certain aggregate function modifiers filter rows +from the group to be summarized, it is possible that the aggregate function +needs to summarize an empty group. + +## Restrict aggregation by a maximum or minimum value + + +Some aggregate functions support two optional clauses that are called +`HAVING MAX` and `HAVING MIN`. These clauses restrict the set of rows that a +function aggregates to rows that have a maximum or minimum value in a particular +column. + +### HAVING MAX clause + + +```sql +HAVING MAX having_expression +``` + +`HAVING MAX` restricts the set of rows that the function aggregates to those +having a value for `having_expression` equal to the maximum value for +`having_expression` within the group. The maximum value is equal to the result +of `MAX(having_expression)`. + +This clause ignores `NULL` values when computing the maximum value unless +`having_expression` evaluates to `NULL` for all rows. + +This clause supports all [orderable data types][agg-data-type-properties], +except for `ARRAY`. + +**Examples** + +In the following query, rows with the most inches of precipitation, `4`, are +added to a group, and then the `year` for one of these rows is produced. +Which row is produced is nondeterministic, not random. + +```sql +WITH + Precipitation AS ( + SELECT 2009 AS year, 'spring' AS season, 3 AS inches + UNION ALL + SELECT 2001, 'winter', 4 + UNION ALL + SELECT 2003, 'fall', 1 + UNION ALL + SELECT 2002, 'spring', 4 + UNION ALL + SELECT 2005, 'summer', 1 + ) +SELECT ANY_VALUE(year HAVING MAX inches) AS any_year_with_max_inches FROM Precipitation; + +/*--------------------------* + | any_year_with_max_inches | + +--------------------------+ + | 2001 | + *--------------------------*/ +``` + +In the following example, the average rainfall is returned for 2001, the most +recent year specified in the query. First, the query gets the rows with the +maximum value in the `year` column. Finally, the query averages the values in +the `inches` column (`9` and `1`): + +```sql +WITH + Precipitation AS ( + SELECT 2001 AS year, 'spring' AS season, 9 AS inches + UNION ALL + SELECT 2001, 'winter', 1 + UNION ALL + SELECT 2000, 'fall', 3 + UNION ALL + SELECT 2000, 'summer', 5 + UNION ALL + SELECT 2000, 'spring', 7 + UNION ALL + SELECT 2000, 'winter', 2 + ) +SELECT AVG(inches HAVING MAX year) AS average FROM Precipitation; + +/*---------* + | average | + +---------+ + | 5 | + *---------*/ +``` + +### HAVING MIN clause + + +```sql +HAVING MIN having_expression +``` + +`HAVING MIN` restricts the set of rows that the function aggregates to those +having a value for `having_expression` equal to the minimum value for +`having_expression` within the group. The minimum value is equal to the result +of `MIN(having_expression)`. + +This clause ignores `NULL` values when computing the minimum +value unless `having_expression` evaluates to `NULL` for all rows. + +This clause supports all [orderable data types][agg-data-type-properties], +except for `ARRAY`. + +**Examples** + +In the following query, rows with the fewest inches of precipitation, `1`, +are added to a group, and then the `year` for one of these rows is produced. +Which row is produced is nondeterministic, not random. + +```sql +WITH + Precipitation AS ( + SELECT 2009 AS year, 'spring' AS season, 3 AS inches + UNION ALL + SELECT 2001, 'winter', 4 + UNION ALL + SELECT 2003, 'fall', 1 + UNION ALL + SELECT 2002, 'spring', 4 + UNION ALL + SELECT 2005, 'summer', 1 + ) +SELECT ANY_VALUE(year HAVING MIN inches) AS any_year_with_min_inches FROM Precipitation; + +/*--------------------------* + | any_year_with_min_inches | + +--------------------------+ + | 2003 | + *--------------------------*/ +``` + +In the following example, the average rainfall is returned for 2000, the +earliest year specified in the query. First, the query gets the rows with +the minimum value in the `year` column, and finally, the query averages the +values in the `inches` column: + +```sql +WITH + Precipitation AS ( + SELECT 2001 AS year, 'spring' AS season, 9 AS inches + UNION ALL + SELECT 2001, 'winter', 1 + UNION ALL + SELECT 2000, 'fall', 3 + UNION ALL + SELECT 2000, 'summer', 5 + UNION ALL + SELECT 2000, 'spring', 7 + UNION ALL + SELECT 2000, 'winter', 2 + ) +SELECT AVG(inches HAVING MIN year) AS average FROM Precipitation; + +/*---------* + | average | + +---------+ + | 4.25 | + *---------*/ +``` + +## Aggregate function examples + +A simple aggregate function call for `COUNT`, `MIN`, and `MAX` looks like this: + +```sql +SELECT + COUNT(*) AS total_count, + COUNT(fruit) AS non_null_count, + MIN(fruit) AS min, + MAX(fruit) AS max +FROM + ( + SELECT NULL AS fruit + UNION ALL + SELECT 'apple' AS fruit + UNION ALL + SELECT 'pear' AS fruit + UNION ALL + SELECT 'orange' AS fruit + ) + +/*-------------+----------------+-------+------* + | total_count | non_null_count | min | max | + +-------------+----------------+-------+------+ + | 4 | 3 | apple | pear | + *-------------+----------------+-------+------*/ +``` + +In the following example, the average of `x` over a specified window is returned +for each row. To learn more about windows and how to use them, see +[Window function calls][window-function-calls]. + +```sql +SELECT + x, + AVG(x) OVER (ORDER BY x ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS avg +FROM UNNEST([0, 2, 4, 4, 5]) AS x; + +/*------+------* + | x | avg | + +------+------+ + | 0 | 0 | + | 2 | 1 | + | 4 | 3 | + | 4 | 4 | + | 5 | 4.5 | + *------+------*/ +``` + + + +[max_min_clause]: #max_min_clause + +[agg-data-type-properties]: https://github.com/google/zetasql/blob/master/docs/data-types.md#data_type_properties + +[window-function-calls]: https://github.com/google/zetasql/blob/master/docs/window-function-calls.md + +[floating-point-semantics]: https://github.com/google/zetasql/blob/master/docs/data-types.md#floating_point_semantics + + + diff --git a/docs/aggregate_functions.md b/docs/aggregate_functions.md index e7b765e19..38abf2d71 100644 --- a/docs/aggregate_functions.md +++ b/docs/aggregate_functions.md @@ -197,8 +197,9 @@ rows. Returns `NULL` when `expression` or `expression2` is `NULL` for all rows in the group. -`ANY_VALUE` behaves as if `RESPECT NULLS` is specified; -rows for which `expression` is `NULL` are considered and may be selected. +`ANY_VALUE` behaves as if `IGNORE NULLS` is specified; +rows for which `expression` is `NULL` are not considered and won't be +selected. To learn more about the optional aggregate clauses that you can pass into this function, see @@ -592,10 +593,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -891,10 +897,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -1079,10 +1090,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -1286,10 +1302,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -1353,10 +1374,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -1748,10 +1774,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see diff --git a/docs/array_functions.md b/docs/array_functions.md index 0bfaee690..f2161de0e 100644 --- a/docs/array_functions.md +++ b/docs/array_functions.md @@ -695,31 +695,72 @@ equality comparison logic as `SELECT DISTINCT`. **Examples** ```sql -WITH example AS ( - SELECT [1, 2, 3] AS arr UNION ALL - SELECT [1, 1, 1] AS arr UNION ALL - SELECT [1, 2, NULL] AS arr UNION ALL - SELECT [1, 1, NULL] AS arr UNION ALL - SELECT [1, NULL, NULL] AS arr UNION ALL - SELECT [] AS arr UNION ALL - SELECT CAST(NULL AS ARRAY) AS arr -) -SELECT - arr, - ARRAY_IS_DISTINCT(arr) as is_distinct -FROM example; - -/*-----------------+-------------* - | arr | is_distinct | - +-----------------+-------------+ - | [1, 2, 3] | TRUE | - | [1, 1, 1] | FALSE | - | [1, 2, NULL] | TRUE | - | [1, 1, NULL] | FALSE | - | [1, NULL, NULL] | FALSE | - | [] | TRUE | - | NULL | NULL | - *-----------------+-------------*/ +SELECT ARRAY_IS_DISTINCT([1, 2, 3]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | true | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, 1, 1]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | false | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, 2, NULL]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | true | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, 1, NULL]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | false | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, NULL, NULL]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | false | + *-------------*/ +``` +```sql +SELECT ARRAY_IS_DISTINCT([]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | true | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT(NULL) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | NULL | + *-------------*/ ``` ### `ARRAY_LAST` @@ -774,20 +815,15 @@ the `array_expression` is `NULL`. **Examples** ```sql -WITH items AS - (SELECT ["coffee", NULL, "milk" ] as list - UNION ALL - SELECT ["cake", "pie"] as list) -SELECT ARRAY_TO_STRING(list, ', ', 'NULL'), ARRAY_LENGTH(list) AS size -FROM items -ORDER BY size DESC; +SELECT + ARRAY_LENGTH(["coffee", NULL, "milk" ]) AS size_a, + ARRAY_LENGTH(["cake", "pie"]) AS size_b; -/*--------------------+------* - | list | size | - +--------------------+------+ - | coffee, NULL, milk | 3 | - | cake, pie | 2 | - *--------------------+------*/ +/*--------+--------* + | size_a | size_b | + +--------+--------+ + | 3 | 2 | + *--------+--------*/ ``` ### `ARRAY_MAX` @@ -885,23 +921,13 @@ Returns the input `ARRAY` with elements in reverse order. **Examples** ```sql -WITH example AS ( - SELECT [1, 2, 3] AS arr UNION ALL - SELECT [4, 5] AS arr UNION ALL - SELECT [] AS arr -) -SELECT - arr, - ARRAY_REVERSE(arr) AS reverse_arr -FROM example; +SELECT ARRAY_REVERSE([1, 2, 3]) AS reverse_arr -/*-----------+-------------* - | arr | reverse_arr | - +-----------+-------------+ - | [1, 2, 3] | [3, 2, 1] | - | [4, 5] | [5, 4] | - | [] | [] | - *-----------+-------------*/ +/*-------------* + | reverse_arr | + +-------------+ + | [3, 2, 1] | + *-------------*/ ``` ### `ARRAY_SLICE` @@ -1233,35 +1259,22 @@ and its preceding delimiter. **Examples** ```sql -WITH items AS - (SELECT ['coffee', 'tea', 'milk' ] as list - UNION ALL - SELECT ['cake', 'pie', NULL] as list) - -SELECT ARRAY_TO_STRING(list, '--') AS text -FROM items; +SELECT ARRAY_TO_STRING(['coffee', 'tea', 'milk', NULL], '--', 'MISSING') AS text /*--------------------------------* | text | +--------------------------------+ - | coffee--tea--milk | - | cake--pie | + | coffee--tea--milk--MISSING | *--------------------------------*/ ``` ```sql -WITH items AS - (SELECT ['coffee', 'tea', 'milk' ] as list - UNION ALL - SELECT ['cake', 'pie', NULL] as list) -SELECT ARRAY_TO_STRING(list, '--', 'MISSING') AS text -FROM items; +SELECT ARRAY_TO_STRING(['cake', 'pie', NULL], '--', 'MISSING') AS text /*--------------------------------* | text | +--------------------------------+ - | coffee--tea--milk | | cake--pie--MISSING | *--------------------------------*/ ``` diff --git a/docs/arrays.md b/docs/arrays.md index 04152058b..681a477f7 100644 --- a/docs/arrays.md +++ b/docs/arrays.md @@ -10,7 +10,8 @@ values of the same data type. You can construct arrays of simple data types, such as `INT64`, and complex data types, such as `STRUCT`s. The current exception to this is the `ARRAY` data type because arrays of arrays are not supported. To learn more about the `ARRAY` -data type, see [Array type][array-data-type]. +data type, including +`NULL` handling, see [Array type][array-data-type]. With ZetaSQL, you can construct array literals, build arrays from subqueries using the diff --git a/docs/collation-concepts.md b/docs/collation-concepts.md new file mode 100644 index 000000000..9b3243445 --- /dev/null +++ b/docs/collation-concepts.md @@ -0,0 +1,898 @@ + + + + +# Working with collation + + +ZetaSQL supports collation. You can learn more about collation +in this topic. + +## About collation + + +Collation determines how strings are sorted and compared in +[collation-supported operations][collate-operations]. If you would like to +customize collation for a collation-supported operation, you must +[assign a collation specification][collate-define] to at least one string +in the operation. Some operations can't use collation, but can +[pass collation through them][collate-propagate]. + +## Operations affected by collation + + +When an operation is affected by collation, this means that the operation +takes into consideration collation during the operation. +These query operations are affected by collation when sorting and comparing +strings: + +| Operations | +| ---------------------------------------------------------- | +| Collation-supported [comparison operations][collate-funcs] | +| [Join operations][join-types] | +| [`ORDER BY`][order-by-clause] | +| [`GROUP BY`][group-by-clause] | +| [`WINDOW`][window-clause] for window functions | +| Collation-supported [scalar functions][collate-funcs] | +| Collation-supported [aggregate functions][collate-funcs] | +| [Set operations][set-operators] | +| [`NULLIF` conditional expression][nullif] | + +## Operations that propagate collation + + +Collation can pass through some query operations to other parts +of a query. When collation passes through an operation in a +query, this is known as _propagation_. During propagation: + ++ If an input contains no collation specification or an empty + collation specification and another input contains an explicitly defined + collation, the explicitly defined collation is used for all of the inputs. ++ All inputs with a non-empty explicitly defined collation specification must + have the same type of collation specification, otherwise an error is thrown. + +ZetaSQL has several [functions][functions-propagation], +[operators][operators-propagation], and [expressions][expressions-propagation] +that can propagate collation. + +In the following example, the `'und:ci'` collation specification is propagated +from the `character` column to the `ORDER BY` operation. + +```sql +-- With collation +SELECT * +FROM UNNEST([ + COLLATE('B', 'und:ci'), + 'b', + 'a' +]) AS character +ORDER BY character + +/*-----------* + | character | + +-----------+ + | a | + | B | + | b | + *-----------*/ +``` + +```sql +-- Without collation +SELECT * +FROM UNNEST([ + 'B', + 'b', + 'a' +]) AS character +ORDER BY character + +/*-----------* + | character | + +-----------+ + | B | + | a | + | b | + *-----------*/ +``` + +### Functions + + +These functions let collation propagate through them: + +| Function | Notes +| --------- | -------- +| [`AEAD.DECRYPT_STRING`][aead_decrypt_string] | +| [`ANY_VALUE`][any-value] | +| [`ARRAY_AGG`][array-agg] | Collation on input arguments are propagated as collation on the array element. +| [`ARRAY_FIRST`][array-first] | +| [`ARRAY_LAST`][array-last] | +| [`ARRAY_SLICE`][array-slice] | +| [`ARRAY_TO_STRING`][array-to-string] | Collation on array elements are propagated to output. +| [`COLLATE`][collate] | +| [`CONCAT`][concat] | +| [`FORMAT`][format-func] | Collation from `format_string` to the returned string is propagated. +| [`FORMAT_DATE`][format-date] | Collation from `format_string` to the returned string is propagated. +| [`FORMAT_DATETIME`][format-datetime] | Collation from `format_string` to the returned string is propagated. +| [`FORMAT_TIME`][format-time] | Collation from `format_string` to the returned string is propagated. +| [`FORMAT_TIMESTAMP`][format-timestamp] | Collation from `format_string` to the returned string is propagated. +| [`FROM_PROTO`][from-proto] | +| [`GREATEST`][greatest] | +| [`LAG`][lag] | +| [`LEAD`][lead] | +| [`LEAST`][least]| +| [`LEFT`][left] | +| [`LOWER`][lower] | +| [`LPAD`][lpad] | +| [`MAX`][max] | +| [`MIN`][min] | +| [`NET.HOST`][nethost] | +| [`NET.MAKE_NET`][netmake-net] | +| [`NET.PUBLIC_SUFFIX`][netpublic-suffix] | +| [`NET.REG_DOMAIN`][netreg-domain] | +| [`NTH_VALUE`][nth-value] | +| [`NORMALIZE`][normalize] | +| [`NORMALIZE_AND_CASEFOLD`][normalize-and-casefold] | +| [`NULLIFERROR`][nulliferror] | +| [`PROTO_DEFAULT_IF_NULL`][proto-default-if-null] | +| [`REPEAT`][repeat] | +| [`REPLACE`][replace] | +| [`REVERSE`][reverse] | +| [`RIGHT`][right] | +| [`RPAD`][rpad] | +| [`SOUNDEX`][soundex] | +| [`SPLIT`][split] | Collation on input arguments are propagated as collation on the array element. +| [`STRING_AGG`][string-agg] | +| [`SUBSTR`][substr] | +| [`UPPER`][upper] | + +### Operators + + +These operators let collation propagate through them: + +| Operator | Notes +| --------- | -------- +| [`\|\|` concatenation operator][concat-op] | +| [Array subscript operator][array-subscript-operator] | Propagated to output. +| [Set operators][set-operators] | Collation of an output column is decided by the collations of input columns at the same position. +| [`STRUCT` field access operator][field-access-operator] | When getting a `STRUCT`, collation on the `STRUCT` field is propagated as the output collation. +| [`UNNEST`][unnest-operator] | Collation on the input array element is propagated to output. + +### Expressions + + +These expressions let collation propagate through them: + +| Expression | Notes +| --------- | -------- +| [`ARRAY`][array-dt] | When you construct an `ARRAY`, collation on input arguments is propagated on the elements in the `ARRAY`. +| [`CASE`][case] | +| [`CASE` expr][case-expr] | +| [`COALESCE`][coalesce] | +| [`IF`][if] | +| [`IFNULL`][ifnull] | +| [`NULLIF`][nullif] | +| [`STRUCT`][struct-dt] | When you construct a `STRUCT`, collation on input arguments is propagated on the fields in the `STRUCT`. + +## Where you can assign a collation specification + + +A [collation specification][collate-spec-details] can be assigned to these +collation-supported types: + ++ A `STRING` ++ A `STRING` field in a `STRUCT` ++ A `STRING` element in an `ARRAY` + +In addition: + ++ You can assign a default collation specification to a schema when you + create or alter it. This assigns a default collation specification to all + future tables that are added to the schema if the tables don't have their + own default collation specifications. ++ You can assign a default collation specification to a table when you create + or alter it. This assigns a collation specification to all future + collation-supported columns that are added to the table if the columns don't + have collation specifications. This overrides a + default collation specification on a schema. ++ You can assign a collation specification to a collation-supported type + in a column. A column that contains a collation-supported type in its + column schema is a collation-supported column. This overrides a + default collation specification on a table. ++ You can assign a collation specification to a collation-supported + query operation. ++ You can assign a collation specification to a collation-supported expression + with the `COLLATE` function. This overrides any collation specifications set + previously. + +In summary: + +You can define a default collation specification for a schema. For example: + +```sql +CREATE SCHEMA (...) +DEFAULT COLLATE 'und:ci' +``` + +You can define a default collation specification for a table. For example: + +```sql +CREATE TABLE (...) +DEFAULT COLLATE 'und:ci' +``` + +You can define a collation specification for a collation-supported column. +For example: + +```sql +CREATE TABLE ( + case_insensitive_column STRING COLLATE 'und:ci' +) +``` + +You can specify a collation specification for a collation-supported expression +with the `COLLATE` function. For example: + +```sql +SELECT COLLATE('a', 'und:ci') AS character +``` + +In the `ORDER BY` clause, you can specify a collation specification for a +collation-supported column. This overrides any +collation specifications set previously. + +For example: + +```sql +SELECT Place +FROM Locations +ORDER BY Place COLLATE "und:ci" +``` + +### DDL statements + + + + +| Location | Support | Notes | +| ----------| ------------------------------- | ---------------------------------------------- | +| Schema | [`CREATE SCHEMA`][create-schema] | Create a schema and optionally add a default | +: : : collation specification to the schema. : +| Schema | [`ALTER SCHEMA`][alter-schema] | Updates the default collation specification | +: : : for a schema. : +| Table | [`CREATE TABLE`][create-table] | Create a table and optionally add a default | +: : : collation specification to a table : +: : : or a collation specification to a : +: : : collation-supported type in a column. : +: : :

: +: : : You can't have collation on a column used : +: : : with `CLUSTERING`. : +| Table | [`ALTER TABLE`][alter-table] | Update the default collation specification | +: : : for collation-supported type in a table. : +| Column | [`ADD COLUMN`][add-column] | Add a collation specification to a | +: : : collation-supported type in a new column : +: : : in an existing table. : + + + +### Data types + + + + +| Type | Notes | +| --------------------- | --------------------------------------------------- | +| [`STRING`][string-dt] | You can apply a collation specification directly to | +: : this data type. : +| [`STRUCT`][struct-dt] | You can apply a collation specification to a | +: : `STRING` field in a `STRUCT`. A `STRUCT` can : +: : have `STRING` fields with different : +: : collation specifications. : +: : A `STRUCT` can only be used in comparisons with the : +: : following operators and conditional expressions:: +: : `=`, `!=`, `IN`, `NULLIF`, and `CASE`. : +| [`ARRAY`][array-dt] | You can apply a collation specification to a | +: : `STRING` element in an `ARRAY`. An `ARRAY` can : +: : have `STRING` elements with different : +: : collation specifications. : + + + +Note: Use the [`COLLATE`][collate] function to apply a collation specification +to collation-supported expressions. + +### Query statements + + +| Type | Support | +| ---------------- | ------------------------------------ | +| Sorting | [`ORDER BY` clause][order-by-clause] | + +### Functions, operators, and conditional expressions + + + + +#### Functions + +| Type | Support | Notes | +| ----------| ---------------------------- | ------------------------------------------- | +| Scalar | [`COLLATE`][collate] | | +| Scalar | [`ENDS_WITH`][ends-with] | | +| Scalar | [`GREATEST`][greatest] | | +: : : : +| Scalar | [`INSTR`][instr] | | +| Scalar | [`LEAST`][least] | | +| Scalar | [`REPLACE`][replace] | | +: : : : +| Scalar | [`SPLIT`][split] | | +| Scalar | [`STARTS_WITH`][starts-with] | | +| Scalar | [`STRPOS`][strpos] | | +| Aggregate | [`COUNT`][count] | This operator is only affected by | +: : : collation when the input includes : +: : : the `DISTINCT` argument. : +| Aggregate | [`MAX`][max] | | +| Aggregate | [`MIN`][min] | | +: : : : + +#### Operators + +| Support | Notes | +| ------------------------------------ | ------------------------------------- | +| [`<`][comparison-op] | | +| [`<=`][comparison-op] | | +| [`>`][comparison-op] | | +| [`>=`][comparison-op] | | +| [`=`][comparison-op] | | +| [`!=`][comparison-op] | | +| [`[NOT] BETWEEN`][comparison-op] | | +| [`[NOT] IN`][in-op] | [Limitations apply][in-op]. | +| [`[NOT] LIKE`][like-op] | [Limitations apply][like-op]. | +| [Quantified `[NOT] LIKE`][q-like-op] | [Limitations apply][q-like-op]. | + +#### Conditional expressions + +| Support | | +| ------------------------ | ------------------------------------------- | +| [`CASE`][case] | | +| [`CASE` expr][case-expr] | | +| [`NULLIF`][nullif] | | + + + +The preceding collation-supported operations +(functions, operators, and conditional expressions) +can include input with explicitly defined collation specifications for +collation-supported types. In a collation-supported operation: + ++ All inputs with a non-empty, explicitly defined collation specification must + be the same, otherwise an error is thrown. ++ If an input doesn't contain an explicitly defined collation + and another input contains an explicitly defined collation, the + explicitly defined collation is used for both. + +For example: + +```sql +-- Assume there's a table with this column declaration: +CREATE TABLE table_a +( + col_a STRING COLLATE 'und:ci', + col_b STRING COLLATE '', + col_c STRING, + col_d STRING COLLATE 'und:ci' +); + +-- This runs. Column 'b' has a collation specification and the +-- column 'c' doesn't. +SELECT STARTS_WITH(col_b_expression, col_c_expression) +FROM table_a; + +-- This runs. Column 'a' and 'd' have the same collation specification. +SELECT STARTS_WITH(col_a_expression, col_d_expression) +FROM table_a; + +-- This runs. Even though column 'a' and 'b' have different +-- collation specifications, column 'b' is considered the default collation +-- because it's assigned to an empty collation specification. +SELECT STARTS_WITH(col_a_expression, col_b_expression) +FROM table_a; + +-- This works. Even though column 'a' and 'b' have different +-- collation specifications, column 'b' is updated to use the same +-- collation specification as column 'a'. +SELECT STARTS_WITH(col_a_expression, COLLATE(col_b_expression, 'und:ci')) +FROM table_a; + +-- This runs. Column 'c' doesn't have a collation specification, so it uses the +-- collation specification of column 'd'. +SELECT STARTS_WITH(col_c_expression, col_d_expression) +FROM table_a; +``` + +## Collation specification details + + +A collation specification determines how strings are sorted and compared in +[collation-supported operations][collate-operations]. You can define a +collation specification for [collation-supported types][collate-define]. +These types of collation specifications are available: + ++ [Binary collation specification][binary-collation] ++ [Unicode collation specification][unicode-collation] + +If a collation specification isn't defined, the default collation specification +is used. To learn more, see the next section. + +### Default collation specification + + +When a collation specification isn't assigned or is empty, +`'binary'` collation is used. Binary collation indicates that the +operation should return data in [Unicode code point order][unicode-code-point]. +You can't set binary collation explicitly. + +In general, the following behavior occurs when an empty string is included in +collation: + ++ If a string has `und:ci` collation, the string comparison is + case-insensitive. ++ If a string has empty collation, the string comparison is case-sensitive. ++ If string not assigned collation, the string comparison is case-sensitive. ++ A column with unassigned collation inherit the table's default + collation. ++ A column with empty collation doesn't inherit the table's default collation. + +### Binary collation specification + + +```sql +collation_specification: + 'language_tag' +``` + +A binary collation specification indicates that the operation should +return data in [Unicode code point order][unicode-code-point]. The +collation specification can be a `STRING` literal or a query parameter. + +The language tag determines how strings are generally sorted and compared. +The allowed value for the `language_tag` is `binary`. + +This is what the `binary` language tag looks like when used with the `ORDER BY` +clause: + +```sql +SELECT Place +FROM Locations +ORDER BY Place COLLATE 'binary' +``` + +### Unicode collation specification + + +```sql +collation_specification: + 'language_tag[:collation_attribute]' +``` + +A unicode collation specification indicates that the operation should use the +[Unicode Collation Algorithm][tr10-collation-algorithm] to sort and compare +strings. The collation specification can be a `STRING` literal or a +query parameter. + +#### The language tag + +The language tag determines how strings are generally sorted and compared. +Allowed values for `language_tag` are: + ++ A standard locale string: This name is usually two or three letters + that represent the language, optionally followed by an underscore or dash and + two letters that represent the region — for example, `en_US`. These + names are defined by the + [Common Locale Data Repository (CLDR)][unicode-locale-identifier]. ++ `und`: A locale string representing the _undetermined_ locale. `und` is a + special language tag defined in the + [IANA language subtag registry][iana-language-subtag-registry] and used to + indicate an undetermined locale. This is also known as the _root_ locale and + can be considered the _default_ Unicode collation. It defines a reasonable, + locale agnostic collation. It differs significantly from + `binary`. ++ `unicode`: Identical to `binary`. It's recommended to migrate `unicode` + to `binary`. + +Additionally, you can append a language tag with an extension. To learn more, +see [extensions][collate-extensions] for the language tag. + +#### The collation attribute + +In addition to the language tag, the unicode collation specification can have +an optional `collation_attribute`, which enables additional rules for sorting +and comparing strings. Allowed values are: + ++ `ci`: Collation is case-insensitive. ++ `cs`: Collation is case-sensitive. By default, `collation_attribute` is + implicitly `cs`. + +If you're using the `unicode` language tag with a collation attribute, these +caveats apply: + ++ `unicode:cs` is identical to `unicode`. ++ `unicode:ci` is identical to `und:ci`. It's recommended to migrate + `unicode:ci` to `binary`. + +#### Collation specification example + +This is what the `ci` collation attribute looks like when used with the +`und` language tag in the `COLLATE` function: + +```sql +COLLATE('orange1', 'und:ci') +``` + +This is what the `ci` collation attribute looks like when used with the +`und` language tag in the `ORDER BY` clause: + +```sql +SELECT Place +FROM Locations +ORDER BY Place COLLATE 'und:ci' +``` + +#### Extensions + + +The [Unicode Collation Algorithm][tr10-collation-algorithm] standard +includes some useful locale extensions. In ZetaSQL, a `language_tag` +may be extended by appending `-u-[extension]` to it and replacing `[extension]` +with your desired [Unicode local extension][tr35-collation-settings]. + +This is what the `kn-true` extension looks like when used with the +`en-us` language tag in the `ORDER BY` clause: + +For example: + +```sql +SELECT * +FROM UNNEST([ + 'a12b', + 'a1b' +]) AS ids +ORDER BY ids COLLATE 'en-us-u-kn-true' + +/*-------* + | ids | + +-------+ + | a1b | + | a12b | + *-------*/ +``` + +```sql +SELECT * +FROM UNNEST([ + 'a12b', + 'a1b' +]) AS ids +ORDER BY ids COLLATE 'en-us-u-kn-false' + +/*-------* + | ids | + +-------+ + | a12b | + | a1b | + *-------*/ +``` + +Here are some commonly used extensions: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExtensionNameExample
ks-level2Case-Insensitive"a1" < "A2"
ks-level1Accent and Case-Insensitive"ä1" < "a2" < "A3"
ks-level1-kc-trueAccent Insensitive"ä1" < "a2"
kn-trueNumeric Ordering"a1b" < "a12b"
+ +For a complete list and in depth technical details, consult +[Unicode Locale Data Markup Language Part 5: Collation] +[tr35-collation-settings]. + +#### Caveats + ++ Differing strings can be considered equal. + For instance, `ẞ` (LATIN CAPITAL LETTER SHARP S) is considered equal to `'SS'` + on the primary level, thus `'ẞ1' < 'SS2'` are equal. This is similar to how + case insensitivity works. ++ In search operations, strings with different lengths could be considered + equal. To ensure consistency, collation should be used without + search tailoring. ++ There are a wide range of unicode code points (punctuation, symbols, etc), + that are treated as if they aren't there. So strings with + and without them are sorted identically. For example, the format control + code point `U+2060` is ignored when the following strings are sorted: + + ```sql + SELECT * + FROM UNNEST([ + COLLATE('oran\u2060ge1', 'und:ci'), + COLLATE('\u2060orange2', 'und:ci'), + COLLATE('orange3', 'und:ci') + ]) AS fruit + ORDER BY fruit + + /*---------* + | fruit | + +---------+ + | orange1 | + | orange2 | + | orange3 | + *---------*/ + ``` ++ Ordering _may_ change. The Unicode specification of the `und` collation can + change occasionally, which can affect sorting + order. If you need a stable sort order that's + guaranteed to never change, use `unicode` collation. + +## Limitations + +Limitations for supported features are captured in the previous +sections, but here are a few general limitations to keep in mind: + ++ Table functions can't take table arguments with collated columns. + + ```sql + CREATE TABLE FUNCTION my_dataset.my_tvf(x TABLE) AS ( + SELECT col_str FROM x + ); + + SELECT * FROM my_dataset.my_tvf( + (SELECT COLLATE('abc', 'und:ci') AS col_str) + ); + + -- User error: + -- "Collation 'und:ci' on column col_str of argument of TVF call is not allowed" + ``` + + + +[unicode-code-point]: https://en.wikipedia.org/wiki/List_of_Unicode_characters + +[iana-language-subtag-registry]: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry + +[unicode-locale-identifier]: https://www.unicode.org/reports/tr35/#Unicode_locale_identifier + +[tr35-collation-settings]: http://www.unicode.org/reports/tr35/tr35-collation.html#Setting_Options + +[tr10-collation-algorithm]: http://www.unicode.org/reports/tr10/ + +[collate-operations]: #collate_operations + +[collate-define]: #collate_define + +[collate-propagate]: #collate_propagate + +[collate-spec-details]: #collate_spec_details + +[collate-funcs]: #collate_funcs + +[collate-query]: #collate_query + +[collate-dts]: #collate_data_types + +[collate-ddl]: #collate_ddl + +[unicode-collation]: #unicode_collation + +[binary-collation]: #binary_collation + +[functions-propagation]: #functions_propagation + +[operators-propagation]: #operators_propagation + +[expressions-propagation]: #expressions_propagation + +[collate-extensions]: #collation_extensions + +[limitations]: #limitations + +[order-by-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#order_by_clause + +[collate-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#collate_clause + +[create-schema]: #create_schema_statement +[create-table]: #create_table_statement +[alter-schema]: #alter_schema_collate_statement +[alter-table]: #alter_table_collate_statement +[alter-column]: #alter_column_set_data_type_statement +[add-column]: #alter_table_add_column_statement + +[string-dt]: https://github.com/google/zetasql/blob/master/docs/data-types.md#string_type + +[struct-dt]: https://github.com/google/zetasql/blob/master/docs/data-types.md#struct_type + +[array-dt]: https://github.com/google/zetasql/blob/master/docs/data-types.md#array_type + +[join-types]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#join_types + +[group-by-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#group_by_clause + +[window-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#window_clause + +[set-operators]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#set_operators + +[unnest-operator]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#unnest_operator + +[aead_decrypt_string]: https://github.com/google/zetasql/blob/master/docs/aead_encryption_functions.md.md#aeaddecrypt-string + +[any-value]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#any_value + +[approx-top-count]: https://github.com/google/zetasql/blob/master/docs/approximate_aggregate_functions.md#approx_top_count + +[array-agg]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#array_agg + +[array-to-string]: https://github.com/google/zetasql/blob/master/docs/array_functions.md#array_to_string + +[array-slice]: https://github.com/google/zetasql/blob/master/docs/array_functions.md#array_slice + +[array-first]: https://github.com/google/zetasql/blob/master/docs/array_functions.md#array_first + +[array-last]: https://github.com/google/zetasql/blob/master/docs/array_functions.md#array_last + +[cast]: https://github.com/google/zetasql/blob/master/docs/conversion_functions.md + +[collate]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#collate + +[concat]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#concat + +[count]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#count + +[ends-with]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#ends_with + +[format-func]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#format_string + +[format-date]: https://github.com/google/zetasql/blob/master/docs/date_functions.md#format_date + +[format-datetime]: https://github.com/google/zetasql/blob/master/docs/datetime_functions.md#format_datetime + +[format-time]: https://github.com/google/zetasql/blob/master/docs/time_functions.md#format_time + +[format-timestamp]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#format_timestamp + +[from-proto]: https://github.com/google/zetasql/blob/master/docs/protocol_buffer_functions.md#from_proto + +[greatest]: https://github.com/google/zetasql/blob/master/docs/mathematical_functions.md#greatest + +[initcap]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#initcap + +[instr]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#instr + +[json-extract]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_extract + +[json-extract-array]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_extract_array + +[json-extract-scalar]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_extract_scalar + +[json-extract-string-array]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_extract_string_array + +[json-query]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_query + +[json-query-array]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_query_array + +[json-value]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_value + +[json-value-array]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#json_value_array + +[lag]: https://github.com/google/zetasql/blob/master/docs/navigation_functions.md#lag + +[lead]: https://github.com/google/zetasql/blob/master/docs/navigation_functions.md#lead + +[least]: https://github.com/google/zetasql/blob/master/docs/mathematical_functions.md#least + +[left]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#left + +[lower]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#lower + +[lpad]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#lpad + +[max]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#max + +[min]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#min + +[nethost]: https://github.com/google/zetasql/blob/master/docs/net_functions.md#nethost + +[netmake-net]: https://github.com/google/zetasql/blob/master/docs/net_functions.md#netmake_net + +[netpublic-suffix]: https://github.com/google/zetasql/blob/master/docs/net_functions.md#netpublic_suffix + +[netreg-domain]: https://github.com/google/zetasql/blob/master/docs/net_functions.md#netreg_domain + +[nth-value]: https://github.com/google/zetasql/blob/master/docs/navigation_functions.md#nth_value + +[normalize]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#normalize + +[normalize-and-casefold]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#normalize_and_casefold + +[nulliferror]: https://github.com/google/zetasql/blob/master/docs/debugging_functions.md#nulliferror + +[proto-default-if-null]: https://github.com/google/zetasql/blob/master/docs/protocol_buffer_functions.md#proto_default_if_null + +[repeat]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#repeat + +[replace]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#replace + +[reverse]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#reverse + +[right]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#right + +[rpad]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#rpad + +[soundex]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#soundex + +[split]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#split + +[starts-with]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#starts_with + +[string-func]: https://github.com/google/zetasql/blob/master/docs/conversion_functions.md#other_conv_functions + +[string-agg]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#string_agg + +[strpos]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#strpos + +[substr]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#substr + +[upper]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#upper + +[comparison-op]: https://github.com/google/zetasql/blob/master/docs/operators.md#comparison_operators + +[in-op]: https://github.com/google/zetasql/blob/master/docs/operators.md#in_operators + +[like-op]: https://github.com/google/zetasql/blob/master/docs/operators.md#like_operator + +[q-like-op]: https://github.com/google/zetasql/blob/master/docs/operators.md#like_operator_quantified + +[concat-op]: https://github.com/google/zetasql/blob/master/docs/operators.md#concatenation_operator + +[field-access-operator]: https://github.com/google/zetasql/blob/master/docs/operators.md#field_access_operator + +[array-subscript-operator]: https://github.com/google/zetasql/blob/master/docs/operators.md#array_subscript_operator + +[case]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#case + +[case-expr]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#case_expr + +[coalesce]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#coalesce + +[if]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#if + +[ifnull]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#ifnull + +[nullif]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#nullif + + + diff --git a/docs/conditional_expressions.md b/docs/conditional_expressions.md index 2cc141ec7..c73cc27a3 100644 --- a/docs/conditional_expressions.md +++ b/docs/conditional_expressions.md @@ -313,26 +313,31 @@ must be coercible to a common [supertype][cond-exp-supertype]. [Supertype][cond-exp-supertype] of `true_result` and `else_result`. -**Example** +**Examples** ```sql -WITH Numbers AS ( - SELECT 10 as A, 20 as B UNION ALL - SELECT 50, 30 UNION ALL - SELECT 60, 60 -) SELECT - A, - B, - IF(A < B, 'true', 'false') AS result -FROM Numbers + 10 AS A, + 20 AS B, + IF(10 < 20, 'true', 'false') AS result /*------------------* | A | B | result | +------------------+ | 10 | 20 | true | - | 50 | 30 | false | - | 60 | 60 | false | + *------------------*/ +``` + +```sql +SELECT + 30 AS A, + 20 AS B, + IF(30 < 20, 'true', 'false') AS result + +/*------------------* + | A | B | result | + +------------------+ + | 30 | 20 | false | *------------------*/ ``` diff --git a/docs/conversion_functions.md b/docs/conversion_functions.md new file mode 100644 index 000000000..c483edd22 --- /dev/null +++ b/docs/conversion_functions.md @@ -0,0 +1,2036 @@ + + + + +# Conversion functions + +ZetaSQL supports conversion functions. These data type +conversions are explicit, but some conversions can happen implicitly. You can +learn more about implicit and explicit conversion [here][conversion-rules]. + +### Function list + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSummary
CAST + + + Convert the results of an expression to the given type. +
PARSE_BIGNUMERIC + + + Converts a STRING value to a BIGNUMERIC value. +
PARSE_NUMERIC + + + Converts a STRING value to a NUMERIC value. +
SAFE_CAST + + + Similar to the CAST function, but returns NULL + when a runtime error is produced. +
+ +### `CAST` + + +```sql +CAST(expression AS typename [format_clause]) +``` + +**Description** + +Cast syntax is used in a query to indicate that the result type of an +expression should be converted to some other type. + +When using `CAST`, a query can fail if ZetaSQL is unable to perform +the cast. If you want to protect your queries from these types of errors, you +can use [SAFE_CAST][con-func-safecast]. + +Casts between supported types that do not successfully map from the original +value to the target domain produce runtime errors. For example, casting +`BYTES` to `STRING` where the byte sequence is not valid UTF-8 results in a +runtime error. + +Other examples include: + ++ Casting `INT64` to `INT32` where the value overflows `INT32`. ++ Casting `STRING` to `INT32` where the `STRING` contains non-digit characters. + +Some casts can include a [format clause][formatting-syntax], which provides +instructions for how to conduct the +cast. For example, you could +instruct a cast to convert a sequence of bytes to a BASE64-encoded string +instead of a UTF-8-encoded string. + +The structure of the format clause is unique to each type of cast and more +information is available in the section for that cast. + +**Examples** + +The following query results in `"true"` if `x` is `1`, `"false"` for any other +non-`NULL` value, and `NULL` if `x` is `NULL`. + +```sql +CAST(x=1 AS STRING) +``` + +### CAST AS ARRAY + +```sql +CAST(expression AS ARRAY) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `ARRAY`. The +`expression` parameter can represent an expression for these data types: + ++ `ARRAY` + +**Conversion rules** + + + + + + + + + + + + +
FromToRule(s) when casting x
ARRAYARRAY + + The element types of the input + array must be castable to the + element types of the target array. + For example, casting from type + ARRAY<INT64> to + ARRAY<DOUBLE> or + ARRAY<STRING> is valid; + casting from type ARRAY<INT64> + to ARRAY<BYTES> is not valid. + +
+ +### CAST AS BIGNUMERIC + + +```sql +CAST(expression AS BIGNUMERIC) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `BIGNUMERIC`. The +`expression` parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `FLOAT` ++ `DOUBLE` ++ `NUMERIC` ++ `BIGNUMERIC` ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
Floating PointBIGNUMERIC + The floating point number will round + half away from zero. + + Casting a NaN, +inf or + -inf will return an error. Casting a value outside the range + of BIGNUMERIC returns an overflow error. +
STRINGBIGNUMERIC + The numeric literal contained in the string must not exceed + the maximum precision or range of the + BIGNUMERIC type, or an error will occur. If the number of + digits after the decimal point exceeds 38, then the resulting + BIGNUMERIC value will round + half away from zero + + to have 38 digits after the decimal point. +
+ +### CAST AS BOOL + +```sql +CAST(expression AS BOOL) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `BOOL`. The +`expression` parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `BOOL` ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
IntegerBOOL + Returns FALSE if x is 0, + TRUE otherwise. +
STRINGBOOL + Returns TRUE if x is "true" and + FALSE if x is "false"
+ All other values of x are invalid and throw an error instead + of casting to a boolean.
+ A string is case-insensitive when converting + to a boolean. +
+ +### CAST AS BYTES + +```sql +CAST(expression AS BYTES [format_clause]) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `BYTES`. The +`expression` parameter can represent an expression for these data types: + ++ `BYTES` ++ `STRING` ++ `PROTO` + +**Format clause** + +When an expression of one type is cast to another type, you can use the +[format clause][formatting-syntax] to provide instructions for how to conduct +the cast. You can use the format clause in this section if `expression` is a +`STRING`. + ++ [Format string as bytes][format-string-as-bytes] + +**Conversion rules** + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGBYTES + Strings are cast to bytes using UTF-8 encoding. For example, + the string "©", when cast to + bytes, would become a 2-byte sequence with the + hex values C2 and A9. +
PROTOBYTES + Returns the proto2 wire format bytes + of x. +
+ +### CAST AS DATE + +```sql +CAST(expression AS DATE [format_clause]) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `DATE`. The `expression` +parameter can represent an expression for these data types: + ++ `STRING` ++ `DATETIME` ++ `TIMESTAMP` + +**Format clause** + +When an expression of one type is cast to another type, you can use the +[format clause][formatting-syntax] to provide instructions for how to conduct +the cast. You can use the format clause in this section if `expression` is a +`STRING`. + ++ [Format string as date and time][format-string-as-date-time] + +**Conversion rules** + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGDATE + When casting from string to date, the string must conform to + the supported date literal format, and is independent of time zone. If the + string expression is invalid or represents a date that is outside of the + supported min/max range, then an error is produced. +
TIMESTAMPDATE + Casting from a timestamp to date effectively truncates the timestamp as + of the default time zone. +
+ +### CAST AS DATETIME + +```sql +CAST(expression AS DATETIME [format_clause]) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `DATETIME`. The +`expression` parameter can represent an expression for these data types: + ++ `STRING` ++ `DATETIME` ++ `TIMESTAMP` + +**Format clause** + +When an expression of one type is cast to another type, you can use the +[format clause][formatting-syntax] to provide instructions for how to conduct +the cast. You can use the format clause in this section if `expression` is a +`STRING`. + ++ [Format string as date and time][format-string-as-date-time] + +**Conversion rules** + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGDATETIME + When casting from string to datetime, the string must conform to the + supported datetime literal format, and is independent of time zone. If + the string expression is invalid or represents a datetime that is outside + of the supported min/max range, then an error is produced. +
TIMESTAMPDATETIME + Casting from a timestamp to datetime effectively truncates the timestamp + as of the default time zone. +
+ +### CAST AS ENUM + +```sql +CAST(expression AS ENUM) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `ENUM`. The `expression` +parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `STRING` ++ `ENUM` + +**Conversion rules** + + + + + + + + + + + + +
FromToRule(s) when casting x
ENUMENUMMust have the same enum name.
+ +### CAST AS Floating Point + + +```sql +CAST(expression AS DOUBLE) +``` + +```sql +CAST(expression AS FLOAT) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to floating point types. +The `expression` parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `FLOAT` ++ `DOUBLE` ++ `NUMERIC` ++ `BIGNUMERIC` ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
IntegerFloating Point + Returns a close but potentially not exact floating point value. +
NUMERICFloating Point + NUMERIC will convert to the closest floating point number + with a possible loss of precision. +
BIGNUMERICFloating Point + BIGNUMERIC will convert to the closest floating point number + with a possible loss of precision. +
STRINGFloating Point + Returns x as a floating point value, interpreting it as + having the same form as a valid floating point literal. + Also supports casts from "[+,-]inf" to + [,-]Infinity, + "[+,-]infinity" to [,-]Infinity, and + "[+,-]nan" to NaN. + Conversions are case-insensitive. +
+ +### CAST AS Integer + + +```sql +CAST(expression AS INT32) +``` + +```sql +CAST(expression AS UINT32) +``` + +```sql +CAST(expression AS INT64) +``` + +```sql +CAST(expression AS UINT64) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to integer types. +The `expression` parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `FLOAT` ++ `DOUBLE` ++ `NUMERIC` ++ `BIGNUMERIC` ++ `ENUM` ++ `BOOL` ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
+ Floating Point + + Integer + + Returns the closest integer value.
+ Halfway cases such as 1.5 or -0.5 round away from zero. +
BOOLInteger + Returns 1 if x is TRUE, + 0 otherwise. +
STRINGInteger + A hex string can be cast to an integer. For example, + 0x123 to 291 or -0x123 to + -291. +
+ +**Examples** + +If you are working with hex strings (`0x123`), you can cast those strings as +integers: + +```sql +SELECT '0x123' as hex_value, CAST('0x123' as INT64) as hex_to_int; + +/*-----------+------------* + | hex_value | hex_to_int | + +-----------+------------+ + | 0x123 | 291 | + *-----------+------------*/ +``` + +```sql +SELECT '-0x123' as hex_value, CAST('-0x123' as INT64) as hex_to_int; + +/*-----------+------------* + | hex_value | hex_to_int | + +-----------+------------+ + | -0x123 | -291 | + *-----------+------------*/ +``` + +### CAST AS INTERVAL + +```sql +CAST(expression AS INTERVAL) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `INTERVAL`. The +`expression` parameter can represent an expression for these data types: + ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGINTERVAL + When casting from string to interval, the string must conform to either + ISO 8601 Duration + + standard or to interval literal + format 'Y-M D H:M:S.F'. Partial interval literal formats are also accepted + when they are not ambiguous, for example 'H:M:S'. + If the string expression is invalid or represents an interval that is + outside of the supported min/max range, then an error is produced. +
+ +**Examples** + +```sql +SELECT input, CAST(input AS INTERVAL) AS output +FROM UNNEST([ + '1-2 3 10:20:30.456', + '1-2', + '10:20:30', + 'P1Y2M3D', + 'PT10H20M30,456S' +]) input + +/*--------------------+--------------------* + | input | output | + +--------------------+--------------------+ + | 1-2 3 10:20:30.456 | 1-2 3 10:20:30.456 | + | 1-2 | 1-2 0 0:0:0 | + | 10:20:30 | 0-0 0 10:20:30 | + | P1Y2M3D | 1-2 3 0:0:0 | + | PT10H20M30,456S | 0-0 0 10:20:30.456 | + *--------------------+--------------------*/ +``` + +### CAST AS NUMERIC + + +```sql +CAST(expression AS NUMERIC) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `NUMERIC`. The +`expression` parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `FLOAT` ++ `DOUBLE` ++ `NUMERIC` ++ `BIGNUMERIC` ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
Floating PointNUMERIC + The floating point number will round + half away from zero. + + Casting a NaN, +inf or + -inf will return an error. Casting a value outside the range + of NUMERIC returns an overflow error. +
STRINGNUMERIC + The numeric literal contained in the string must not exceed + the maximum precision or range of the NUMERIC + type, or an error will occur. If the number of digits + after the decimal point exceeds nine, then the resulting + NUMERIC value will round + half away from zero. + + to have nine digits after the decimal point. +
+ +### CAST AS PROTO + +```sql +CAST(expression AS PROTO) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `PROTO`. The +`expression` parameter can represent an expression for these data types: + ++ `STRING` ++ `BYTES` ++ `PROTO` + +**Conversion rules** + + + + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGPROTO + Returns the protocol buffer that results from parsing + from proto2 text format.
+ Throws an error if parsing fails, e.g., if not all required fields are + set. +
BYTESPROTO + Returns the protocol buffer that results from parsing + x from the proto2 wire format.
+ Throws an error if parsing fails, e.g., if not all required fields are + set. +
PROTOPROTOMust have the same protocol buffer name.
+ +**Example** + +This example references a protocol buffer called `Award`. + +```proto +message Award { + required int32 year = 1; + optional int32 month = 2; + repeated Type type = 3; + + message Type { + optional string award_name = 1; + optional string category = 2; + } +} +``` + +```sql +SELECT + CAST( + ''' + year: 2001 + month: 9 + type { award_name: 'Best Artist' category: 'Artist' } + type { award_name: 'Best Album' category: 'Album' } + ''' + AS zetasql.examples.music.Award) + AS award_col + +/*---------------------------------------------------------* + | award_col | + +---------------------------------------------------------+ + | { | + | year: 2001 | + | month: 9 | + | type { award_name: "Best Artist" category: "Artist" } | + | type { award_name: "Best Album" category: "Album" } | + | } | + *---------------------------------------------------------*/ +``` + +### CAST AS RANGE + +```sql +CAST(expression AS RANGE) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `RANGE`. The +`expression` parameter can represent an expression for these data types: + ++ `STRING` + +**Conversion rules** + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGRANGE + When casting from string to range, the string must conform to the + supported range literal format. If the string expression is invalid or + represents a range that is outside of the supported subtype min/max range, + then an error is produced. +
+ +**Examples** + +```sql +SELECT CAST( + '[2020-01-01, 2020-01-02)' + AS RANGE) AS string_to_range + +/*----------------------------------------* + | string_to_range | + +----------------------------------------+ + | [DATE '2020-01-01', DATE '2020-01-02') | + *----------------------------------------*/ +``` + +```sql +SELECT CAST( + '[2014-09-27 12:30:00.45, 2016-10-17 11:15:00.33)' + AS RANGE) AS string_to_range + +/*------------------------------------------------------------------------* + | string_to_range | + +------------------------------------------------------------------------+ + | [DATETIME '2014-09-27 12:30:00.45', DATETIME '2016-10-17 11:15:00.33') | + *------------------------------------------------------------------------*/ +``` + +```sql +SELECT CAST( + '[2014-09-27 12:30:00+08, 2016-10-17 11:15:00+08)' + AS RANGE) AS string_to_range + +-- Results depend upon where this query was executed. +/*--------------------------------------------------------------------------* + | string_to_range | + +--------------------------------------------------------------------------+ + | [TIMESTAMP '2014-09-27 12:30:00+08', TIMESTAMP '2016-10-17 11:15:00+08') | + *--------------------------------------------------------------------------*/ +``` + +```sql +SELECT CAST( + '[UNBOUNDED, 2020-01-02)' + AS RANGE) AS string_to_range + +/*--------------------------------* + | string_to_range | + +--------------------------------+ + | [UNBOUNDED, DATE '2020-01-02') | + *--------------------------------*/ +``` + +```sql +SELECT CAST( + '[2020-01-01, NULL)' + AS RANGE) AS string_to_range + +/*--------------------------------* + | string_to_range | + +--------------------------------+ + | [DATE '2020-01-01', UNBOUNDED) | + *--------------------------------*/ +``` + +### CAST AS STRING + + +```sql +CAST(expression AS STRING [format_clause [AT TIME ZONE timezone_expr]]) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `STRING`. The +`expression` parameter can represent an expression for these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `FLOAT` ++ `DOUBLE` ++ `NUMERIC` ++ `BIGNUMERIC` ++ `ENUM` ++ `BOOL` ++ `BYTES` ++ `PROTO` ++ `TIME` ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` ++ `RANGE` ++ `INTERVAL` ++ `STRING` + +**Format clause** + +When an expression of one type is cast to another type, you can use the +[format clause][formatting-syntax] to provide instructions for how to conduct +the cast. You can use the format clause in this section if `expression` is one +of these data types: + ++ `INT32` ++ `UINT32` ++ `INT64` ++ `UINT64` ++ `FLOAT` ++ `DOUBLE` ++ `NUMERIC` ++ `BIGNUMERIC` ++ `BYTES` ++ `TIME` ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +The format clause for `STRING` has an additional optional clause called +`AT TIME ZONE timezone_expr`, which you can use to specify a specific time zone +to use during formatting of a `TIMESTAMP`. If this optional clause is not +included when formatting a `TIMESTAMP`, your current time zone is used. + +For more information, see the following topics: + ++ [Format bytes as string][format-bytes-as-string] ++ [Format date and time as string][format-date-time-as-string] ++ [Format numeric type as string][format-numeric-type-as-string] + +**Conversion rules** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
Floating PointSTRINGReturns an approximate string representation. A returned + NaN or 0 will not be signed.
+
BOOLSTRING + Returns "true" if x is TRUE, + "false" otherwise.
BYTESSTRING + Returns x interpreted as a UTF-8 string.
+ For example, the bytes literal + b'\xc2\xa9', when cast to a string, + is interpreted as UTF-8 and becomes the unicode character "©".
+ An error occurs if x is not valid UTF-8.
ENUMSTRING + Returns the canonical enum value name of + x.
+ If an enum value has multiple names (aliases), + the canonical name/alias for that value is used.
PROTOSTRINGReturns the proto2 text format representation of x.
TIMESTRING + Casting from a time type to a string is independent of time zone and + is of the form HH:MM:SS. +
DATESTRING + Casting from a date type to a string is independent of time zone and is + of the form YYYY-MM-DD. +
DATETIMESTRING + Casting from a datetime type to a string is independent of time zone and + is of the form YYYY-MM-DD HH:MM:SS. +
TIMESTAMPSTRING + When casting from timestamp types to string, the timestamp is interpreted + using the default time zone, which is implementation defined. The number of + subsecond digits produced depends on the number of trailing zeroes in the + subsecond part: the CAST function will truncate zero, three, or six + digits. +
INTERVALSTRING + Casting from an interval to a string is of the form + Y-M D H:M:S. +
+ +**Examples** + +```sql +SELECT CAST(CURRENT_DATE() AS STRING) AS current_date + +/*---------------* + | current_date | + +---------------+ + | 2021-03-09 | + *---------------*/ +``` + +```sql +SELECT CAST(CURRENT_DATE() AS STRING FORMAT 'DAY') AS current_day + +/*-------------* + | current_day | + +-------------+ + | MONDAY | + *-------------*/ +``` + +```sql +SELECT CAST( + TIMESTAMP '2008-12-25 00:00:00+00:00' + AS STRING FORMAT 'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS date_time_to_string + +-- Results depend upon where this query was executed. +/*------------------------------* + | date_time_to_string | + +------------------------------+ + | 2008-12-24 16:00:00 -08:00 | + *------------------------------*/ +``` + +```sql +SELECT CAST( + TIMESTAMP '2008-12-25 00:00:00+00:00' + AS STRING FORMAT 'YYYY-MM-DD HH24:MI:SS TZH:TZM' + AT TIME ZONE 'Asia/Kolkata') AS date_time_to_string + +-- Because the time zone is specified, the result is always the same. +/*------------------------------* + | date_time_to_string | + +------------------------------+ + | 2008-12-25 05:30:00 +05:30 | + *------------------------------*/ +``` + +```sql +SELECT CAST(INTERVAL 3 DAY AS STRING) AS interval_to_string + +/*--------------------* + | interval_to_string | + +--------------------+ + | 0-0 3 0:0:0 | + *--------------------*/ +``` + +```sql +SELECT CAST( + INTERVAL "1-2 3 4:5:6.789" YEAR TO SECOND + AS STRING) AS interval_to_string + +/*--------------------* + | interval_to_string | + +--------------------+ + | 1-2 3 4:5:6.789 | + *--------------------*/ +``` + +### CAST AS STRUCT + +```sql +CAST(expression AS STRUCT) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `STRUCT`. The +`expression` parameter can represent an expression for these data types: + ++ `STRUCT` + +**Conversion rules** + + + + + + + + + + + + +
FromToRule(s) when casting x
STRUCTSTRUCT + Allowed if the following conditions are met:
+
    +
  1. + The two structs have the same number of + fields. +
  2. +
  3. + The original struct field types can be + explicitly cast to the corresponding target + struct field types (as defined by field + order, not field name). +
  4. +
+
+ +### CAST AS TIME + +```sql +CAST(expression AS TIME [format_clause]) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to TIME. The `expression` +parameter can represent an expression for these data types: + ++ `STRING` ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +**Format clause** + +When an expression of one type is cast to another type, you can use the +[format clause][formatting-syntax] to provide instructions for how to conduct +the cast. You can use the format clause in this section if `expression` is a +`STRING`. + ++ [Format string as date and time][format-string-as-date-time] + +**Conversion rules** + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGTIME + When casting from string to time, the string must conform to + the supported time literal format, and is independent of time zone. If the + string expression is invalid or represents a time that is outside of the + supported min/max range, then an error is produced. +
+ +### CAST AS TIMESTAMP + +```sql +CAST(expression AS TIMESTAMP [format_clause [AT TIME ZONE timezone_expr]]) +``` + +**Description** + +ZetaSQL supports [casting][con-func-cast] to `TIMESTAMP`. The +`expression` parameter can represent an expression for these data types: + ++ `STRING` ++ `DATETIME` ++ `TIMESTAMP` + +**Format clause** + +When an expression of one type is cast to another type, you can use the +[format clause][formatting-syntax] to provide instructions for how to conduct +the cast. You can use the format clause in this section if `expression` is a +`STRING`. + ++ [Format string as date and time][format-string-as-date-time] + +The format clause for `TIMESTAMP` has an additional optional clause called +`AT TIME ZONE timezone_expr`, which you can use to specify a specific time zone +to use during formatting. If this optional clause is not included, your +current time zone is used. + +**Conversion rules** + + + + + + + + + + + + + + + + + + + + + + + + + + +
FromToRule(s) when casting x
STRINGTIMESTAMP + When casting from string to a timestamp, string_expression + must conform to the supported timestamp literal formats, or else a runtime + error occurs. The string_expression may itself contain a + time zone. +

+ If there is a time zone in the string_expression, that + time zone is used for conversion, otherwise the default time zone, + which is implementation defined, is used. If the string has fewer than six digits, + then it is implicitly widened. +

+ An error is produced if the string_expression is invalid, + has more than six subsecond digits (i.e., precision greater than + microseconds), or represents a time outside of the supported timestamp + range. +
DATETIMESTAMP + Casting from a date to a timestamp interprets date_expression + as of midnight (start of the day) in the default time zone, + which is implementation defined. +
DATETIMETIMESTAMP + Casting from a datetime to a timestamp interprets + datetime_expression in the default time zone, + which is implementation defined. +

+ Most valid datetime values have exactly one corresponding timestamp + in each time zone. However, there are certain combinations of valid + datetime values and time zones that have zero or two corresponding + timestamp values. This happens in a time zone when clocks are set forward + or set back, such as for Daylight Savings Time. + When there are two valid timestamps, the earlier one is used. + When there is no valid timestamp, the length of the gap in time + (typically one hour) is added to the datetime. +
+ +**Examples** + +The following example casts a string-formatted timestamp as a timestamp: + +```sql +SELECT CAST("2020-06-02 17:00:53.110+00:00" AS TIMESTAMP) AS as_timestamp + +-- Results depend upon where this query was executed. +/*----------------------------* + | as_timestamp | + +----------------------------+ + | 2020-06-03 00:00:53.110+00 | + *----------------------------*/ +``` + +The following examples cast a string-formatted date and time as a timestamp. +These examples return the same output as the previous example. + +```sql +SELECT CAST('06/02/2020 17:00:53.110' AS TIMESTAMP FORMAT 'MM/DD/YYYY HH24:MI:SS.FF3' AT TIME ZONE 'UTC') AS as_timestamp +``` + +```sql +SELECT CAST('06/02/2020 17:00:53.110' AS TIMESTAMP FORMAT 'MM/DD/YYYY HH24:MI:SS.FF3' AT TIME ZONE '+00') AS as_timestamp +``` + +```sql +SELECT CAST('06/02/2020 17:00:53.110 +00' AS TIMESTAMP FORMAT 'MM/DD/YYYY HH24:MI:SS.FF3 TZH') AS as_timestamp +``` + +[formatting-syntax]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#formatting_syntax + +[format-string-as-bytes]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#format_string_as_bytes + +[format-bytes-as-string]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#format_bytes_as_string + +[format-date-time-as-string]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#format_date_time_as_string + +[format-string-as-date-time]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#format_string_as_datetime + +[format-numeric-type-as-string]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#format_numeric_type_as_string + +[con-func-cast]: #cast + +[con-func-safecast]: #safe_casting + +### `PARSE_BIGNUMERIC` + + +```sql +PARSE_BIGNUMERIC(string_expression) +``` + +**Description** + +Converts a `STRING` to a `BIGNUMERIC` value. + +The numeric literal contained in the string must not exceed the +[maximum precision or range][bignumeric-type] of the `BIGNUMERIC` type, or an +error occurs. If the number of digits after the decimal point exceeds 38, then +the resulting `BIGNUMERIC` value rounds +[half away from zero][half-from-zero-wikipedia] to have 38 digits after the +decimal point. + +```sql + +-- This example shows how a string with a decimal point is parsed. +SELECT PARSE_BIGNUMERIC("123.45") AS parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ + +-- This example shows how a string with an exponent is parsed. +SELECT PARSE_BIGNUMERIC("123.456E37") AS parsed; + +/*-----------------------------------------* + | parsed | + +-----------------------------------------+ + | 123400000000000000000000000000000000000 | + *-----------------------------------------*/ + +-- This example shows the rounding when digits after the decimal point exceeds 38. +SELECT PARSE_BIGNUMERIC("1.123456789012345678901234567890123456789") as parsed; + +/*------------------------------------------* + | parsed | + +------------------------------------------+ + | 1.12345678901234567890123456789012345679 | + *------------------------------------------*/ +``` + +This function is similar to using the [`CAST AS BIGNUMERIC`][cast-bignumeric] +function except that the `PARSE_BIGNUMERIC` function only accepts string inputs +and allows the following in the string: + ++ Spaces between the sign (+/-) and the number ++ Signs (+/-) after the number + +Rules for valid input strings: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RuleExample InputOutput
+ The string can only contain digits, commas, decimal points and signs. + + "- 12,34567,89.0" + -123456789
Whitespaces are allowed anywhere except between digits. + " - 12.345 " + -12.345
Only digits and commas are allowed before the decimal point. + " 12,345,678" + 12345678
Only digits are allowed after the decimal point. + "1.234 " + 1.234
+ Use E or e for exponents. After the + e, digits and a leading sign indicator are allowed. + " 123.45e-1"12.345
+ If the integer part is not empty, then it must contain at least one + digit. + " 0,.12 -"-0.12
+ If the string contains a decimal point, then it must contain at least + one digit. + " .1"0.1
+ The string cannot contain more than one sign. + " 0.5 +"0.5
+ +**Return Data Type** + +`BIGNUMERIC` + +**Examples** + +This example shows an input with spaces before, after, and between the +sign and the number: + +```sql +SELECT PARSE_BIGNUMERIC(" - 12.34 ") as parsed; + +/*--------* + | parsed | + +--------+ + | -12.34 | + *--------*/ +``` + +This example shows an input with an exponent as well as the sign after the +number: + +```sql +SELECT PARSE_BIGNUMERIC("12.34e-1-") as parsed; + +/*--------* + | parsed | + +--------+ + | -1.234 | + *--------*/ +``` + +This example shows an input with multiple commas in the integer part of the +number: + +```sql +SELECT PARSE_BIGNUMERIC(" 1,2,,3,.45 + ") as parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ +``` + +This example shows an input with a decimal point and no digits in the whole +number part: + +```sql +SELECT PARSE_BIGNUMERIC(".1234 ") as parsed; + +/*--------* + | parsed | + +--------+ + | 0.1234 | + *--------*/ +``` + +**Examples of invalid inputs** + +This example is invalid because the whole number part contains no digits: + +```sql +SELECT PARSE_BIGNUMERIC(",,,.1234 ") as parsed; +``` + +This example is invalid because there are whitespaces between digits: + +```sql +SELECT PARSE_BIGNUMERIC("1 23.4 5 ") as parsed; +``` + +This example is invalid because the number is empty except for an exponent: + +```sql +SELECT PARSE_BIGNUMERIC(" e1 ") as parsed; +``` + +This example is invalid because the string contains multiple signs: + +```sql +SELECT PARSE_BIGNUMERIC(" - 12.3 - ") as parsed; +``` + +This example is invalid because the value of the number falls outside the range +of `BIGNUMERIC`: + +```sql +SELECT PARSE_BIGNUMERIC("12.34E100 ") as parsed; +``` + +This example is invalid because the string contains invalid characters: + +```sql +SELECT PARSE_BIGNUMERIC("$12.34") as parsed; +``` + +[half-from-zero-wikipedia]: https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero + +[cast-bignumeric]: #cast_bignumeric + +[bignumeric-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#decimal_types + +### `PARSE_NUMERIC` + +```sql +PARSE_NUMERIC(string_expression) +``` + +**Description** + +Converts a `STRING` to a `NUMERIC` value. + +The numeric literal contained in the string must not exceed the +[maximum precision or range][numeric-type] of the `NUMERIC` type, or an error +occurs. If the number of digits after the decimal point exceeds nine, then the +resulting `NUMERIC` value rounds +[half away from zero][half-from-zero-wikipedia] to have nine digits after the +decimal point. + +```sql + +-- This example shows how a string with a decimal point is parsed. +SELECT PARSE_NUMERIC("123.45") AS parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ + +-- This example shows how a string with an exponent is parsed. +SELECT PARSE_NUMERIC("12.34E27") as parsed; + +/*-------------------------------* + | parsed | + +-------------------------------+ + | 12340000000000000000000000000 | + *-------------------------------*/ + +-- This example shows the rounding when digits after the decimal point exceeds 9. +SELECT PARSE_NUMERIC("1.0123456789") as parsed; + +/*-------------* + | parsed | + +-------------+ + | 1.012345679 | + *-------------*/ +``` + +This function is similar to using the [`CAST AS NUMERIC`][cast-numeric] function +except that the `PARSE_NUMERIC` function only accepts string inputs and allows +the following in the string: + ++ Spaces between the sign (+/-) and the number ++ Signs (+/-) after the number + +Rules for valid input strings: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RuleExample InputOutput
+ The string can only contain digits, commas, decimal points and signs. + + "- 12,34567,89.0" + -123456789
Whitespaces are allowed anywhere except between digits. + " - 12.345 " + -12.345
Only digits and commas are allowed before the decimal point. + " 12,345,678" + 12345678
Only digits are allowed after the decimal point. + "1.234 " + 1.234
+ Use E or e for exponents. After + the e, + digits and a leading sign indicator are allowed. + " 123.45e-1"12.345
+ If the integer part is not empty, then it must contain at least one + digit. + " 0,.12 -"-0.12
+ If the string contains a decimal point, then it must contain at least + one digit. + " .1"0.1
+ The string cannot contain more than one sign. + " 0.5 +"0.5
+ +**Return Data Type** + +`NUMERIC` + +**Examples** + +This example shows an input with spaces before, after, and between the +sign and the number: + +```sql +SELECT PARSE_NUMERIC(" - 12.34 ") as parsed; + +/*--------* + | parsed | + +--------+ + | -12.34 | + *--------*/ +``` + +This example shows an input with an exponent as well as the sign after the +number: + +```sql +SELECT PARSE_NUMERIC("12.34e-1-") as parsed; + +/*--------* + | parsed | + +--------+ + | -1.234 | + *--------*/ +``` + +This example shows an input with multiple commas in the integer part of the +number: + +```sql +SELECT PARSE_NUMERIC(" 1,2,,3,.45 + ") as parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ +``` + +This example shows an input with a decimal point and no digits in the whole +number part: + +```sql +SELECT PARSE_NUMERIC(".1234 ") as parsed; + +/*--------* + | parsed | + +--------+ + | 0.1234 | + *--------*/ +``` + +**Examples of invalid inputs** + +This example is invalid because the whole number part contains no digits: + +```sql +SELECT PARSE_NUMERIC(",,,.1234 ") as parsed; +``` + +This example is invalid because there are whitespaces between digits: + +```sql +SELECT PARSE_NUMERIC("1 23.4 5 ") as parsed; +``` + +This example is invalid because the number is empty except for an exponent: + +```sql +SELECT PARSE_NUMERIC(" e1 ") as parsed; +``` + +This example is invalid because the string contains multiple signs: + +```sql +SELECT PARSE_NUMERIC(" - 12.3 - ") as parsed; +``` + +This example is invalid because the value of the number falls outside the range +of `BIGNUMERIC`: + +```sql +SELECT PARSE_NUMERIC("12.34E100 ") as parsed; +``` + +This example is invalid because the string contains invalid characters: + +```sql +SELECT PARSE_NUMERIC("$12.34") as parsed; +``` + +[half-from-zero-wikipedia]: https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero + +[cast-numeric]: #cast_numeric + +[numeric-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#decimal_types + +### `SAFE_CAST` + + +
+SAFE_CAST(expression AS typename [format_clause])
+
+ +**Description** + +When using `CAST`, a query can fail if ZetaSQL is unable to perform +the cast. For example, the following query generates an error: + +```sql +SELECT CAST("apple" AS INT64) AS not_a_number; +``` + +If you want to protect your queries from these types of errors, you can use +`SAFE_CAST`. `SAFE_CAST` replaces runtime errors with `NULL`s. However, during +static analysis, impossible casts between two non-castable types still produce +an error because the query is invalid. + +```sql +SELECT SAFE_CAST("apple" AS INT64) AS not_a_number; + +/*--------------* + | not_a_number | + +--------------+ + | NULL | + *--------------*/ +``` + +Some casts can include a [format clause][formatting-syntax], which provides +instructions for how to conduct the +cast. For example, you could +instruct a cast to convert a sequence of bytes to a BASE64-encoded string +instead of a UTF-8-encoded string. + +The structure of the format clause is unique to each type of cast and more +information is available in the section for that cast. + +If you are casting from bytes to strings, you can also use the +function, [`SAFE_CONVERT_BYTES_TO_STRING`][SC_BTS]. Any invalid UTF-8 characters +are replaced with the unicode replacement character, `U+FFFD`. + +[SC_BTS]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#safe_convert_bytes_to_string + +[formatting-syntax]: https://github.com/google/zetasql/blob/master/docs/format-elements.md#formatting_syntax + +### Other conversion functions + + +You can learn more about these conversion functions elsewhere in the +documentation: + + + +Conversion function | From | To +------- | -------- | ------- +[ARRAY_TO_STRING][ARRAY_STRING] | ARRAY | STRING +[BIT_CAST_TO_INT32][BIT_I32] | UINT32 | INT32 +[BIT_CAST_TO_INT64][BIT_I64] | UINT64 | INT64 +[BIT_CAST_TO_UINT32][BIT_U32] | INT32 | UINT32 +[BIT_CAST_TO_UINT64][BIT_U64] | INT64 | UINT64 +[BOOL][JSON_TO_BOOL] | JSON | BOOL +[DATE][T_DATE] | Various data types | DATE +[DATE_FROM_UNIX_DATE][T_DATE_FROM_UNIX_DATE] | INT64 | DATE +[DATETIME][T_DATETIME] | Various data types | DATETIME +[DOUBLE][JSON_TO_DOUBLE] | JSON | DOUBLE +[FROM_BASE32][F_B32] | STRING | BYTEs +[FROM_BASE64][F_B64] | STRING | BYTES +[FROM_HEX][F_HEX] | STRING | BYTES +[FROM_PROTO][F_PROTO] | PROTO value | Most data types +[INT64][JSON_TO_INT64] | JSON | INT64 +[PARSE_DATE][P_DATE] | STRING | DATE +[PARSE_DATETIME][P_DATETIME] | STRING | DATETIME +[PARSE_JSON][P_JSON] | STRING | JSON +[PARSE_TIME][P_TIME] | STRING | TIME +[PARSE_TIMESTAMP][P_TIMESTAMP] | STRING | TIMESTAMP +[SAFE_CONVERT_BYTES_TO_STRING][SC_BTS] | BYTES | STRING +[STRING][STRING_TIMESTAMP] | TIMESTAMP | STRING +[STRING][JSON_TO_STRING] | JSON | STRING +[TIME][T_TIME] | Various data types | TIME +[TIMESTAMP][T_TIMESTAMP] | Various data types | TIMESTAMP +[TIMESTAMP_FROM_UNIX_MICROS][T_TIMESTAMP_FROM_UNIX_MICROS] | INT64 | TIMESTAMP +[TIMESTAMP_FROM_UNIX_MILLIS][T_TIMESTAMP_FROM_UNIX_MILLIS] | INT64 | TIMESTAMP +[TIMESTAMP_FROM_UNIX_SECONDS][T_TIMESTAMP_FROM_UNIX_SECONDS] | INT64 | TIMESTAMP +[TIMESTAMP_MICROS][T_TIMESTAMP_MICROS] | INT64 | TIMESTAMP +[TIMESTAMP_MILLIS][T_TIMESTAMP_MILLIS] | INT64 | TIMESTAMP +[TIMESTAMP_SECONDS][T_TIMESTAMP_SECONDS] | INT64 | TIMESTAMP +[TO_BASE32][T_B32] | BYTES | STRING +[TO_BASE64][T_B64] | BYTES | STRING +[TO_HEX][T_HEX] | BYTES | STRING +[TO_JSON][T_JSON] | All data types | JSON +[TO_JSON_STRING][T_JSON_STRING] | All data types | STRING +[TO_PROTO][T_PROTO] | Most data types | PROTO value + + + + + +[conversion-rules]: https://github.com/google/zetasql/blob/master/docs/conversion_rules.md + +[ARRAY_STRING]: https://github.com/google/zetasql/blob/master/docs/array_functions.md#array_to_string + +[BIT_I32]: https://github.com/google/zetasql/blob/master/docs/bit_functions.md#bit_cast_to_int32 + +[BIT_U32]: https://github.com/google/zetasql/blob/master/docs/bit_functions.md#bit_cast_to_uint32 + +[BIT_I64]: https://github.com/google/zetasql/blob/master/docs/bit_functions.md#bit_cast_to_int64 + +[BIT_U64]: https://github.com/google/zetasql/blob/master/docs/bit_functions.md#bit_cast_to_uint64 + +[F_B32]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#from_base32 + +[F_B64]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#from_base64 + +[F_HEX]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#from_hex + +[F_PROTO]: https://github.com/google/zetasql/blob/master/docs/protocol_buffer_functions.md#from_proto + +[P_DATE]: https://github.com/google/zetasql/blob/master/docs/date_functions.md#parse_date + +[P_DATETIME]: https://github.com/google/zetasql/blob/master/docs/datetime_functions.md#parse_datetime + +[P_JSON]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#parse_json + +[P_TIME]: https://github.com/google/zetasql/blob/master/docs/time_functions.md#parse_time + +[P_TIMESTAMP]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#parse_timestamp + +[SC_BTS]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#safe_convert_bytes_to_string + +[STRING_TIMESTAMP]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#string + +[T_B32]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#to_base32 + +[T_B64]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#to_base64 + +[T_HEX]: https://github.com/google/zetasql/blob/master/docs/string_functions.md#to_hex + +[T_JSON]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#to_json + +[T_JSON_STRING]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#to_json_string + +[T_PROTO]: https://github.com/google/zetasql/blob/master/docs/protocol_buffer_functions.md#to_proto + +[T_DATE]: https://github.com/google/zetasql/blob/master/docs/date_functions.md#date + +[T_DATETIME]: https://github.com/google/zetasql/blob/master/docs/datetime_functions.md#datetime + +[T_TIMESTAMP]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp + +[T_TIME]: https://github.com/google/zetasql/blob/master/docs/time_functions.md#time + +[JSON_TO_BOOL]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#bool_for_json + +[JSON_TO_STRING]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#string_for_json + +[JSON_TO_INT64]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#int64_for_json + +[JSON_TO_DOUBLE]: https://github.com/google/zetasql/blob/master/docs/json_functions.md#double_for_json + +[T_DATE_FROM_UNIX_DATE]: https://github.com/google/zetasql/blob/master/docs/date_functions.md#date_from_unix_date + +[T_TIMESTAMP_FROM_UNIX_MICROS]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp_from_unix_micros + +[T_TIMESTAMP_FROM_UNIX_MILLIS]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp_from_unix_millis + +[T_TIMESTAMP_FROM_UNIX_SECONDS]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp_from_unix_seconds + +[T_TIMESTAMP_MICROS]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp_micros + +[T_TIMESTAMP_MILLIS]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp_millis + +[T_TIMESTAMP_SECONDS]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timestamp_seconds + + + diff --git a/docs/data-manipulation-language.md b/docs/data-manipulation-language.md index b06daad0d..447ae7ff2 100644 --- a/docs/data-manipulation-language.md +++ b/docs/data-manipulation-language.md @@ -533,8 +533,8 @@ inserted rows that include these parts: + `WITH ACTION`: An optional clause that adds a `string` column called `ACTION` to the result row set. Each value in this column represents the type of action that was applied to the column during statement execution. - Values include `"INSERT"`, `"REPLACE"`, and `"UPDATE"`. The `ACTION` column - is appended as the last output column. + Values include `"INSERT"`, `"REPLACE"`, + and `"UPDATE"`. The `ACTION` column is appended as the last output column. + `*`: Returns all columns. + `expression.*`: Returns all columns from range variables, like a table or proto field. The dot star expression cannot be applied on other expressions, diff --git a/docs/data-model.md b/docs/data-model.md index c62c6a8a6..92557e8a9 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -224,8 +224,7 @@ pseudocolumns such as `ROWNUM`. In addition to standard SQL _tables_, ZetaSQL supports _value tables_. In a value table, rather than having rows made up of a list of columns, each row -is a single value of -a specific type, and there are no column names. +is a single value of type `STRUCT`, and there are no column names. In the following example, a value table for a `STRUCT` is produced with the `SELECT AS VALUE` statement: @@ -240,11 +239,6 @@ SELECT * FROM (SELECT AS VALUE STRUCT(123 AS a, FALSE AS b)) *-----+-------*/ ``` -Value tables are often but not exclusively used with compound data types. -A value table can consist of any supported ZetaSQL data type, -although value tables consisting of scalar types occur less frequently than -structs or protocol buffers. - Value tables are common when working with protocol buffers that may be stored in files instead of in the database. diff --git a/docs/data-types.md b/docs/data-types.md index 35f2cc977..cd3cebf1a 100644 --- a/docs/data-types.md +++ b/docs/data-types.md @@ -157,7 +157,7 @@ information on data type literals and constructors, see

- A decimal value with precision of 76.76 digits.
+ A decimal value with precision of 76.76 digits (the 77th digit is partial).
SQL type name: BIGNUMERIC
SQL aliases: BIGDECIMAL @@ -259,7 +259,8 @@ properties in mind: ### Nullable data types For nullable data types, `NULL` is a valid value. Currently, all existing -data types are nullable. +data types are nullable. Conditions apply for +[arrays][array-nulls]. ### Orderable data types @@ -460,8 +461,58 @@ arrays][working-with-arrays]. ### `NULL`s and the array type -An empty array and a `NULL` array are two distinct values. Arrays can contain -`NULL` elements. +Currently, ZetaSQL has the following rules with respect to `NULL`s and +arrays: + ++ An array can be `NULL`. + + For example: + + ```sql + SELECT CAST(NULL AS ARRAY) IS NULL AS array_is_null; + + /*---------------* + | array_is_null | + +---------------+ + | TRUE | + *---------------*/ + ``` ++ ZetaSQL translates a `NULL` array into an empty array in the query + result, although inside the query, `NULL` and empty arrays are two distinct + values. + + For example: + + ```sql + WITH Items AS ( + SELECT [] AS numbers, "Empty array in query" AS description UNION ALL + SELECT CAST(NULL AS ARRAY), "NULL array in query") + SELECT numbers, description, numbers IS NULL AS numbers_null + FROM Items; + + /*---------+----------------------+--------------* + | numbers | description | numbers_null | + +---------+----------------------+--------------+ + | [] | Empty array in query | false | + | [] | NULL array in query | true | + *---------+----------------------+--------------*/ + ``` + + When you write a `NULL` array to a table, it is converted to an + empty array. If you write `Items` to a table from the previous query, + then each array is written as an empty array: + + ```sql + SELECT numbers, description, numbers IS NULL AS numbers_null + FROM Items; + + /*---------+----------------------+--------------* + | numbers | description | numbers_null | + +---------+----------------------+--------------+ + | [] | Empty array in query | false | + | [] | NULL array in query | false | + *---------+----------------------+--------------*/ + ``` ### Declaring an array type @@ -1351,8 +1402,8 @@ Expect these canonicalization behaviors when creating a value of JSON type: + Booleans, strings, and nulls are preserved exactly. + Whitespace characters are not preserved. + A JSON value can store integers in the range of - -9,223,372,036,854,775,808 (minimal signed 64-bit integer) to - 18,446,744,073,709,551,615 (maximal unsigned 64-bit integer) and + -9,223,372,036,854,775,808 (minimum signed 64-bit integer) to + 18,446,744,073,709,551,615 (maximum unsigned 64-bit integer) and floating point numbers within a domain of `DOUBLE`. + The order of elements in an array is preserved exactly. @@ -2700,6 +2751,8 @@ when there is a leap second. [timestamp-literals]: https://github.com/google/zetasql/blob/master/docs/lexical.md#timestamp_literals +[array-nulls]: #array_nulls + [floating-point-types]: #floating_point_types [lexical-literals]: https://github.com/google/zetasql/blob/master/docs/lexical.md#literals diff --git a/docs/date_functions.md b/docs/date_functions.md index 50b8fab57..b81ba7a0c 100644 --- a/docs/date_functions.md +++ b/docs/date_functions.md @@ -214,24 +214,6 @@ SELECT CURRENT_DATE AS the_date; *--------------*/ ``` -When a column named `current_date` is present, the column name and the function -call without parentheses are ambiguous. To ensure the function call, add -parentheses; to ensure the column name, qualify it with its -[range variable][date-range-variables]. For example, the -following query will select the function in the `the_date` column and the table -column in the `current_date` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_date`) -SELECT current_date() AS the_date, t.current_date FROM t; - -/*------------+--------------* - | the_date | current_date | - +------------+--------------+ - | 2016-12-25 | column value | - *------------+--------------*/ -``` - [date-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables [date-timezone-definitions]: https://github.com/google/zetasql/blob/master/docs/data-types.md#time_zones @@ -345,7 +327,9 @@ Gets the number of unit boundaries between two `DATE` values (`end_date` - + `start_date`: The starting `DATE` value. + `end_date`: The ending `DATE` value. -+ `granularity`: The date part that represents the granularity. This can be: ++ `granularity`: The date part that represents the granularity. If + you have passed in `DATE` values for the first arguments, `granularity` can + be: + `DAY` + `WEEK` This date part begins on Sunday. @@ -365,6 +349,10 @@ Gets the number of unit boundaries between two `DATE` values (`end_date` - If `end_date` is earlier than `start_date`, the output is negative. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `DATE_DIFF(TIMESTAMP, TIMESTAMP, PART)` +behaves like `TIMESTAMP_DIFF(TIMESTAMP, TIMESTAMP, PART)`. + **Return Data Type** `INT64` @@ -510,38 +498,51 @@ SELECT DATE_SUB(DATE '2008-12-25', INTERVAL 5 DAY) AS five_days_ago; ### `DATE_TRUNC` ```sql -DATE_TRUNC(date_expression, date_part) +DATE_TRUNC(date_expression, granularity) ``` **Description** -Truncates a `DATE` value to the granularity of `date_part`. The `DATE` value -is always rounded to the beginning of `date_part`, which can be one of the -following: - -+ `DAY`: The day in the Gregorian calendar year that contains the - `DATE` value. -+ `WEEK`: The first day of the week in the week that contains the - `DATE` value. Weeks begin on Sundays. `WEEK` is equivalent to - `WEEK(SUNDAY)`. -+ `WEEK(WEEKDAY)`: The first day of the week in the week that contains the - `DATE` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the - following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, - or `SATURDAY`. -+ `ISOWEEK`: The first day of the [ISO 8601 week][ISO-8601-week] in the - ISO week that contains the `DATE` value. The ISO week begins on - Monday. The first ISO week of each ISO year contains the first Thursday of the - corresponding Gregorian calendar year. -+ `MONTH`: The first day of the month in the month that contains the - `DATE` value. -+ `QUARTER`: The first day of the quarter in the quarter that contains the - `DATE` value. -+ `YEAR`: The first day of the year in the year that contains the - `DATE` value. -+ `ISOYEAR`: The first day of the [ISO 8601][ISO-8601] week-numbering year - in the ISO year that contains the `DATE` value. The ISO year is the - Monday of the first week whose Thursday belongs to the corresponding - Gregorian calendar year. +Truncates a `DATE` value at a particular time granularity. The `DATE` value +is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `date_expression`: The `DATE` value to truncate. ++ `granularity`: The date part that represents the granularity. If + you passed in a `DATE` value for the first argument, `granularity` can + be: + + + `DAY`: The day in the Gregorian calendar year that contains the + `DATE` value. + + + `WEEK`: The first day in the week that contains the + `DATE` value. Weeks begin on Sundays. `WEEK` is equivalent to + `WEEK(SUNDAY)`. + + + `WEEK(WEEKDAY)`: The first day in the week that contains the + `DATE` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the + following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, + or `SATURDAY`. + + + `ISOWEEK`: The first day in the [ISO 8601 week][ISO-8601-week] that contains + the `DATE` value. The ISO week begins on + Monday. The first ISO week of each ISO year contains the first Thursday of the + corresponding Gregorian calendar year. + + + `MONTH`: The first day in the month that contains the + `DATE` value. + + + `QUARTER`: The first day in the quarter that contains the + `DATE` value. + + + `YEAR`: The first day in the year that contains the + `DATE` value. + + + `ISOYEAR`: The first day in the [ISO 8601][ISO-8601] week-numbering year + that contains the `DATE` value. The ISO year is the + Monday of the first week where Thursday belongs to the corresponding + Gregorian calendar year. @@ -553,7 +554,7 @@ following: **Return Data Type** -DATE +`DATE` **Examples** diff --git a/docs/datetime_functions.md b/docs/datetime_functions.md index 6a1a5c978..de534e2b7 100644 --- a/docs/datetime_functions.md +++ b/docs/datetime_functions.md @@ -152,24 +152,6 @@ SELECT CURRENT_DATETIME() as now; *----------------------------*/ ``` -When a column named `current_datetime` is present, the column name and the -function call without parentheses are ambiguous. To ensure the function call, -add parentheses; to ensure the column name, qualify it with its -[range variable][datetime-range-variables]. For example, the -following query will select the function in the `now` column and the table -column in the `current_datetime` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_datetime`) -SELECT current_datetime() as now, t.current_datetime FROM t; - -/*----------------------------+------------------* - | now | current_datetime | - +----------------------------+------------------+ - | 2016-05-19 10:38:47.046465 | column value | - *----------------------------+------------------*/ -``` - [datetime-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables [datetime-timezone-definitions]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timezone_definitions @@ -277,8 +259,9 @@ Gets the number of unit boundaries between two `DATETIME` values + `start_datetime`: The starting `DATETIME` value. + `end_datetime`: The ending `DATETIME` value. -+ `granularity`: The datetime part that represents the granularity. - This can be: ++ `granularity`: The datetime part that represents the granularity. If + you have passed in `DATETIME` values for the first arguments, `granularity` + can be: + `NANOSECOND` @@ -310,6 +293,10 @@ Produces an error if the computation overflows, such as if the difference in nanoseconds between the two `DATETIME` values overflows. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `DATETIME_DIFF(TIMESTAMP, TIMESTAMP, PART)` +behaves like `TIMESTAMP_DIFF(TIMESTAMP, TIMESTAMP, PART)`. + **Return Data Type** `INT64` @@ -447,44 +434,63 @@ SELECT ### `DATETIME_TRUNC` ```sql -DATETIME_TRUNC(datetime_expression, date_time_part) +DATETIME_TRUNC(datetime_expression, granularity) ``` **Description** -Truncates a `DATETIME` value to the granularity of `date_time_part`. -The `DATETIME` value is always rounded to the beginning of `date_time_part`, -which can be one of the following: - -+ `NANOSECOND`: If used, nothing is truncated from the value. -+ `MICROSECOND`: The nearest lessor or equal microsecond. -+ `MILLISECOND`: The nearest lessor or equal millisecond. -+ `SECOND`: The nearest lessor or equal second. -+ `MINUTE`: The nearest lessor or equal minute. -+ `HOUR`: The nearest lessor or equal hour. -+ `DAY`: The day in the Gregorian calendar year that contains the - `DATETIME` value. -+ `WEEK`: The first day of the week in the week that contains the - `DATETIME` value. Weeks begin on Sundays. `WEEK` is equivalent to - `WEEK(SUNDAY)`. -+ `WEEK(WEEKDAY)`: The first day of the week in the week that contains the - `DATETIME` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the - following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, - or `SATURDAY`. -+ `ISOWEEK`: The first day of the [ISO 8601 week][ISO-8601-week] in the - ISO week that contains the `DATETIME` value. The ISO week begins on - Monday. The first ISO week of each ISO year contains the first Thursday of the - corresponding Gregorian calendar year. -+ `MONTH`: The first day of the month in the month that contains the - `DATETIME` value. -+ `QUARTER`: The first day of the quarter in the quarter that contains the - `DATETIME` value. -+ `YEAR`: The first day of the year in the year that contains the - `DATETIME` value. -+ `ISOYEAR`: The first day of the [ISO 8601][ISO-8601] week-numbering year - in the ISO year that contains the `DATETIME` value. The ISO year is the - Monday of the first week whose Thursday belongs to the corresponding - Gregorian calendar year. +Truncates a `DATETIME` value at a particular time granularity. The `DATETIME` +value is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `datetime_expression`: The `DATETIME` value to truncate. ++ `granularity`: The datetime part that represents the granularity. If + you passed in a `DATETIME` value for the first argument, `granularity` can + be: + + + `NANOSECOND`: If used, nothing is truncated from the value. + + + `MICROSECOND`: The nearest lesser than or equal microsecond. + + + `MILLISECOND`: The nearest lesser than or equal millisecond. + + + `SECOND`: The nearest lesser than or equal second. + + + `MINUTE`: The nearest lesser than or equal minute. + + + `HOUR`: The nearest lesser than or equal hour. + + + `DAY`: The day in the Gregorian calendar year that contains the + `DATETIME` value. + + + `WEEK`: The first day in the week that contains the + `DATETIME` value. Weeks begin on Sundays. `WEEK` is equivalent to + `WEEK(SUNDAY)`. + + + `WEEK(WEEKDAY)`: The first day in the week that contains the + `DATETIME` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the + following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, + or `SATURDAY`. + + + `ISOWEEK`: The first day in the [ISO 8601 week][ISO-8601-week] that contains + the `DATETIME` value. The ISO week begins on + Monday. The first ISO week of each ISO year contains the first Thursday of the + corresponding Gregorian calendar year. + + + `MONTH`: The first day in the month that contains the + `DATETIME` value. + + + `QUARTER`: The first day in the quarter that contains the + `DATETIME` value. + + + `YEAR`: The first day in the year that contains the + `DATETIME` value. + + + `ISOYEAR`: The first day in the [ISO 8601][ISO-8601] week-numbering year + that contains the `DATETIME` value. The ISO year is the + Monday of the first week where Thursday belongs to the corresponding + Gregorian calendar year. diff --git a/docs/debugging_functions.md b/docs/debugging_functions.md index 251170e13..c171e00e5 100644 --- a/docs/debugging_functions.md +++ b/docs/debugging_functions.md @@ -72,7 +72,8 @@ Returns an error. **Definitions** + `error_message`: A `STRING` value that represents the error message to - produce. + produce. Any whitespace characters beyond a + single space are trimmed from the results. **Details** @@ -85,6 +86,13 @@ ZetaSQL infers the return type in context. **Examples** +In the following example, the query produces an error message: + +```sql +-- ERROR: Show this error message (while evaluating error("Show this error message")) +SELECT ERROR('Show this error message') +``` + In the following example, the query returns an error message if the value of the row does not match one of two defined values. @@ -168,7 +176,7 @@ The [supertype][supertype] for `try_expression` and **Example** -In the following examples, the query successfully evaluates `try_expression`. +In the following example, the query successfully evaluates `try_expression`. ```sql SELECT IFERROR('a', 'b') AS result @@ -180,6 +188,9 @@ SELECT IFERROR('a', 'b') AS result *--------*/ ``` +In the following example, the query successfully evaluates the +`try_expression` subquery. + ```sql SELECT IFERROR((SELECT [1,2,3][OFFSET(0)]), -1) AS result @@ -190,7 +201,7 @@ SELECT IFERROR((SELECT [1,2,3][OFFSET(0)]), -1) AS result *--------*/ ``` -In the following examples, `IFERROR` catches an evaluation error in the +In the following example, `IFERROR` catches an evaluation error in the `try_expression` and successfully evaluates `catch_expression`. ```sql @@ -203,6 +214,9 @@ SELECT IFERROR(ERROR('a'), 'b') AS result *--------*/ ``` +In the following example, `IFERROR` catches an evaluation error in the +`try_expression` subquery and successfully evaluates `catch_expression`. + ```sql SELECT IFERROR((SELECT [1,2,3][OFFSET(9)]), -1) AS result @@ -397,7 +411,7 @@ The data type for `try_expression` or `NULL` **Example** -In the following examples, `NULLIFERROR` successfully evaluates +In the following example, `NULLIFERROR` successfully evaluates `try_expression`. ```sql @@ -410,6 +424,9 @@ SELECT NULLIFERROR('a') AS result *--------*/ ``` +In the following example, `NULLIFERROR` successfully evaluates +the `try_expression` subquery. + ```sql SELECT NULLIFERROR((SELECT [1,2,3][OFFSET(0)])) AS result @@ -420,7 +437,7 @@ SELECT NULLIFERROR((SELECT [1,2,3][OFFSET(0)])) AS result *--------*/ ``` -In the following examples, `NULLIFERROR` catches an evaluation error in +In the following example, `NULLIFERROR` catches an evaluation error in `try_expression`. ```sql @@ -433,6 +450,9 @@ SELECT NULLIFERROR(ERROR('a')) AS result *--------*/ ``` +In the following example, `NULLIFERROR` catches an evaluation error in +the `try_expression` subquery. + ```sql SELECT NULLIFERROR((SELECT [1,2,3][OFFSET(9)])) AS result diff --git a/docs/format-elements.md b/docs/format-elements.md new file mode 100644 index 000000000..bc6484b7e --- /dev/null +++ b/docs/format-elements.md @@ -0,0 +1,3259 @@ + + + + +# Format elements + +ZetaSQL supports the following format elements. + +## Format elements for date and time parts + + +Many ZetaSQL parsing and formatting functions rely on a format string +to describe the format of parsed or formatted values. A format string represents +the textual form of date and time and contains separate format elements that are +applied left-to-right. + +These functions use format strings: + ++ [`FORMAT_DATE`][format-date] ++ [`FORMAT_DATETIME`][format-datetime] ++ [`FORMAT_TIME`][format-time] ++ [`FORMAT_TIMESTAMP`][format-timestamp] ++ [`PARSE_DATE`][parse-date] ++ [`PARSE_DATETIME`][parse-datetime] ++ [`PARSE_TIME`][parse-time] ++ [`PARSE_TIMESTAMP`][parse-timestamp] + +Format strings generally support the following elements: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementTypeDescriptionExample
%A + +DATE
DATETIME
TIMESTAMP
+
The full weekday name (English).Wednesday
%a + +DATE
DATETIME
TIMESTAMP
+
The abbreviated weekday name (English).Wed
%B + +DATE
DATETIME
TIMESTAMP
+
The full month name (English).January
%b + +DATE
DATETIME
TIMESTAMP
+
The abbreviated month name (English).Jan
%C + +DATE
DATETIME
TIMESTAMP
+
+ The century (a year divided by 100 and truncated to an integer) as a + decimal number (00-99). + 20
%c + +DATETIME
TIMESTAMP
+
The date and time representation (English).Wed Jan 20 21:47:00 2021
%D + +DATE
DATETIME
TIMESTAMP
+
The date in the format %m/%d/%y.01/20/21
%d + +DATE
DATETIME
TIMESTAMP
+
The day of the month as a decimal number (01-31).20
%e + +DATE
DATETIME
TIMESTAMP
+
+ The day of month as a decimal number (1-31); single digits are preceded + by a space. + 20
%F + +DATE
DATETIME
TIMESTAMP
+
The date in the format %Y-%m-%d.2021-01-20
%G + +DATE
DATETIME
TIMESTAMP
+
+ The ISO 8601 + year with century as a decimal number. Each ISO year begins + on the Monday before the first Thursday of the Gregorian calendar year. + Note that %G and %Y may produce different results near Gregorian year + boundaries, where the Gregorian year and ISO year can diverge.2021
%g + +DATE
DATETIME
TIMESTAMP
+
+ The ISO 8601 + year without century as a decimal number (00-99). Each ISO + year begins on the Monday before the first Thursday of the Gregorian + calendar year. Note that %g and %y may produce different results near + Gregorian year boundaries, where the Gregorian year and ISO year can + diverge. + 21
%H + +TIME
DATETIME
TIMESTAMP
+
The hour (24-hour clock) as a decimal number (00-23).21
%h + +DATE
DATETIME
TIMESTAMP
+
The abbreviated month name (English).Jan
%I + +TIME
DATETIME
TIMESTAMP
+
The hour (12-hour clock) as a decimal number (01-12).09
%J + +DATE
DATETIME
TIMESTAMP
+
+ The ISO 8601 + 1-based day of the year (001-364 or 001-371 days). If the ISO year is + not set, this format element is ignored. + 364
%j + +DATE
DATETIME
TIMESTAMP
+
The day of the year as a decimal number (001-366).020
%k + +TIME
DATETIME
TIMESTAMP
+
+ The hour (24-hour clock) as a decimal number (0-23); single digits are + preceded by a space.21
%l + +TIME
DATETIME
TIMESTAMP
+
+ The hour (12-hour clock) as a decimal number (1-12); single digits are + preceded by a space. 9
%M + +TIME
DATETIME
TIMESTAMP
+
The minute as a decimal number (00-59).47
%m + +DATE
DATETIME
TIMESTAMP
+
The month as a decimal number (01-12).01
%nAllA newline character.
%P + +TIME
DATETIME
TIMESTAMP
+
+ When formatting, this is either am or pm. +
+ + + + When parsing, this can be used with am, pm, AM, or PM. + + + +
+
pm
%p + +TIME
DATETIME
TIMESTAMP
+
When formatting, this is either AM or PM. +
When parsing, this can be used with am, pm, AM, or PM.
PM
%Q + +DATE
DATETIME
TIMESTAMP
+
The quarter as a decimal number (1-4).1
%R + +TIME
DATETIME
TIMESTAMP
+
The time in the format %H:%M.21:47
%S + +TIME
DATETIME
TIMESTAMP
+
The second as a decimal number (00-60).00
%s + +TIME
DATETIME
TIMESTAMP
+
+ The number of seconds since 1970-01-01 00:00:00. Always overrides all + other format elements, independent of where %s appears in the string. + If multiple %s elements appear, then the last one takes precedence. + 1611179220
%T + +TIME
DATETIME
TIMESTAMP
+
The time in the format %H:%M:%S.21:47:00
%tAllA tab character.
%U + +DATE
DATETIME
TIMESTAMP
+
+ The week number of the year (Sunday as the first day of the week) as a + decimal number (00-53). + 03
%u + +DATE
DATETIME
TIMESTAMP
+
+ The weekday (Monday as the first day of the week) as a decimal number + (1-7). + 3
%V + +DATE
DATETIME
TIMESTAMP
+
+ The ISO 8601 + week number of the year (Monday as the first + day of the week) as a decimal number (01-53). If the week containing + January 1 has four or more days in the new year, then it is week 1; + otherwise it is week 53 of the previous year, and the next week is + week 1. + 03
%W + +DATE
DATETIME
TIMESTAMP
+
+ The week number of the year (Monday as the first day of the week) as a + decimal number (00-53). + 03
%w + +DATE
DATETIME
TIMESTAMP
+
+ The weekday (Sunday as the first day of the week) as a decimal number + (0-6). + 3
%X + +TIME
DATETIME
TIMESTAMP
+
The time representation in HH:MM:SS format.21:47:00
%x + +DATE
DATETIME
TIMESTAMP
+
The date representation in MM/DD/YY format.01/20/21
%Y + +DATE
DATETIME
TIMESTAMP
+
The year with century as a decimal number.2021
%y + +DATE
DATETIME
TIMESTAMP
+
+ The year without century as a decimal number (00-99), with an optional + leading zero. Can be mixed with %C. If %C is not specified, years 00-68 are + 2000s, while years 69-99 are 1900s. + 21
%Z + +TIMESTAMP
+
The time zone name.UTC-5
%z + +TIMESTAMP
+
+ The offset from the Prime Meridian in the format +HHMM or -HHMM as + appropriate, with positive values representing locations east of + Greenwich. + -0500
%%AllA single % character.%
%Ez + +TIMESTAMP
+
RFC 3339-compatible numeric time zone (+HH:MM or -HH:MM).-05:00
%E<number>S + +TIME
DATETIME
TIMESTAMP
+
Seconds with <number> digits of fractional precision.00.000 for %E3S
%E*S + +TIME
DATETIME
TIMESTAMP
+
Seconds with full fractional precision (a literal '*').00.123456
%E4Y + +DATE
DATETIME
TIMESTAMP
+
+ Four-character years (0001 ... 9999). Note that %Y produces as many + characters as it takes to fully render the year. + 2021
+ +Examples: + +```sql +SELECT FORMAT_DATE("%b-%d-%Y", DATE "2008-12-25") AS formatted; + +/*-------------* + | formatted | + +-------------+ + | Dec-25-2008 | + *-------------*/ +``` + +```sql +SELECT + FORMAT_DATETIME("%c", DATETIME "2008-12-25 15:30:00") + AS formatted; + +/*--------------------------* + | formatted | + +--------------------------+ + | Thu Dec 25 15:30:00 2008 | + *--------------------------*/ +``` + +```sql +SELECT FORMAT_TIME("%R", TIME "15:30:00") as formatted_time; + +/*----------------* + | formatted_time | + +----------------+ + | 15:30 | + *----------------*/ +``` + +```sql +SELECT FORMAT_TIMESTAMP("%b %Y %Ez", TIMESTAMP "2008-12-25 15:30:00+00") + AS formatted; + +/*-----------------* + | formatted | + +-----------------+ + | Dec 2008 +00:00 | + *-----------------*/ +``` + +```sql +SELECT PARSE_DATE("%Y%m%d", "20081225") AS parsed; + +/*------------* + | parsed | + +------------+ + | 2008-12-25 | + *------------*/ +``` + +```sql +SELECT PARSE_DATETIME('%Y-%m-%d %H:%M:%S', '1998-10-18 13:45:55') AS datetime; + +/*---------------------* + | datetime | + +---------------------+ + | 1998-10-18 13:45:55 | + *---------------------*/ +``` + +```sql +SELECT PARSE_TIME('%I:%M:%S %p', '2:23:38 pm') AS parsed_time + +/*-------------* + | parsed_time | + +-------------+ + | 14:23:38 | + *-------------*/ +``` + +```sql +SELECT PARSE_TIMESTAMP("%c", "Thu Dec 25 07:30:00 2008") AS parsed; + +-- Display of results may differ, depending upon the environment and +-- time zone where this query was executed. +/*---------------------------------------------* + | parsed | + +---------------------------------------------+ + | 2008-12-25 07:30:00.000 America/Los_Angeles | + *---------------------------------------------*/ +``` + +## Format clause for CAST + + +```sql +format_clause: + FORMAT format_model + +format_model: + format_string_expression +``` + +The format clause can be used in some [`CAST` functions][cast-functions]. You +use a format clause to provide instructions for how to conduct a +cast. For example, you could +instruct a cast to convert a sequence of bytes to a BASE64-encoded string +instead of a UTF-8-encoded string. + +The format clause includes a format model. The format model can contain +format elements combined together as a format string. + +### Format bytes as string + + +```sql +CAST(bytes_expression AS STRING FORMAT format_string_expression) +``` + +You can cast a sequence of bytes to a string with a format element in the +format string. If the bytes cannot be formatted with a +format element, an error is returned. If the sequence of bytes is `NULL`, the +result is `NULL`. Format elements are case-insensitive. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
HEX + Converts a sequence of bytes into a hexadecimal string. + + Input: b'\x00\x01\xEF\xFF'
+ Output: 0001efff +
+ BASEX + + Converts a sequence of bytes into a + BASEX encoded string. + X represents one of these numbers: 2, 8, 16, 32, 64. + + Input as BASE8: b'\x02\x11\x3B'
+ Output: 00410473 +
BASE64M + Converts a sequence of bytes into a + BASE64-encoded string based on + rfc 2045 + for MIME. Generates a newline character ("\n") every 76 characters. + + Input: b'\xde\xad\xbe\xef'
+ Output: 3q2+7w== +
ASCII + Converts a sequence of bytes that are ASCII values to a string. If the + input contains bytes that are not a valid ASCII encoding, an error + is returned. + + Input: b'\x48\x65\x6c\x6c\x6f'
+ Output: Hello +
UTF-8 + Converts a sequence of bytes that are UTF-8 values to a string. + If the input contains bytes that are not a valid UTF-8 encoding, + an error is returned. + + Input: b'\x24'
+ Output: $ +
UTF8 + Same behavior as UTF-8. + +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(b'\x48\x65\x6c\x6c\x6f' AS STRING FORMAT 'ASCII') AS bytes_to_string; + +/*-----------------* + | bytes_to_string | + +-----------------+ + | Hello | + *-----------------*/ +``` + +### Format string as bytes + + +```sql +CAST(string_expression AS BYTES FORMAT format_string_expression) +``` + +You can cast a string to bytes with a format element in the +format string. If the string cannot be formatted with the +format element, an error is returned. Format elements are case-insensitive. + +In the string expression, whitespace characters, such as `\n`, are ignored +if the `BASE64` or `BASE64M` format element is used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
HEX + Converts a hexadecimal-encoded string to bytes. If the input + contains characters that are not part of the HEX encoding alphabet + (0~9, case-insensitive a~f), an error is returned. + + Input: '0001efff'
+ Output: b'\x00\x01\xEF\xFF' +
+ BASEX + + Converts a BASEX-encoded string to + bytes. X represents one of these numbers: 2, 8, 16, 32, 64. An error + is returned if the input contains characters that are not part of the + BASEX encoding alphabet, except whitespace characters if the + format element is BASE64. + + Input as BASE8: '00410473'
+ Output: b'\x02\x11\x3B' +
BASE64M + Converts a BASE64-encoded string to + bytes. If the input contains characters that are not whitespace and not + part of the BASE64 encoding alphabet defined at + rfc 2045, + an error is returned. BASE64M and BASE64 decoding have the same + behavior. + + Input: '3q2+7w=='
+ Output: b'\xde\xad\xbe\xef' +
ASCII + Converts a string with only ASCII characters to bytes. If the input + contains characters that are not ASCII characters, an error is + returned. + + Input: 'Hello'
+ Output: b'\x48\x65\x6c\x6c\x6f' +
UTF-8 + Converts a string to a sequence of UTF-8 bytes. + + Input: '$'
+ Output: b'\x24' +
UTF8 + Same behavior as UTF-8. + +
+ +**Return type** + +`BYTES` + +**Example** + +```sql +SELECT CAST('Hello' AS BYTES FORMAT 'ASCII') AS string_to_bytes + +/*-------------------------* + | string_to_bytes | + +-------------------------+ + | b'\x48\x65\x6c\x6c\x6f' | + *-------------------------*/ +``` + +### Format date and time as string + + +You can format these date and time parts as a string: + ++ [Format year part as string][format-year-as-string] ++ [Format month part as string][format-month-as-string] ++ [Format day part as string][format-day-as-string] ++ [Format hour part as string][format-hour-as-string] ++ [Format minute part as string][format-minute-as-string] ++ [Format second part as string][format-second-as-string] ++ [Format meridian indicator as string][format-meridian-as-string] ++ [Format time zone as string][format-tz-as-string] ++ [Format literal as string][format-literal-as-string] + +Case matching is supported when you format some date or time parts as a string +and the output contains letters. To learn more, +see [Case matching][case-matching-date-time]. + +#### Case matching + + +When the output of some format element contains letters, the letter cases of +the output is matched with the letter cases of the format element, +meaning the words in the output are capitalized according to how the +format element is capitalized. This is called case matching. The rules are: + ++ If the first two letters of the element are both upper case, the words in + the output are capitalized. For example `DAY` = `THURSDAY`. ++ If the first letter of the element is upper case, and the second letter is + lowercase, the first letter of each word in the output is capitalized and + other letters are lowercase. For example `Day` = `Thursday`. ++ If the first letter of the element is lowercase, then all letters in the + output are lowercase. For example, `day` = `thursday`. + +#### Format year part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the year part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the year + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the year format element. + +These data types include a year part: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
YYYYYear, 4 or more digits. + Input: DATE '2018-01-30'
+ Output: 2018 +
+ Input: DATE '76-01-30'
+ Output: 0076 +
+ Input: DATE '10000-01-30'
+ Output: 10000 +
YYYYear, last 3 digits only. + Input: DATE '2018-01-30'
+ Output: 018 +
+ Input: DATE '98-01-30'
+ Output: 098 +
YYYear, last 2 digits only. + Input: DATE '2018-01-30'
+ Output: 18 +
+ Input: DATE '8-01-30'
+ Output: 08 +
YYear, last digit only. + Input: DATE '2018-01-30'
+ Output: 8 +
RRRRSame behavior as YYYY.
RRSame behavior as YY.
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(DATE '2018-01-30' AS STRING FORMAT 'YYYY') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 2018 | + *---------------------*/ +``` + +#### Format month part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the month part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the month + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the month format element. + +These data types include a month part: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
MMMonth, 2 digits. + Input: DATE '2018-01-30'
+ Output: 01 +
MON + Abbreviated, 3-character name of the month. The abbreviated month names + for locale en-US are: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, + NOV, DEC. Case matching is + supported. + + Input: DATE '2018-01-30'
+ Output: JAN +
MONTH + Name of the month. + Case matching is supported. + + Input: DATE '2018-01-30'
+ Output: JANUARY +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(DATE '2018-01-30' AS STRING FORMAT 'MONTH') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | JANUARY | + *---------------------*/ +``` + +#### Format day part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the day part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the day + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the day format element. + +These data types include a day part: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
DAY + Name of the day of the week, localized. Spaces are padded on the right + side to make the output size exactly 9. + Case matching is supported. + + Input: DATE '2020-12-31'
+ Output: THURSDAY +
DY + Abbreviated, 3-character name of the weekday, localized. + The abbreviated weekday names for locale en-US are: MON, TUE, WED, THU, + FRI, SAT, SUN. + Case matching is supported. + + Input: DATE '2020-12-31'
+ Output: THU +
DDay of the week (1 to 7), starting with Sunday as 1. + Input: DATE '2020-12-31'
+ Output: 4 +
DD2-digit day of the month. + Input: DATE '2018-12-02'
+ Output: 02 +
DDD3-digit day of the year. + Input: DATE '2018-02-03'
+ Output: 034 +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(DATE '2018-02-15' AS STRING FORMAT 'DD') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 15 | + *---------------------*/ +``` + +#### Format hour part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the hour part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the hour + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the hour format element. + +These data types include a hour part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
HHHour of the day, 12-hour clock, 2 digits. + Input: TIME '21:30:00'
+ Output: 09 +
HH12 + Hour of the day, 12-hour clock. + + Input: TIME '21:30:00'
+ Output: 09 +
HH24 + Hour of the day, 24-hour clock, 2 digits. + + Input: TIME '21:30:00'
+ Output: 21 +
+ +**Return type** + +`STRING` + +**Examples** + +```sql +SELECT CAST(TIME '21:30:00' AS STRING FORMAT 'HH24') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 21 | + *---------------------*/ +``` + +```sql +SELECT CAST(TIME '21:30:00' AS STRING FORMAT 'HH12') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 09 | + *---------------------*/ +``` + +#### Format minute part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the minute part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the minute + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the minute format element. + +These data types include a minute part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + +
Format elementReturnsExample
MIMinute, 2 digits. + Input: TIME '01:02:03'
+ Output: 02 +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(TIME '21:30:00' AS STRING FORMAT 'MI') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 30 | + *---------------------*/ +``` + +#### Format second part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the second part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the second + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the second format element. + +These data types include a second part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
SSSeconds of the minute, 2 digits. + Input: TIME '01:02:03'
+ Output: 03 +
SSSSSSeconds of the day, 5 digits. + Input: TIME '01:02:03'
+ Output: 03723 +
FFn + Fractional part of the second, n digits long. + Replace n with a value from 1 to 9. For example, FF5. + The fractional part of the second is rounded + to fit the size of the output. + + Input for FF1: TIME '01:05:07.16'
+ Output: 1 +
+ Input for FF2: TIME '01:05:07.16'
+ Output: 16 +
+ Input for FF3: TIME '01:05:07.16'
+ Output: 016 +
+ +**Return type** + +`STRING` + +**Examples** + +```sql +SELECT CAST(TIME '21:30:25.16' AS STRING FORMAT 'SS') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 25 | + *---------------------*/ +``` + +```sql +SELECT CAST(TIME '21:30:25.16' AS STRING FORMAT 'FF2') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | 16 | + *---------------------*/ +``` + +#### Format meridian indicator part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the meridian indicator part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the meridian indicator + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the meridian indicator format element. + +These data types include a meridian indicator part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
A.M. + A.M. if the time is less than 12, otherwise P.M. + The letter case of the output is determined by the first letter case + of the format element. + + Input for A.M.: TIME '01:02:03'
+ Output: A.M. +
+ Input for A.M.: TIME '16:02:03'
+ Output: P.M. +
+ Input for a.m.: TIME '01:02:03'
+ Output: a.m. +
+ Input for a.M.: TIME '01:02:03'
+ Output: a.m. +
AM + AM if the time is less than 12, otherwise PM. + The letter case of the output is determined by the first letter case + of the format element. + + Input for AM: TIME '01:02:03'
+ Output: AM +
+ Input for AM: TIME '16:02:03'
+ Output: PM +
+ Input for am: TIME '01:02:03'
+ Output: am +
+ Input for aM: TIME '01:02:03'
+ Output: am +
P.M.Output is the same as A.M. format element.
PMOutput is the same as AM format element.
+ +**Return type** + +`STRING` + +**Examples** + +```sql +SELECT CAST(TIME '21:30:00' AS STRING FORMAT 'AM') AS date_time_to_string; +SELECT CAST(TIME '21:30:00' AS STRING FORMAT 'PM') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | PM | + *---------------------*/ +``` + +```sql +SELECT CAST(TIME '01:30:00' AS STRING FORMAT 'AM') AS date_time_to_string; +SELECT CAST(TIME '01:30:00' AS STRING FORMAT 'PM') AS date_time_to_string; + +/*---------------------* + | date_time_to_string | + +---------------------+ + | AM | + *---------------------*/ +``` + +#### Format time zone part as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + +Casts a data type that contains the time zone part to a string. Includes +format elements, which provide instructions for how to conduct the cast. + ++ `expression`: This expression contains the data type with the time zone + that you need to format. ++ `format_string_expression`: A string which contains format elements, including + the time zone format element. + +These data types include a time zone part: + ++ `DATE` ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If `expression` or `format_string_expression` is `NULL` the return value is +`NULL`. If `format_string_expression` is an empty string, the output is an +empty string. An error is generated if a value that is not a supported +format element appears in `format_string_expression` or `expression` does not +contain a value specified by a format element. + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
TZH + Hour offset for a time zone. This includes the +/- sign and + 2-digit hour. + + Inputstamp: TIMESTAMP '2008-12-25 05:30:00+00' + Output: −08 +
TZM + Minute offset for a time zone. This includes only the 2-digit minute. + + Inputstamp: TIMESTAMP '2008-12-25 05:30:00+00' + Output: 00 +
+ +**Return type** + +`STRING` + +**Examples** + +```sql +SELECT CAST(TIMESTAMP '2008-12-25 00:00:00+00:00' AS STRING FORMAT 'TZH') AS date_time_to_string; + +-- Results depend upon where this query was executed. +/*---------------------* + | date_time_to_string | + +---------------------+ + | -08 | + *---------------------*/ +``` + +```sql +SELECT CAST(TIMESTAMP '2008-12-25 00:00:00+00:00' AS STRING FORMAT 'TZH' AT TIME ZONE 'Asia/Kolkata') +AS date_time_to_string; + +-- Because the time zone is specified, the result is always the same. +/*---------------------* + | date_time_to_string | + +---------------------+ + | +05 | + *---------------------*/ +``` + +```sql +SELECT CAST(TIMESTAMP '2008-12-25 00:00:00+00:00' AS STRING FORMAT 'TZM') AS date_time_to_string; + +-- Results depend upon where this query was executed. +/*---------------------* + | date_time_to_string | + +---------------------+ + | 00 | + *---------------------*/ +``` + +```sql +SELECT CAST(TIMESTAMP '2008-12-25 00:00:00+00:00' AS STRING FORMAT 'TZM' AT TIME ZONE 'Asia/Kolkata') +AS date_time_to_string; + +-- Because the time zone is specified, the result is always the same. +/*---------------------* + | date_time_to_string | + +---------------------+ + | 30 | + *---------------------*/ +``` + +#### Format literal as string + + +```sql +CAST(expression AS STRING FORMAT format_string_expression) +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
-Output is the same as the input.-
.Output is the same as the input..
/Output is the same as the input./
,Output is the same as the input.,
'Output is the same as the input.'
;Output is the same as the input.;
:Output is the same as the input.:
Whitespace + Output is the same as the input. + Whitespace means the space character, ASCII 32. It does not mean + other types of space like tab or new line. Any whitespace character + that is not the ASCII 32 character in the format model generates + an error. +
"text" + Output is the value within the double quotes. To preserve a double + quote or backslash character, use the \" or \\ + escape sequence. Other escape sequences are not supported. + + Input: "abc"
+ Output: abc +
+ Input: "a\"b\\c"
+ Output: a"b\c +
+ +### Format string as date and time + + +You can format a string with these date and time parts: + ++ [Format string as year part][format-string-as-year] ++ [Format string as month part][format-string-as-month] ++ [Format string as day part][format-string-as-day] ++ [Format string as hour part][format-string-as-hour] ++ [Format string as minute part][format-string-as-minute] ++ [Format string as second part][format-string-as-second] ++ [Format string as meridian indicator part][format-string-as-meridian] ++ [Format string as time zone part][format-string-as-tz] ++ [Format string as literal part][format-string-as-literal] + +When formatting a string with date and time parts, you must follow the +[format model rules][format-model-rules-date-time]. + +#### Format model rules + + +When casting a string to date and time parts, you must ensure the _format model_ +is valid. The format model represents the elements passed into +`CAST(string_expression AS type FORMAT format_string_expression)` as the +`format_string_expression` and is validated according to the following +rules: + ++ It contains at most one of each of the following parts: + meridian indicator, year, month, day, hour. ++ A non-literal, non-whitespace format element cannot appear more than once. ++ If it contains the day of year format element, `DDD`, then it cannot contain + the month. ++ If it contains the 24-hour format element, `HH24`, then it cannot contain the + 12-hour format element or a meridian indicator. ++ If it contains the 12-hour format element, `HH12` or `HH`, then it must also + contain a meridian indicator. ++ If it contains a meridian indicator, then it must also contain a 12-hour + format element. ++ If it contains the second of the day format element, `SSSSS`, then it cannot + contain any of the following: hour, minute, second, or meridian indicator. ++ It cannot contain a format element such that the value it sets does not exist + in the target type. For example, an hour format element such as `HH24` cannot + appear in a string you are casting as a `DATE`. + +#### Format string as year part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted year to a data type that contains +the year part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the year + that you need to format. ++ `type`: The data type to which you are casting. Must include the year + part. ++ `format_string_expression`: A string which contains format elements, including + the year format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a year part: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +If the `YEAR` part is missing from `string_expression` and the return type +includes this part, `YEAR` is set to the current year. + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
YYYY + If it is delimited, matches 1 to 5 digits. If it is not delimited, + matches 4 digits. Sets the year part to the matched number. + + Input for MM-DD-YYYY: '03-12-2018'
+ Output as DATE: 2018-12-03 +
+ Input for YYYY-MMDD: '10000-1203'
+ Output as DATE: 10000-12-03 +
+ Input for YYYY: '18'
+ Output as DATE: 2018-03-01 (Assume current date is March 23, 2021) +
YYY + Matches 3 digits. Sets the last 3 digits of the year part to the + matched number. + + Input for YYY-MM-DD: '018-12-03'
+ Output as DATE: 2018-12-03 +
+ Input for YYY-MM-DD: '038-12-03'
+ Output as DATE: 2038-12-03 +
YY + Matches 2 digits. Sets the last 2 digits of the year part to the + matched number. + + Input for YY-MM-DD: '18-12-03'
+ Output as DATE: 2018-12-03 +
+ Input for YY-MM-DD: '38-12-03'
+ Output as DATE: 2038-12-03 +
Y + Matches 1 digit. Sets the last digit of the year part to the matched + number. + + Input for Y-MM-DD: '8-12-03'
+ Output as DATE: 2008-12-03 +
Y,YYY + Matches the pattern of 1 to 2 digits, comma, then exactly 3 digits. + Sets the year part to the matched number. + + Input for Y,YYY-MM-DD: '2,018-12-03'
+ Output as DATE: 2008-12-03 +
RRRRSame behavior as YYYY.
RR +

+ Matches 2 digits. +

+

+ If the 2 digits entered are between 00 and 49 and the + last 2 digits of the current year are between 00 and 49, the + returned year has the same first 2 digits as the current year. + If the last 2 digits of the current year are between 50 and 99, + the first 2 digits of the returned year is 1 greater than the first 2 + digits of the current year. +

+

+ If the 2 digits entered are between 50 and 99 and the + last 2 digits of the current year are between 00 and 49, the first + 2 digits of the returned year are 1 less than the first 2 digits of + the current year. If the last 2 digits of the current year are + between 50 and 99, the returned year has the same first 2 digits + as the current year. +

+
+ Input for RR-MM-DD: '18-12-03'
+ Output as DATE: 2018-12-03 (executed in the year 2021) + Output as DATE: 2118-12-03 (executed in the year 2050) +
+ Input for RR-MM-DD: '50-12-03'
+ Output as DATE: 2050-12-03 (executed in the year 2021) + Output as DATE: 2050-12-03 (executed in the year 2050) +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('18-12-03' AS DATE FORMAT 'YY-MM-DD') AS string_to_date + +/*----------------* + | string_to_date | + +----------------+ + | 2018-12-03 | + *----------------*/ +``` + +#### Format string as month part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted month to a data type that contains +the month part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the month + that you need to format. ++ `type`: The data type to which you are casting. Must include the month + part. ++ `format_string_expression`: A string which contains format elements, including + the month format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a month part: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +If the `MONTH` part is missing from `string_expression` and the return type +includes this part, `MONTH` is set to the current month. + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
MM + Matches 2 digits. Sets the month part to the matched number. + + Input for MM-DD-YYYY: '03-12-2018'
+ Output as DATE: 2018-12-03 +
MON + Matches 3 letters. Sets the month part to the matched string interpreted + as the abbreviated name of the month. + + Input for MON DD, YYYY: 'DEC 03, 2018'
+ Output as DATE: 2018-12-03 +
MONTH + Matches 9 letters. Sets the month part to the matched string interpreted + as the name of the month. + + Input for MONTH DD, YYYY: 'DECEMBER 03, 2018'
+ Output as DATE: 2018-12-03 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('DEC 03, 2018' AS DATE FORMAT 'MON DD, YYYY') AS string_to_date + +/*----------------* + | string_to_date | + +----------------+ + | 2018-12-03 | + *----------------*/ +``` + +#### Format string as day part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted day to a data type that contains +the day part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the day + that you need to format. ++ `type`: The data type to which you are casting. Must include the day + part. ++ `format_string_expression`: A string which contains format elements, including + the day format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a day part: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +If the `DAY` part is missing from `string_expression` and the return type +includes this part, `DAY` is set to `1`. + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + +
Format elementReturnsExample
DDMatches 2 digits. Sets the day part to the matched number. + Input for MONTH DD, YYYY: 'DECEMBER 03, 2018'
+ Output as DATE: 2018-12-03 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `DATE` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('DECEMBER 03, 2018' AS DATE FORMAT 'MONTH DD, YYYY') AS string_to_date + +/*----------------* + | string_to_date | + +----------------+ + | 2018-12-03 | + *----------------*/ +``` + +#### Format string as hour part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted hour to a data type that contains +the hour part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the hour + that you need to format. ++ `type`: The data type to which you are casting. Must include the hour + part. ++ `format_string_expression`: A string which contains format elements, including + the hour format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a hour part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If the `HOUR` part is missing from `string_expression` and the return type +includes this part, `HOUR` is set to `0`. + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
HH + Matches 2 digits. If the matched number n is 12, + sets temp = 0; otherwise, sets temp = n. If + the matched value of the A.M./P.M. format element is P.M., sets + temp = n + 12. Sets the hour part to temp. + A meridian indicator must be present in the format model, when + HH is present. + + Input for HH:MI P.M.: '03:30 P.M.'
+ Output as TIME: 15:30:00 +
HH12 + Same behavior as HH. +
HH24 + Matches 2 digits. Sets the hour part to the matched number. + + Input for HH24:MI: '15:30'
+ Output as TIME: 15:30:00 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('15:30' AS TIME FORMAT 'HH24:MI') AS string_to_date_time + +/*---------------------* + | string_to_date_time | + +---------------------+ + | 15:30:00 | + *---------------------*/ +``` + +#### Format string as minute part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted minute to a data type that contains +the minute part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the minute + that you need to format. ++ `type`: The data type to which you are casting. Must include the minute + part. ++ `format_string_expression`: A string which contains format elements, including + the minute format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a minute part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If the `MINUTE` part is missing from `string_expression` and the return type +includes this part, `MINUTE` is set to `0`. + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + +
Format elementReturnsExample
MI + Matches 2 digits. Sets the minute part to the matched number. + + Input for HH:MI P.M.: '03:30 P.M.'
+ Output as TIME: 15:30:00 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('03:30 P.M.' AS TIME FORMAT 'HH:MI P.M.') AS string_to_date_time + +/*---------------------* + | string_to_date_time | + +---------------------+ + | 15:30:00 | + *---------------------*/ +``` + +#### Format string as second part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted second to a data type that contains +the second part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the second + that you need to format. ++ `type`: The data type to which you are casting. Must include the second + part. ++ `format_string_expression`: A string which contains format elements, including + the second format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a second part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +If the `SECOND` part is missing from `string_expression` and the return type +includes this part, `SECOND` is set to `0`. + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
SS + Matches 2 digits. Sets the second part to the matched number. + + Input for HH:MI:SS P.M.: '03:30:02 P.M.'
+ Output as TIME: 15:30:02 +
SSSSS + Matches 5 digits. Sets the hour, minute and second parts by interpreting + the matched number as the number of seconds past midnight. + + Input for SSSSS: '03723'
+ Output as TIME: 01:02:03 +
FFn + Matches n digits, where n is the number + following FF in the format element. Sets the fractional part of the + second part to the matched number. + + Input for HH24:MI:SS.FF1: '01:05:07.16'
+ Output as TIME: 01:05:07.2 +
+ Input for HH24:MI:SS.FF2: '01:05:07.16'
+ Output as TIME: 01:05:07.16 +
+ Input for HH24:MI:SS.FF3: 'FF3: 01:05:07.16'
+ Output as TIME: 01:05:07.160 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('01:05:07.16' AS TIME FORMAT 'HH24:MI:SS.FF1') AS string_to_date_time + +/*---------------------* + | string_to_date_time | + +---------------------+ + | 01:05:07.2 | + *---------------------*/ +``` + +#### Format string as meridian indicator part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted meridian indicator to a data type that contains +the meridian indicator part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the meridian indicator + that you need to format. ++ `type`: The data type to which you are casting. Must include the meridian indicator + part. ++ `format_string_expression`: A string which contains format elements, including + the meridian indicator format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a meridian indicator part: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + +
Format elementReturnsExample
A.M. or P.M. + Matches using the regular expression '(A|P)\.M\.'. + + Input for HH:MI A.M.: '03:30 A.M.'
+ Output as TIME: 03:30:00 +
+ Input for HH:MI P.M.: '03:30 P.M.'
+ Output as TIME: 15:30:00 +
+ Input for HH:MI P.M.: '03:30 A.M.'
+ Output as TIME: 03:30:00 +
+ Input for HH:MI A.M.: '03:30 P.M.'
+ Output as TIME: 15:30:00 +
+ Input for HH:MI a.m.: '03:30 a.m.'
+ Output as TIME: 03:30:00 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('03:30 P.M.' AS TIME FORMAT 'HH:MI A.M.') AS string_to_date_time + +/*---------------------* + | string_to_date_time | + +---------------------+ + | 15:30:00 | + *---------------------*/ +``` + +#### Format string as time zone part + + +```sql +CAST(string_expression AS type FORMAT format_string_expression) +``` + +Casts a string-formatted time zone to a data type that contains +the time zone part. Includes format elements, which provide instructions for how +to conduct the cast. + ++ `string_expression`: This expression contains the string with the time zone + that you need to format. ++ `type`: The data type to which you are casting. Must include the time zone + part. ++ `format_string_expression`: A string which contains format elements, including + the time zone format element. The formats elements in this string are + defined collectively as the format model, which must follow + [these rules][format-model-rules-date-time]. + +These data types include a time zone part: + ++ `DATE` ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +An error is generated if a value that is not a supported format element appears +in `format_string_expression` or `string_expression` does not contain a value +specified by a format element. + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
TZH + Matches using the regular expression '(\+|\-| )[0-9]{2}'. + Sets the time zone and hour parts to the matched sign and number. + Sets the time zone sign to be the first letter of the matched string. + The number 2 means matching up to 2 digits for non-exact matching, and + exactly 2 digits for exact matching. + + Input for YYYY-MM-DD HH:MI:SSTZH: '2008-12-25 05:30:00-08'
+ Output as TIMESTAMP: 2008-12-25 05:30:00-08 +
TZM + Matches 2 digits. Let n be the matched number. If the + time zone sign is the minus sign, sets the time zone minute part to + -n. Otherwise, sets the time zone minute part to + n. + + Input for YYYY-MM-DD HH:MI:SSTZH: '2008-12-25 05:30:00+05.30'
+ Output as TIMESTAMP: 2008-12-25 05:30:00+05.30 +
+ +**Return type** + +The data type to which the string was cast. This can be: + ++ `DATE` ++ `TIME` ++ `DATETIME` ++ `TIMESTAMP` + +**Examples** + +```sql +SELECT CAST('2020.06.03 00:00:53+00' AS TIMESTAMP FORMAT 'YYYY.MM.DD HH:MI:SSTZH') AS string_to_date_time + +/*----------------------------* + | as_timestamp | + +----------------------------+ + | 2020-06-03 00:00:53.110+00 | + *----------------------------*/ +``` + +#### Format string as literal + + +```sql +CAST(string_expression AS data_type FORMAT format_string_expression) +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
-Output is the same as the input.
.Output is the same as the input..
/Output is the same as the input./
,Output is the same as the input.,
'Output is the same as the input.'
;Output is the same as the input.;
:Output is the same as the input.:
Whitespace + A consecutive sequence of one or more spaces in the format model + is matched with one or more consecutive Unicode whitespace characters + in the input. Space means the ASCII 32 space character. + It does not mean the general whitespace such as a tab or new line. + Any whitespace character that is not the ASCII 32 character in the + format model generates an error. +
"text" + Output generated by the format element in formatting, using this + regular expression, with s representing the string input: + regex.escape(s). + + Input: "abc"
+ Output: abc +
+ Input: "a\"b\\c"
+ Output: a"b\c +
+ +### Format numeric type as string + + +```sql +CAST(numeric_expression AS STRING FORMAT format_string_expression) +``` + +You can cast a [numeric type][numeric-types] to a string by combining the +following format elements: + +- [Digits][format-digits] +- [Decimal point][format-decimal-point] +- [Sign][format-sign] +- [Currency symbol][format-currency-symbol] +- [Group separator][format-group-separator] +- [Other format elements][format-other-elements] + +Except for the exponent format element (`EEEE`), all of the format elements +generate a fixed number of characters in the output, and the output is aligned +by the decimal point. The first character outputs a `-` for negative numbers; +otherwise a space. To suppress blank characters and trailing zeroes, use the +`FM` flag. + +**Return type** + +`STRING` + +**Example** + +```sql +SELECT input, CAST(input AS STRING FORMAT '$999,999.999') AS output +FROM UNNEST([1.2, 12.3, 123.456, 1234.56, -12345.678, 1234567.89]) AS input + +/*------------+---------------* + | input | output | + +------------+---------------+ + | 1.2 | $1.200 | + | 12.3 | $12.300 | + | 123.456 | $123.456 | + | 1234.56 | $1,234.560 | + | -12345.678 | -$12,345.678 | + | 1234567.89 | $###,###.### | + *------------+---------------*/ +``` + +#### Format digits as string + + +The following format elements output digits. If there aren't enough +digit format elements to represent the input, all digit format elements are +replaced with `#` in the output. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
0A decimal digit. Leading and trailing zeros are included. + Input: 12
+ Format: '000'
+ Output: ' 012' +
+ Input: 12
+ Format: '000.000'
+ Output: ' 012.000' +
+ Input: -12
+ Format: '000.000'
+ Output: '-012.000' +
9A decimal digit. Leading zeros are replaced with spaces. Trailing + zeros are included. + Input: 12
+ Format: '999'
+ Output: '  12' +
+ Input: 12
+ Format: '999.999'
+ Output: '  12.000' +
X or x

A hexadecimal digit. Cannot appear with other format elements + except 0, FM, and the sign format elements. The maximum number of + hexadecimal digits in the format string is 16.

+

X generates uppercase letters and x generates lowercase letters. +

+

When 0 is combined with the hexadecimal format element, the letter + generated by 0 matches the case of the next X or x element. If + there is no subsequent X or x, then 0 generates an uppercase + letter.

+
+ Input: 43981
+ Format: 'XXXX'
+ Output: ' ABCD' +
+ Input: 43981
+ Format: 'xxxx'
+ Output: ' abcd' +
+ Input: 43981
+ Format: '0X0x'
+ Output: ' ABcd' +
+ Input: 43981
+ Format: '0000000X'
+ Output: ' 0000ABCD' +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT + CAST(12 AS STRING FORMAT '999') as a, + CAST(-12 AS STRING FORMAT '999') as b; + +/*------+------* + | a | b | + +------+------+ + | 12 | -12 | + *------+------*/ +``` + +#### Format decimal point as string + + +The following format elements output a decimal point. These format elements are +mutually exclusive. At most one can appear in the format string. + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
. (period)Decimal point. + Input: 123.58
+ Format: '999.999'
+ Output: ' 123.580' +
DThe decimal point of the current locale. + Input: 123.58
+ Format: '999D999'
+ Output: ' 123.580' +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(12.5 AS STRING FORMAT '99.99') as a; + +/*--------* + | a | + +--------+ + | 12.50 | + *--------*/ +``` + +#### Format sign as string + + +The following format elements output the sign (+/-). These format elements are +mutually exclusive. At most one can appear in the format string. + +If there are no sign format elements, one extra space is reserved for the sign. +For example, if the input is 12 and the format string is +'99', then the output is ' 12', with a length of three +characters. + +The sign appears before the number. If the format model includes a currency +symbol element, then the sign appears before the currency symbol. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
SExplicit sign. Outputs + for positive numbers and + - for negative numbers. The position in the output is + anchored to the number. NaN and 0 + will not be signed. + Input: -12
+ Format: 'S9999'
+ Output: '  -12' +
+ Input: -12
+ Format: '9999S'
+ Output: '  12-' +
MIExplicit sign. Outputs a space for positive numbers and - + for negative numbers. This element can only appear in the last position. + + Input: 12
+ Format: '9999MI'
+ Output: '  12 ' +
+ Input: -12
+ Format: '9999MI'
+ Output: '  12-' +
PRFor negative numbers, the value is enclosed in angle brackets. For + positive numbers, the value is returned with a leading and trailing + space. This element can only appear in the last position. + + Input: 12
+ Format: '9999PR'
+ Output: '   12 ' +
+ Input: -12
+ Format: '9999PR'
+ Output: '  <12>' +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT + CAST(12 AS STRING FORMAT 'S99') as a, + CAST(-12 AS STRING FORMAT 'S99') as b; + +/*-----+-----* + | a | b | + +-----+-----+ + | +12 | -12 | + *-----+-----*/ +``` + +#### Format currency symbol as string + + +The following format elements output a currency symbol. These format elements +are mutually exclusive. At most one can appear in the format string. In the +output, the currency symbol appears before the first digit or decimal point. + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
$Dollar sign ($). + Input: -12
+ Format: '$999'
+ Output: ' -$12' +
C or cThe ISO-4217 currency code of the current locale. + Input: -12
+ Format: 'C999'
+ Output: ' -USD12' +
+ Input: -12
+ Format: 'c999'
+ Output: ' -usd12' +
LThe currency symbol of the current locale. + Input: -12
+ Format: 'L999'
+ Output: ' -$12' +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT + CAST(12 AS STRING FORMAT '$99') as a, + CAST(-12 AS STRING FORMAT '$99') as b; + +/*------+------* + | a | b | + +------+------+ + | $12 | -$12 | + *------+------*/ +``` + +#### Format group separator as string + + +The following format elements output a group separator. + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
, (comma)Group separator. + Input: 12345
+ Format: '999,999'
+ Output: '  12,345' +
GThe group separator point of the current locale. + Input: 12345
+ Format: '999G999'
+ Output: '  12,345' +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(1234 AS STRING FORMAT '999,999') as a; + +/*----------* + | a | + +----------+ + | 1,234 | + *----------*/ +``` + +#### Other numeric format elements + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Format elementReturnsExample
BOutputs spaces when the integer part is zero. If the integer part of + the number is 0, then the following format elements generate spaces in + the output: digits (9, X, 0), decimal point, group separator, currency, + sign, and exponent. + Input: 0.23
+ Format: 'B99.999S'
+ Output: '       ' +
+ Input: 1.23
+ Format: 'B99.999S'
+ Output: ' 1.230+' +
EEEEOutputs the exponent part of the value in scientific notation. If + the exponent value is between -99 and 99, the output is four characters. + Otherwise, the minimum number of digits is used in the output. + + Input: 20
+ Format: '9.99EEEE'
+ Output: ' 2.0E+01' +
+ Input: 299792458
+ Format: 'S9.999EEEE'
+ Output: '+2.998E+08' +
FMRemoves all spaces and trailing zeroes from the output. You can use + this element to suppress spaces and trailing zeroes that are generated + by other format elements. + Input: 12.5
+ Format: '999999.000FM'
+ Output: '12.5' +
RNReturns the value as Roman numerals, rounded to the nearest integer. + The input must be between 1 and 3999. The output is padded with spaces + to the left to a length of 15. This element cannot be used with other + format elements except FM. + + Input: 2021
+ Format: 'RN'
+ Output: '          MMXXI' +
VThe input value is multiplied by 10^n, where n is the number of 9s + after the V. This element cannot be used with a decimal + point or exponent format element. + + Input: 23.5
+ Format: 'S000V00'
+ Output: '+02350' +
+ +**Return type** + +`STRING` + +**Example** + +```sql +SELECT CAST(-123456 AS STRING FORMAT '9.999EEEE') as a;" + +/*------------* + | a | + +------------+ + | -1.235E+05 | + *------------*/ +``` + +### About BASE encoding + + +BASE encoding translates binary data in string format into a radix-X +representation. + +If X is 2, 8, or 16, Arabic numerals 0–9 and the Latin letters +a–z are used in the encoded string. So for example, BASE16/Hexadecimal encoding +results contain 0~9 and a~f. + +If X is 32 or 64, the default character tables are defined in +[rfc 4648][rfc-4648]. When you decode a BASE string where X is 2, 8, or 16, +the Latin letters in the input string are case-insensitive. For example, both +"3a" and "3A" are valid input strings for BASE16/Hexadecimal decoding, and +will output the same result. + +[format-date]: https://github.com/google/zetasql/blob/master/docs/date_functions.md#format_date + +[parse-date]: https://github.com/google/zetasql/blob/master/docs/date_functions.md#parse_date + +[format-time]: https://github.com/google/zetasql/blob/master/docs/time_functions.md#format_time + +[parse-time]: https://github.com/google/zetasql/blob/master/docs/time_functions.md#parse_time + +[format-datetime]: https://github.com/google/zetasql/blob/master/docs/datetime_functions.md#format_datetime + +[parse-datetime]: https://github.com/google/zetasql/blob/master/docs/datetime_functions.md#parse_datetime + +[format-timestamp]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#format_timestamp + +[parse-timestamp]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#parse_timestamp + +[rfc-4648]: https://tools.ietf.org/html/rfc4648#section-3.3 + +[case-matching-date-time]: #case_matching_date_time + +[format-year-as-string]: #format_year_as_string + +[format-month-as-string]: #format_month_as_string + +[format-day-as-string]: #format_day_as_string + +[format-hour-as-string]: #format_hour_as_string + +[format-minute-as-string]: #format_minute_as_string + +[format-second-as-string]: #format_second_as_string + +[format-meridian-as-string]: #format_meridian_as_string + +[format-tz-as-string]: #format_tz_as_string + +[format-literal-as-string]: #format_literal_as_string + +[format-model-rules-date-time]: #format_model_rules_date_time + +[format-string-as-year]: #format_string_as_year + +[format-string-as-month]: #format_string_as_month + +[format-string-as-day]: #format_string_as_day + +[format-string-as-hour]: #format_string_as_hour + +[format-string-as-minute]: #format_string_as_minute + +[format-string-as-second]: #format_string_as_second + +[format-string-as-meridian]: #format_string_as_meridian + +[format-string-as-tz]: #format_string_as_tz + +[format-string-as-literal]: #format_string_as_literal + +[format-digits]: #format_digits + +[format-decimal-point]: #format_decimal_point + +[format-sign]: #format_sign + +[format-currency-symbol]: #format_currency_symbol + +[format-group-separator]: #format_group_separator + +[format-other-elements]: #format_other_elements + +[numeric-types]: https://github.com/google/zetasql/blob/master/docs/data-types.md#numeric_types + +[cast-functions]: https://github.com/google/zetasql/blob/master/docs/conversion_functions.md#cast + diff --git a/docs/functions-and-operators.md b/docs/functions-and-operators.md index 16567d0ca..e146ace7d 100644 --- a/docs/functions-and-operators.md +++ b/docs/functions-and-operators.md @@ -570,18 +570,14 @@ a field by position is useful when fields are un-named or have ambiguous names. **Example** -In the following example, the expression is `t.customer` and the -field access operations are `.address` and `.country`. An operation is an -application of an operator (`.`) to specific operands (in this case, -`address` and `country`, or more specifically, `t.customer` and `address`, -for the first operation, and `t.customer.address` and `country` for the -second operation). +In the following example, the field access operations are `.address` and +`.country`. ```sql -WITH orders AS ( - SELECT STRUCT(STRUCT('Yonge Street' AS street, 'Canada' AS country) AS address) AS customer -) -SELECT t.customer.address.country FROM orders AS t; +SELECT + STRUCT( + STRUCT('Yonge Street' AS street, 'Canada' AS country) + AS address).address.country /*---------* | country | @@ -595,8 +591,10 @@ SELECT t.customer.address.country FROM orders AS t; ### Array subscript operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` -array_expression[array_subscript_specifier] +array_expression "[" array_subscript_specifier "]" array_subscript_specifier: { index | position_keyword(index) } @@ -605,9 +603,6 @@ position_keyword: { OFFSET | SAFE_OFFSET | ORDINAL | SAFE_ORDINAL } ``` -Note: The brackets (`[]`) around `array_subscript_specifier` are part of the -syntax; they do not represent an optional part. - **Description** Gets a value from an array at a specific position. @@ -646,14 +641,12 @@ reference an index (`6`) in an array that is out of range. If the `SAFE` prefix is included, `NULL` is returned, otherwise an error is produced. ```sql -WITH Items AS (SELECT ["coffee", "tea", "milk"] AS item_array) SELECT - item_array, - item_array[0] AS item_index, - item_array[OFFSET(0)] AS item_offset, - item_array[ORDINAL(1)] AS item_ordinal, - item_array[SAFE_OFFSET(6)] AS item_safe_offset -FROM Items + ["coffee", "tea", "milk"] AS item_array, + ["coffee", "tea", "milk"][0] AS item_index, + ["coffee", "tea", "milk"][OFFSET(0)] AS item_offset, + ["coffee", "tea", "milk"][ORDINAL(1)] AS item_ordinal, + ["coffee", "tea", "milk"][SAFE_OFFSET(6)] AS item_safe_offset /*---------------------+------------+-------------+--------------+------------------* | item_array | item_index | item_offset | item_ordinal | item_safe_offset | @@ -667,28 +660,22 @@ keyword that begins with `SAFE` is not included, an error is produced. For example: ```sql -WITH Items AS (SELECT ["coffee", "tea", "milk"] AS item_array) -SELECT - item_array[6] AS item_offset -FROM Items - -- Error. Array index 6 is out of bounds. +SELECT ["coffee", "tea", "milk"][6] AS item_offset ``` ```sql -WITH Items AS (SELECT ["coffee", "tea", "milk"] AS item_array) -SELECT - item_array[OFFSET(6)] AS item_offset -FROM Items - -- Error. Array index 6 is out of bounds. +SELECT ["coffee", "tea", "milk"][OFFSET(6)] AS item_offset ``` ### Struct subscript operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` -struct_expression[struct_subscript_specifier] +struct_expression "[" struct_subscript_specifier "]" struct_subscript_specifier: { index | position_keyword(index) } @@ -697,9 +684,6 @@ position_keyword: { OFFSET | ORDINAL } ``` -Note: The brackets (`[]`) around `struct_subscript_specifier` are part of the -syntax; they do not represent an optional part. - **Description** Gets the value of a field at a selected position in a struct. @@ -730,12 +714,10 @@ shows what happens when you reference an index (`6`) in an struct that is out of range. ```sql -WITH Items AS (SELECT STRUCT(23, "tea", FALSE) AS item_struct) SELECT - item_struct[0] AS field_index, - item_struct[OFFSET(0)] AS field_offset, - item_struct[ORDINAL(1)] AS field_ordinal -FROM Items + STRUCT(23, "tea", FALSE)[0] AS field_index, + STRUCT(23, "tea", FALSE)[OFFSET(0)] AS field_offset, + STRUCT(23, "tea", FALSE)[ORDINAL(1)] AS field_ordinal /*-------------+--------------+---------------* | field_index | field_offset | field_ordinal | @@ -748,37 +730,28 @@ When you reference an index that is out of range in a struct, an error is produced. For example: ```sql -WITH Items AS (SELECT STRUCT(23, "tea", FALSE) AS item_struct) -SELECT - item_struct[6] AS field_offset -FROM Items - --- Error. Field ordinal 6 is out of bounds in STRUCT +-- Error: Field ordinal 6 is out of bounds in STRUCT +SELECT STRUCT(23, "tea", FALSE)[6] AS field_offset ``` ```sql -WITH Items AS (SELECT STRUCT(23, "tea", FALSE) AS item_struct) -SELECT - item_struct[OFFSET(6)] AS field_offset -FROM Items - --- Error. Field ordinal 6 is out of bounds in STRUCT +-- Error: Field ordinal 6 is out of bounds in STRUCT +SELECT STRUCT(23, "tea", FALSE)[OFFSET(6)] AS field_offset ``` ### JSON subscript operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` -json_expression[array_element_id] +json_expression "[" array_element_id "]" ``` ``` -json_expression[field_name] +json_expression "[" field_name "]" ``` -Note: The brackets (`[]`) around `array_element_id` and `field_name` are part -of the syntax; they do not represent an optional part. - **Description** Gets a value of an array element or field in a JSON expression. Can be @@ -959,6 +932,8 @@ FROM ### Array elements field access operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` array_expression.field_or_element[. ...] @@ -966,12 +941,9 @@ field_or_element: { fieldname | array_element } array_element: - array_fieldname[array_subscript_specifier] + array_fieldname "[" array_subscript_specifier "]" ``` -Note: The brackets (`[]`) around `array_subscript_specifier` are part of the -syntax; they do not represent an optional part. - **Description** The array elements field access operation lets you traverse through the @@ -3534,26 +3506,31 @@ must be coercible to a common [supertype][cond-exp-supertype]. [Supertype][cond-exp-supertype] of `true_result` and `else_result`. -**Example** +**Examples** ```sql -WITH Numbers AS ( - SELECT 10 as A, 20 as B UNION ALL - SELECT 50, 30 UNION ALL - SELECT 60, 60 -) SELECT - A, - B, - IF(A < B, 'true', 'false') AS result -FROM Numbers + 10 AS A, + 20 AS B, + IF(10 < 20, 'true', 'false') AS result /*------------------* | A | B | result | +------------------+ | 10 | 20 | true | - | 50 | 30 | false | - | 60 | 60 | false | + *------------------*/ +``` + +```sql +SELECT + 30 AS A, + 20 AS B, + IF(30 < 20, 'true', 'false') AS result + +/*------------------* + | A | B | result | + +------------------+ + | 30 | 20 | false | *------------------*/ ``` @@ -3709,6 +3686,858 @@ SELECT ZEROIFNULL(NULL) AS result --- ## FUNCTIONS +## AEAD encryption functions + +ZetaSQL supports the following AEAD encryption functions. +For a description of how the AEAD encryption +functions work, see [AEAD encryption concepts][aead-encryption-concepts]. + +### Function list + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSummary
AEAD.DECRYPT_BYTES + + + Uses the matching key from a keyset to decrypt a + BYTES ciphertext. +
AEAD.DECRYPT_STRING + + + Uses the matching key from a keyset to decrypt a BYTES + ciphertext into a STRING plaintext. +
AEAD.ENCRYPT + + + Encrypts STRING plaintext, using the primary cryptographic key + in a keyset. +
DETERMINISTIC_DECRYPT_BYTES + + + Uses the matching key from a keyset to decrypt a BYTES + ciphertext, using deterministic AEAD. +
DETERMINISTIC_DECRYPT_STRING + + + Uses the matching key from a keyset to decrypt a BYTES + ciphertext into a STRING plaintext, using deterministic AEAD. +
DETERMINISTIC_ENCRYPT + + + Encrypts STRING plaintext, using the primary cryptographic key + in a keyset, using deterministic AEAD encryption. +
KEYS.ADD_KEY_FROM_RAW_BYTES + + + Adds a key to a keyset, and return the new keyset as a serialized + BYTES value. +
KEYS.KEYSET_FROM_JSON + + + Converts a STRING JSON keyset to a serialized + BYTES value. +
KEYS.KEYSET_LENGTH + + + Gets the number of keys in the provided keyset. +
KEYS.KEYSET_TO_JSON + + + Gets a JSON STRING representation of a keyset. +
KEYS.NEW_KEYSET + + + Gets a serialized keyset containing a new key based on the key type. +
KEYS.ROTATE_KEYSET + + + Adds a new primary cryptographic key to a keyset, based on the key type. +
+ +### `AEAD.DECRYPT_BYTES` + +```sql +AEAD.DECRYPT_BYTES(keyset, ciphertext, additional_data) +``` + +**Description** + +Uses the matching key from `keyset` to decrypt `ciphertext` and verifies the +integrity of the data using `additional_data`. Returns an error if decryption or +verification fails. + +`keyset` is a serialized `BYTES` value returned by one of the +`KEYS` functions. `keyset` must contain the key that was used to +encrypt `ciphertext`, and the key must be in an `'ENABLED'` state, or else the +function returns an error. `AEAD.DECRYPT_BYTES` identifies the matching key +in `keyset` by finding the key with the key ID that matches the one encrypted in +`ciphertext`. + +`ciphertext` is a `BYTES` value that is the result of +a call to `AEAD.ENCRYPT` where the input `plaintext` was of type +`BYTES`. + +If `ciphertext` includes an initialization vector (IV), +it should be the first bytes of `ciphertext`. If `ciphertext` includes an +authentication tag, it should be the last bytes of `ciphertext`. If the +IV and authentic tag are one (SIV), it should be the first bytes of +`ciphertext`. The IV and authentication tag commonly require 16 bytes, but may +vary in size. + +`additional_data` is a `STRING` or `BYTES` value that binds the ciphertext to +its context. This forces the ciphertext to be decrypted in the same context in +which it was encrypted. This function casts any +`STRING` value to `BYTES`. +This must be the same as the `additional_data` provided to `AEAD.ENCRYPT` to +encrypt `ciphertext`, ignoring its type, or else the function returns an error. + +**Return Data Type** + +`BYTES` + +**Example** + +This example creates a table of unique IDs with associated plaintext values and +keysets. Then it uses these keysets to encrypt the plaintext values as +`BYTES` and store them in a new table. Finally, it +uses `AEAD.DECRYPT_BYTES` to decrypt the encrypted values and display them as +plaintext. + +The following statement creates a table `CustomerKeysets` containing a column of +unique IDs, a column of `AEAD_AES_GCM_256` keysets, and a column of favorite +animals. + +```sql +CREATE TABLE aead.CustomerKeysets AS +SELECT + 1 AS customer_id, + KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset, + b'jaguar' AS favorite_animal +UNION ALL +SELECT + 2 AS customer_id, + KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset, + b'zebra' AS favorite_animal +UNION ALL +SELECT + 3 AS customer_id, + KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset, + b'nautilus' AS favorite_animal; +``` + +The following statement creates a table `EncryptedCustomerData` containing a +column of unique IDs and a column of ciphertext. The statement encrypts the +plaintext `favorite_animal` using the keyset value from `CustomerKeysets` +corresponding to each unique ID. + +```sql +CREATE TABLE aead.EncryptedCustomerData AS +SELECT + customer_id, + AEAD.ENCRYPT(keyset, favorite_animal, CAST(CAST(customer_id AS STRING) AS BYTES)) + AS encrypted_animal +FROM + aead.CustomerKeysets AS ck; +``` + +The following query uses the keysets in the `CustomerKeysets` table to decrypt +data in the `EncryptedCustomerData` table. + +```sql +SELECT + ecd.customer_id, + AEAD.DECRYPT_BYTES( + (SELECT ck.keyset + FROM aead.CustomerKeysets AS ck + WHERE ecd.customer_id = ck.customer_id), + ecd.encrypted_animal, + CAST(CAST(customer_id AS STRING) AS BYTES) + ) AS favorite_animal +FROM aead.EncryptedCustomerData AS ecd; +``` + +### `AEAD.DECRYPT_STRING` + +```sql +AEAD.DECRYPT_STRING(keyset, ciphertext, additional_data) +``` + +**Description** + +Like [`AEAD.DECRYPT_BYTES`][aeaddecrypt-bytes], but where `additional_data` is +of type `STRING`. + +**Return Data Type** + +`STRING` + +[aeaddecrypt-bytes]: #aeaddecrypt_bytes + +### `AEAD.ENCRYPT` + +```sql +AEAD.ENCRYPT(keyset, plaintext, additional_data) +``` + +**Description** + +Encrypts `plaintext` using the primary cryptographic key in `keyset`. The +algorithm of the primary key must be `AEAD_AES_GCM_256`. Binds the ciphertext to +the context defined by `additional_data`. Returns `NULL` if any input is `NULL`. + +`keyset` is a serialized `BYTES` value returned by one of the +`KEYS` functions. + +`plaintext` is the `STRING` or +`BYTES` value to be encrypted. + +`additional_data` is a `STRING` or `BYTES` value that binds the ciphertext to +its context. This forces the ciphertext to be decrypted in the same context in +which it was encrypted. `plaintext` and `additional_data` must be of the same +type. `AEAD.ENCRYPT(keyset, string1, string2)` is equivalent to +`AEAD.ENCRYPT(keyset, CAST(string1 AS BYTES), CAST(string2 AS BYTES))`. + +The output is ciphertext `BYTES`. The ciphertext contains a +[Tink-specific][tink] prefix indicating the key used to perform the encryption. + +**Return Data Type** + +`BYTES` + +**Example** + +The following query uses the keysets for each `customer_id` in the +`CustomerKeysets` table to encrypt the value of the plaintext `favorite_animal` +in the `PlaintextCustomerData` table corresponding to that `customer_id`. The +output contains a column of `customer_id` values and a column of +corresponding ciphertext output as `BYTES`. + +```sql +WITH CustomerKeysets AS ( + SELECT 1 AS customer_id, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset UNION ALL + SELECT 2, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') UNION ALL + SELECT 3, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') +), PlaintextCustomerData AS ( + SELECT 1 AS customer_id, 'elephant' AS favorite_animal UNION ALL + SELECT 2, 'walrus' UNION ALL + SELECT 3, 'leopard' +) +SELECT + pcd.customer_id, + AEAD.ENCRYPT( + (SELECT keyset + FROM CustomerKeysets AS ck + WHERE ck.customer_id = pcd.customer_id), + pcd.favorite_animal, + CAST(pcd.customer_id AS STRING) + ) AS encrypted_animal +FROM PlaintextCustomerData AS pcd; +``` + +[tink]: https://github.com/google/tink/blob/master/docs/KEY-MANAGEMENT.md + +### `DETERMINISTIC_DECRYPT_BYTES` + +```sql +DETERMINISTIC_DECRYPT_BYTES(keyset, ciphertext, additional_data) +``` + +**Description** + +Uses the matching key from `keyset` to decrypt `ciphertext` and verifies the +integrity of the data using `additional_data`. Returns an error if decryption +fails. + +`keyset` is a serialized `BYTES` value returned by one of the `KEYS` functions. `keyset` must contain +the key that was used to encrypt `ciphertext`, the key must be in an `'ENABLED'` +state, and the key must be of type `DETERMINISTIC_AEAD_AES_SIV_CMAC_256`, or +else the function returns an error. `DETERMINISTIC_DECRYPT_BYTES` identifies the +matching key in `keyset` by finding the key with the key ID that matches the one +encrypted in `ciphertext`. + +`ciphertext` is a `BYTES` value that is the result of a call to +`DETERMINISTIC_ENCRYPT` where the input `plaintext` was of type `BYTES`. + +The ciphertext must follow Tink's [wire format][tink-wire-format]. The first +byte of `ciphertext` should contain a Tink key version followed by a 4 byte key +hint. If `ciphertext` includes an initialization vector (IV), it should be the +next bytes of `ciphertext`. If `ciphertext` includes an authentication tag, it +should be the last bytes of `ciphertext`. If the IV and authentic tag are one +(SIV), it should be the first bytes of `ciphertext`. The IV and authentication +tag commonly require 16 bytes, but may vary in size. + +`additional_data` is a `STRING` or `BYTES` value that binds the ciphertext to +its context. This forces the ciphertext to be decrypted in the same context in +which it was encrypted. This function casts any `STRING` value to `BYTES`. This +must be the same as the `additional_data` provided to `DETERMINISTIC_ENCRYPT` to +encrypt `ciphertext`, ignoring its type, or else the function returns an error. + +**Return Data Type** + +`BYTES` + +**Example** + +This example creates a table of unique IDs with associated plaintext values and +keysets. Then it uses these keysets to encrypt the plaintext values as `BYTES` +and store them in a new table. Finally, it uses `DETERMINISTIC_DECRYPT_BYTES` to +decrypt the encrypted values and display them as plaintext. + +The following statement creates a table `CustomerKeysets` containing a column of +unique IDs, a column of `DETERMINISTIC_AEAD_AES_SIV_CMAC_256` keysets, and a +column of favorite animals. + +```sql +CREATE TABLE deterministic.CustomerKeysets AS +SELECT + 1 AS customer_id, + KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') AS keyset, + b'jaguar' AS favorite_animal +UNION ALL +SELECT + 2 AS customer_id, + KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') AS keyset, + b'zebra' AS favorite_animal +UNION ALL +SELECT + 3 AS customer_id, + KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') AS keyset, + b'nautilus' AS favorite_animal; +``` + +The following statement creates a table `EncryptedCustomerData` containing a +column of unique IDs and a column of ciphertext. The statement encrypts the +plaintext `favorite_animal` using the keyset value from `CustomerKeysets` +corresponding to each unique ID. + +```sql +CREATE TABLE deterministic.EncryptedCustomerData AS +SELECT + customer_id, + DETERMINISTIC_ENCRYPT(ck.keyset, favorite_animal, CAST(CAST(customer_id AS STRING) AS BYTES)) + AS encrypted_animal +FROM + deterministic.CustomerKeysets AS ck; +``` + +The following query uses the keysets in the `CustomerKeysets` table to decrypt +data in the `EncryptedCustomerData` table. + +```sql +SELECT + ecd.customer_id, + DETERMINISTIC_DECRYPT_BYTES( + (SELECT ck.keyset + FROM deterministic.CustomerKeysets AS ck + WHERE ecd.customer_id = ck.customer_id), + ecd.encrypted_animal, + CAST(CAST(ecd.customer_id AS STRING) AS BYTES) + ) AS favorite_animal +FROM deterministic.EncryptedCustomerData AS ecd; +``` + +[tink-wire-format]: https://developers.google.com/tink/wire-format#deterministic_aead + +### `DETERMINISTIC_DECRYPT_STRING` + +```sql +DETERMINISTIC_DECRYPT_STRING(keyset, ciphertext, additional_data) +``` + +**Description** + +Like [`DETERMINISTIC_DECRYPT_BYTES`][deterministic-decrypt-bytes], but where +`plaintext` is of type `STRING`. + +**Return Data Type** + +`STRING` + +[deterministic-decrypt-bytes]: #deterministic_decrypt_bytes + +### `DETERMINISTIC_ENCRYPT` + +```sql +DETERMINISTIC_ENCRYPT(keyset, plaintext, additional_data) +``` + +**Description** + +Encrypts `plaintext` using the primary cryptographic key in `keyset` using +[deterministic AEAD][deterministic-aead]. The algorithm of the primary key must +be `DETERMINISTIC_AEAD_AES_SIV_CMAC_256`. Binds the ciphertext to the context +defined by `additional_data`. Returns `NULL` if any input is `NULL`. + +`keyset` is a serialized `BYTES` value returned by one of the `KEYS` functions. + +`plaintext` is the `STRING` or `BYTES` value to be encrypted. + +`additional_data` is a `STRING` or `BYTES` value that binds the ciphertext to +its context. This forces the ciphertext to be decrypted in the same context in +which it was encrypted. `plaintext` and `additional_data` must be of the same +type. `DETERMINISTIC_ENCRYPT(keyset, string1, string2)` is equivalent to +`DETERMINISTIC_ENCRYPT(keyset, CAST(string1 AS BYTES), CAST(string2 AS BYTES))`. + +The output is ciphertext `BYTES`. The ciphertext contains a +[Tink-specific][tink] prefix indicating the key used to perform the encryption. +Given an identical `keyset` and `plaintext`, this function returns the same +ciphertext each time it is invoked (including across queries). + +**Return Data Type** + +`BYTES` + +**Example** + +The following query uses the keysets for each `customer_id` in the +`CustomerKeysets` table to encrypt the value of the plaintext `favorite_animal` +in the `PlaintextCustomerData` table corresponding to that `customer_id`. The +output contains a column of `customer_id` values and a column of corresponding +ciphertext output as `BYTES`. + +```sql +WITH CustomerKeysets AS ( + SELECT 1 AS customer_id, + KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') AS keyset UNION ALL + SELECT 2, KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') UNION ALL + SELECT 3, KEYS.NEW_KEYSET('DETERMINISTIC_AEAD_AES_SIV_CMAC_256') +), PlaintextCustomerData AS ( + SELECT 1 AS customer_id, 'elephant' AS favorite_animal UNION ALL + SELECT 2, 'walrus' UNION ALL + SELECT 3, 'leopard' +) +SELECT + pcd.customer_id, + DETERMINISTIC_ENCRYPT( + (SELECT keyset + FROM CustomerKeysets AS ck + WHERE ck.customer_id = pcd.customer_id), + pcd.favorite_animal, + CAST(pcd.customer_id AS STRING) + ) AS encrypted_animal +FROM PlaintextCustomerData AS pcd; +``` + +[tink]: https://github.com/google/tink/blob/master/docs/KEY-MANAGEMENT.md + +[deterministic-aead]: https://developers.google.com/tink/deterministic-aead + +### `KEYS.ADD_KEY_FROM_RAW_BYTES` + +``` +KEYS.ADD_KEY_FROM_RAW_BYTES(keyset, key_type, raw_key_bytes) +``` + +**Description** + +Returns a serialized keyset as `BYTES` with the +addition of a key to `keyset` based on `key_type` and `raw_key_bytes`. + +The primary cryptographic key remains the same as in `keyset`. The expected +length of `raw_key_bytes` depends on the value of `key_type`. The following are +supported `key_types`: + ++ `'AES_CBC_PKCS'`: Creates a key for AES decryption using cipher block chaining + and PKCS padding. `raw_key_bytes` is expected to be a raw key + `BYTES` value of length 16, 24, or 32; these + lengths have sizes of 128, 192, and 256 bits, respectively. ZetaSQL + AEAD functions do not support keys of these types for encryption; instead, + prefer `'AEAD_AES_GCM_256'` or `'AES_GCM'` keys. ++ `'AES_GCM'`: Creates a key for AES decryption or encryption using + [Galois/Counter Mode][galois-counter-mode]. + `raw_key_bytes` must be a raw key `BYTES` + value of length 16 or 32; these lengths have sizes of 128 and 256 bits, + respectively. When keys of this type are inputs to `AEAD.ENCRYPT`, the output + ciphertext does not have a Tink-specific prefix indicating which key was + used as input. + +**Return Data Type** + +`BYTES` + +**Example** + +The following query creates a table of customer IDs along with raw key bytes, +called `CustomerRawKeys`, and a table of unique IDs, called `CustomerIds`. It +creates a new `'AEAD_AES_GCM_256'` keyset for each `customer_id`; then it adds a +new key to each keyset, using the `raw_key_bytes` value corresponding to that +`customer_id`. The output is a table where each row contains a `customer_id` and +a keyset in `BYTES`, which contains the raw key added +using KEYS.ADD_KEY_FROM_RAW_BYTES. + +```sql +WITH CustomerRawKeys AS ( + SELECT 1 AS customer_id, b'0123456789012345' AS raw_key_bytes UNION ALL + SELECT 2, b'9876543210543210' UNION ALL + SELECT 3, b'0123012301230123' +), CustomerIds AS ( + SELECT 1 AS customer_id UNION ALL + SELECT 2 UNION ALL + SELECT 3 +) +SELECT + ci.customer_id, + KEYS.ADD_KEY_FROM_RAW_BYTES( + KEYS.NEW_KEYSET('AEAD_AES_GCM_256'), + 'AES_CBC_PKCS', + (SELECT raw_key_bytes FROM CustomerRawKeys AS crk + WHERE crk.customer_id = ci.customer_id) + ) AS keyset +FROM CustomerIds AS ci; +``` + +The output keysets each contain two things: the primary cryptographic key +created using `KEYS.NEW_KEYSET('AEAD_AES_GCM_256')`, and the raw key added using +`KEYS.ADD_KEY_FROM_RAW_BYTES`. If a keyset in the output is used with +`AEAD.ENCRYPT`, ZetaSQL uses the primary cryptographic key created +using `KEYS.NEW_KEYSET('AEAD_AES_GCM_256')` to encrypt the input plaintext. If +the keyset is used with `AEAD.DECRYPT_STRING` or `AEAD.DECRYPT_BYTES`, +ZetaSQL returns the resulting plaintext if either key succeeds in +decrypting the ciphertext. + +[galois-counter-mode]: https://en.wikipedia.org/wiki/Galois/Counter_Mode + +### `KEYS.KEYSET_FROM_JSON` + +```sql +KEYS.KEYSET_FROM_JSON(json_keyset) +``` + +**Description** + +Returns the input `json_keyset` `STRING` as +serialized `BYTES`, which is a valid input for other +`KEYS` and `AEAD` functions. The JSON `STRING` must +be compatible with the definition of the +[google.crypto.tink.Keyset][google-crypto-tink-keyset] +protocol buffer message: the JSON keyset should be a JSON object containing +objects and name-value pairs corresponding to those in the "keyset" message in +the google.crypto.tink.Keyset definition. You can convert the output serialized +`BYTES` representation back to a JSON +`STRING` using `KEYS.KEYSET_TO_JSON`. + +**Return Data Type** + +`BYTES` + +**Example** + +`KEYS.KEYSET_FROM_JSON` takes JSON-formatted `STRING` +values like the following: + +```json +{ + "key":[ + { + "keyData":{ + "keyMaterialType":"SYMMETRIC", + "typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey", + "value":"GiD80Z8kL6AP3iSNHhqseZGAIvq7TVQzClT7FQy8YwK3OQ==" + }, + "keyId":3101427138, + "outputPrefixType":"TINK", + "status":"ENABLED" + } + ], + "primaryKeyId":3101427138 +} +``` + +The following query creates a new keyset from a JSON-formatted +`STRING` `json_keyset`: + +```sql +SELECT KEYS.KEYSET_FROM_JSON(json_keyset); +``` + +This returns the `json_keyset` serialized as `BYTES`, like the following: + +``` +\x08\x9d\x8e\x85\x82\x09\x12d\x0aX\x0a0 +type.googleapis.com/google.crypto.tink.AesGcmKey\x12\"\x1a qX\xe4IG\x87\x1f\xde +\xe3)+e\x98\x0a\x1c}\xfe\x88<\x12\xeb\xc1t\xb8\x83\x1a\xcd\xa8\x97\x84g\x18\x01 +\x10\x01\x18\x9d\x8e\x85\x82\x09 \x01 +``` + +[google-crypto-tink-keyset]: https://github.com/google/tink/blob/master/proto/tink.proto + +### `KEYS.KEYSET_LENGTH` + +```sql +KEYS.KEYSET_LENGTH(keyset) +``` + +**Description** + +Returns the number of keys in the provided keyset. + +**Return Data Type** + +`INT64` + +**Example** + +This example references a JSON-formatted STRING +called `json_keyset` that contains two keys: + +```json +{ + "primaryKeyId":1354994251, + "key":[ + { + "keyData":{ + "keyMaterialType":"SYMMETRIC", + "typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey", + "value":"GiD9sxQRgFj4aYN78vaIlxInjZkG/uvyWSY9a8GN+ELV2Q==" + }, + "keyId":1354994251, + "outputPrefixType":"TINK", + "status":"ENABLED" + } + ], + "key":[ + { + "keyData":{ + "keyMaterialType":"SYMMETRIC", + "typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey", + "value":"PRn76sxQRgFj4aYN00vaIlxInjZkG/uvyWSY9a2bLRm" + }, + "keyId":852264701, + "outputPrefixType":"TINK", + "status":"DISABLED" + } + ] +} +``` + +The following query converts `json_keyset` to a keyset and then returns +the number of keys in the keyset: + +```sql +SELECT KEYS.KEYSET_LENGTH(KEYS.KEYSET_FROM_JSON(json_keyset)) as key_count; + +/*-----------* + | key_count | + +-----------+ + | 2 | + *-----------*/ +``` + +### `KEYS.KEYSET_TO_JSON` + +```sql +KEYS.KEYSET_TO_JSON(keyset) +``` + +**Description** + +Returns a JSON `STRING` representation of the input +`keyset`. The returned JSON `STRING` is compatible +with the definition of the +[google.crypto.tink.Keyset][google-crypto-tink-keyset] +protocol buffer message. You can convert the JSON +`STRING` representation back to +`BYTES` using `KEYS.KEYSET_FROM_JSON`. + +**Return Data Type** + +`STRING` + +**Example** + +The following query returns a new `'AEAD_AES_GCM_256'` keyset as a +JSON-formatted `STRING`. + +```sql +SELECT KEYS.KEYSET_TO_JSON(KEYS.NEW_KEYSET('AEAD_AES_GCM_256')); +``` + +The result is a `STRING` like the following. + +```json +{ + "key":[ + { + "keyData":{ + "keyMaterialType":"SYMMETRIC", + "typeUrl":"type.googleapis.com/google.crypto.tink.AesGcmKey", + "value":"GiD80Z8kL6AP3iSNHhqseZGAIvq7TVQzClT7FQy8YwK3OQ==" + }, + "keyId":3101427138, + "outputPrefixType":"TINK", + "status":"ENABLED" + } + ], + "primaryKeyId":3101427138 +} +``` + +[google-crypto-tink-keyset]: https://github.com/google/tink/blob/master/proto/tink.proto + +### `KEYS.NEW_KEYSET` + +``` +KEYS.NEW_KEYSET(key_type) +``` + +**Description** + +Returns a serialized keyset containing a new key based on `key_type`. The +returned keyset is a serialized `BYTES` +representation of +[google.crypto.tink.Keyset][google-crypto-tink-keyset] +that contains a primary cryptographic key and no additional keys. You can use +the keyset with the `AEAD.ENCRYPT`, `AEAD.DECRYPT_BYTES`, and +`AEAD.DECRYPT_STRING` functions for encryption and decryption, as well as with +the `KEYS` group of key- and keyset-related functions. + +`key_type` is a `STRING` literal representation of the type of key to create. +`key_type` cannot be `NULL`. `key_type` can be: + ++ `AEAD_AES_GCM_256`: Creates a 256-bit key with the pseudo-random number + generator provided by [boringSSL][boringSSL]. The key uses AES-GCM for + encryption and decryption operations. ++ `DETERMINISTIC_AEAD_AES_SIV_CMAC_256`: + Creates a 512-bit `AES-SIV-CMAC` key, which contains a 256-bit `AES-CTR` key + and 256-bit `AES-CMAC` key. The `AES-SIV-CMAC` key is created with the + pseudo-random number generator provided by [boringSSL][boringSSL]. The key + uses AES-SIV for encryption and decryption operations. + +**Return Data Type** + +`BYTES` + +**Example** + +The following query creates a keyset for each row in `CustomerIds`, which can +subsequently be used to encrypt data. Each keyset contains a single encryption +key with randomly-generated key data. Each row in the output contains a +`customer_id` and an `'AEAD_AES_GCM_256'` key in +`BYTES`. + +```sql +SELECT customer_id, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset +FROM ( + SELECT 1 AS customer_id UNION ALL + SELECT 2 UNION ALL + SELECT 3 +) AS CustomerIds; +``` + +[boringSSL]: https://boringssl.googlesource.com/boringssl/ + +[google-crypto-tink-keyset]: https://github.com/google/tink/blob/master/proto/tink.proto + +### `KEYS.ROTATE_KEYSET` + +```sql +KEYS.ROTATE_KEYSET(keyset, key_type) +``` + +**Description** + +Adds a new key to `keyset` based on `key_type`. This new key becomes the primary +cryptographic key of the new keyset. Returns the new keyset serialized as +`BYTES`. + +The old primary cryptographic key from the input `keyset` remains an additional +key in the returned keyset. + +The new `key_type` must match the key type of existing keys in the `keyset`. + +**Return Data Type** + +`BYTES` + +**Example** + +The following statement creates a table containing a column of unique +`customer_id` values and `'AEAD_AES_GCM_256'` keysets. Then, it creates a new +primary cryptographic key within each keyset in the source table using +`KEYS.ROTATE_KEYSET`. Each row in the output contains a `customer_id` and an +`'AEAD_AES_GCM_256'` keyset in `BYTES`. + +```sql +WITH ExistingKeysets AS ( +SELECT 1 AS customer_id, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') AS keyset + UNION ALL + SELECT 2, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') UNION ALL + SELECT 3, KEYS.NEW_KEYSET('AEAD_AES_GCM_256') +) +SELECT customer_id, KEYS.ROTATE_KEYSET(keyset, 'AEAD_AES_GCM_256') AS keyset +FROM ExistingKeysets; +``` + +[aead-encryption-concepts]: https://github.com/google/zetasql/blob/master/docs/aead-encryption-concepts.md + ## Aggregate functions ZetaSQL supports the following general aggregate functions. @@ -3904,8 +4733,9 @@ rows. Returns `NULL` when `expression` or `expression2` is `NULL` for all rows in the group. -`ANY_VALUE` behaves as if `RESPECT NULLS` is specified; -rows for which `expression` is `NULL` are considered and may be selected. +`ANY_VALUE` behaves as if `IGNORE NULLS` is specified; +rows for which `expression` is `NULL` are not considered and won't be +selected. To learn more about the optional aggregate clauses that you can pass into this function, see @@ -4299,10 +5129,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -4598,10 +5433,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -4786,10 +5626,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -4993,10 +5838,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -5060,10 +5910,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -5455,10 +6310,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -6681,31 +7541,72 @@ equality comparison logic as `SELECT DISTINCT`. **Examples** ```sql -WITH example AS ( - SELECT [1, 2, 3] AS arr UNION ALL - SELECT [1, 1, 1] AS arr UNION ALL - SELECT [1, 2, NULL] AS arr UNION ALL - SELECT [1, 1, NULL] AS arr UNION ALL - SELECT [1, NULL, NULL] AS arr UNION ALL - SELECT [] AS arr UNION ALL - SELECT CAST(NULL AS ARRAY) AS arr -) -SELECT - arr, - ARRAY_IS_DISTINCT(arr) as is_distinct -FROM example; +SELECT ARRAY_IS_DISTINCT([1, 2, 3]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | true | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, 1, 1]) AS is_distinct -/*-----------------+-------------* - | arr | is_distinct | - +-----------------+-------------+ - | [1, 2, 3] | TRUE | - | [1, 1, 1] | FALSE | - | [1, 2, NULL] | TRUE | - | [1, 1, NULL] | FALSE | - | [1, NULL, NULL] | FALSE | - | [] | TRUE | - | NULL | NULL | - *-----------------+-------------*/ +/*-------------* + | is_distinct | + +-------------+ + | false | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, 2, NULL]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | true | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, 1, NULL]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | false | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT([1, NULL, NULL]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | false | + *-------------*/ +``` +```sql +SELECT ARRAY_IS_DISTINCT([]) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | true | + *-------------*/ +``` + +```sql +SELECT ARRAY_IS_DISTINCT(NULL) AS is_distinct + +/*-------------* + | is_distinct | + +-------------+ + | NULL | + *-------------*/ ``` ### `ARRAY_LAST` @@ -6760,20 +7661,15 @@ the `array_expression` is `NULL`. **Examples** ```sql -WITH items AS - (SELECT ["coffee", NULL, "milk" ] as list - UNION ALL - SELECT ["cake", "pie"] as list) -SELECT ARRAY_TO_STRING(list, ', ', 'NULL'), ARRAY_LENGTH(list) AS size -FROM items -ORDER BY size DESC; +SELECT + ARRAY_LENGTH(["coffee", NULL, "milk" ]) AS size_a, + ARRAY_LENGTH(["cake", "pie"]) AS size_b; -/*--------------------+------* - | list | size | - +--------------------+------+ - | coffee, NULL, milk | 3 | - | cake, pie | 2 | - *--------------------+------*/ +/*--------+--------* + | size_a | size_b | + +--------+--------+ + | 3 | 2 | + *--------+--------*/ ``` ### `ARRAY_MAX` @@ -6871,23 +7767,13 @@ Returns the input `ARRAY` with elements in reverse order. **Examples** ```sql -WITH example AS ( - SELECT [1, 2, 3] AS arr UNION ALL - SELECT [4, 5] AS arr UNION ALL - SELECT [] AS arr -) -SELECT - arr, - ARRAY_REVERSE(arr) AS reverse_arr -FROM example; +SELECT ARRAY_REVERSE([1, 2, 3]) AS reverse_arr -/*-----------+-------------* - | arr | reverse_arr | - +-----------+-------------+ - | [1, 2, 3] | [3, 2, 1] | - | [4, 5] | [5, 4] | - | [] | [] | - *-----------+-------------*/ +/*-------------* + | reverse_arr | + +-------------+ + | [3, 2, 1] | + *-------------*/ ``` ### `ARRAY_SLICE` @@ -7219,35 +8105,22 @@ and its preceding delimiter. **Examples** ```sql -WITH items AS - (SELECT ['coffee', 'tea', 'milk' ] as list - UNION ALL - SELECT ['cake', 'pie', NULL] as list) - -SELECT ARRAY_TO_STRING(list, '--') AS text -FROM items; +SELECT ARRAY_TO_STRING(['coffee', 'tea', 'milk', NULL], '--', 'MISSING') AS text /*--------------------------------* | text | +--------------------------------+ - | coffee--tea--milk | - | cake--pie | + | coffee--tea--milk--MISSING | *--------------------------------*/ ``` ```sql -WITH items AS - (SELECT ['coffee', 'tea', 'milk' ] as list - UNION ALL - SELECT ['cake', 'pie', NULL] as list) -SELECT ARRAY_TO_STRING(list, '--', 'MISSING') AS text -FROM items; +SELECT ARRAY_TO_STRING(['cake', 'pie', NULL], '--', 'MISSING') AS text /*--------------------------------* | text | +--------------------------------+ - | coffee--tea--milk | | cake--pie--MISSING | *--------------------------------*/ ``` @@ -8249,6 +9122,24 @@ learn more about implicit and explicit conversion [here][conversion-rules]. + + PARSE_BIGNUMERIC + + + + Converts a STRING value to a BIGNUMERIC value. + + + + + PARSE_NUMERIC + + + + Converts a STRING value to a NUMERIC value. + + + SAFE_CAST @@ -9593,6 +10484,472 @@ SELECT CAST('06/02/2020 17:00:53.110 +00' AS TIMESTAMP FORMAT 'MM/DD/YYYY HH24:M [con-func-safecast]: #safe_casting +### `PARSE_BIGNUMERIC` + + +```sql +PARSE_BIGNUMERIC(string_expression) +``` + +**Description** + +Converts a `STRING` to a `BIGNUMERIC` value. + +The numeric literal contained in the string must not exceed the +[maximum precision or range][bignumeric-type] of the `BIGNUMERIC` type, or an +error occurs. If the number of digits after the decimal point exceeds 38, then +the resulting `BIGNUMERIC` value rounds +[half away from zero][half-from-zero-wikipedia] to have 38 digits after the +decimal point. + +```sql + +-- This example shows how a string with a decimal point is parsed. +SELECT PARSE_BIGNUMERIC("123.45") AS parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ + +-- This example shows how a string with an exponent is parsed. +SELECT PARSE_BIGNUMERIC("123.456E37") AS parsed; + +/*-----------------------------------------* + | parsed | + +-----------------------------------------+ + | 123400000000000000000000000000000000000 | + *-----------------------------------------*/ + +-- This example shows the rounding when digits after the decimal point exceeds 38. +SELECT PARSE_BIGNUMERIC("1.123456789012345678901234567890123456789") as parsed; + +/*------------------------------------------* + | parsed | + +------------------------------------------+ + | 1.12345678901234567890123456789012345679 | + *------------------------------------------*/ +``` + +This function is similar to using the [`CAST AS BIGNUMERIC`][cast-bignumeric] +function except that the `PARSE_BIGNUMERIC` function only accepts string inputs +and allows the following in the string: + ++ Spaces between the sign (+/-) and the number ++ Signs (+/-) after the number + +Rules for valid input strings: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RuleExample InputOutput
+ The string can only contain digits, commas, decimal points and signs. + + "- 12,34567,89.0" + -123456789
Whitespaces are allowed anywhere except between digits. + " - 12.345 " + -12.345
Only digits and commas are allowed before the decimal point. + " 12,345,678" + 12345678
Only digits are allowed after the decimal point. + "1.234 " + 1.234
+ Use E or e for exponents. After the + e, digits and a leading sign indicator are allowed. + " 123.45e-1"12.345
+ If the integer part is not empty, then it must contain at least one + digit. + " 0,.12 -"-0.12
+ If the string contains a decimal point, then it must contain at least + one digit. + " .1"0.1
+ The string cannot contain more than one sign. + " 0.5 +"0.5
+ +**Return Data Type** + +`BIGNUMERIC` + +**Examples** + +This example shows an input with spaces before, after, and between the +sign and the number: + +```sql +SELECT PARSE_BIGNUMERIC(" - 12.34 ") as parsed; + +/*--------* + | parsed | + +--------+ + | -12.34 | + *--------*/ +``` + +This example shows an input with an exponent as well as the sign after the +number: + +```sql +SELECT PARSE_BIGNUMERIC("12.34e-1-") as parsed; + +/*--------* + | parsed | + +--------+ + | -1.234 | + *--------*/ +``` + +This example shows an input with multiple commas in the integer part of the +number: + +```sql +SELECT PARSE_BIGNUMERIC(" 1,2,,3,.45 + ") as parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ +``` + +This example shows an input with a decimal point and no digits in the whole +number part: + +```sql +SELECT PARSE_BIGNUMERIC(".1234 ") as parsed; + +/*--------* + | parsed | + +--------+ + | 0.1234 | + *--------*/ +``` + +**Examples of invalid inputs** + +This example is invalid because the whole number part contains no digits: + +```sql +SELECT PARSE_BIGNUMERIC(",,,.1234 ") as parsed; +``` + +This example is invalid because there are whitespaces between digits: + +```sql +SELECT PARSE_BIGNUMERIC("1 23.4 5 ") as parsed; +``` + +This example is invalid because the number is empty except for an exponent: + +```sql +SELECT PARSE_BIGNUMERIC(" e1 ") as parsed; +``` + +This example is invalid because the string contains multiple signs: + +```sql +SELECT PARSE_BIGNUMERIC(" - 12.3 - ") as parsed; +``` + +This example is invalid because the value of the number falls outside the range +of `BIGNUMERIC`: + +```sql +SELECT PARSE_BIGNUMERIC("12.34E100 ") as parsed; +``` + +This example is invalid because the string contains invalid characters: + +```sql +SELECT PARSE_BIGNUMERIC("$12.34") as parsed; +``` + +[half-from-zero-wikipedia]: https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero + +[cast-bignumeric]: #cast_bignumeric + +[bignumeric-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#decimal_types + +### `PARSE_NUMERIC` + +```sql +PARSE_NUMERIC(string_expression) +``` + +**Description** + +Converts a `STRING` to a `NUMERIC` value. + +The numeric literal contained in the string must not exceed the +[maximum precision or range][numeric-type] of the `NUMERIC` type, or an error +occurs. If the number of digits after the decimal point exceeds nine, then the +resulting `NUMERIC` value rounds +[half away from zero][half-from-zero-wikipedia] to have nine digits after the +decimal point. + +```sql + +-- This example shows how a string with a decimal point is parsed. +SELECT PARSE_NUMERIC("123.45") AS parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ + +-- This example shows how a string with an exponent is parsed. +SELECT PARSE_NUMERIC("12.34E27") as parsed; + +/*-------------------------------* + | parsed | + +-------------------------------+ + | 12340000000000000000000000000 | + *-------------------------------*/ + +-- This example shows the rounding when digits after the decimal point exceeds 9. +SELECT PARSE_NUMERIC("1.0123456789") as parsed; + +/*-------------* + | parsed | + +-------------+ + | 1.012345679 | + *-------------*/ +``` + +This function is similar to using the [`CAST AS NUMERIC`][cast-numeric] function +except that the `PARSE_NUMERIC` function only accepts string inputs and allows +the following in the string: + ++ Spaces between the sign (+/-) and the number ++ Signs (+/-) after the number + +Rules for valid input strings: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RuleExample InputOutput
+ The string can only contain digits, commas, decimal points and signs. + + "- 12,34567,89.0" + -123456789
Whitespaces are allowed anywhere except between digits. + " - 12.345 " + -12.345
Only digits and commas are allowed before the decimal point. + " 12,345,678" + 12345678
Only digits are allowed after the decimal point. + "1.234 " + 1.234
+ Use E or e for exponents. After + the e, + digits and a leading sign indicator are allowed. + " 123.45e-1"12.345
+ If the integer part is not empty, then it must contain at least one + digit. + " 0,.12 -"-0.12
+ If the string contains a decimal point, then it must contain at least + one digit. + " .1"0.1
+ The string cannot contain more than one sign. + " 0.5 +"0.5
+ +**Return Data Type** + +`NUMERIC` + +**Examples** + +This example shows an input with spaces before, after, and between the +sign and the number: + +```sql +SELECT PARSE_NUMERIC(" - 12.34 ") as parsed; + +/*--------* + | parsed | + +--------+ + | -12.34 | + *--------*/ +``` + +This example shows an input with an exponent as well as the sign after the +number: + +```sql +SELECT PARSE_NUMERIC("12.34e-1-") as parsed; + +/*--------* + | parsed | + +--------+ + | -1.234 | + *--------*/ +``` + +This example shows an input with multiple commas in the integer part of the +number: + +```sql +SELECT PARSE_NUMERIC(" 1,2,,3,.45 + ") as parsed; + +/*--------* + | parsed | + +--------+ + | 123.45 | + *--------*/ +``` + +This example shows an input with a decimal point and no digits in the whole +number part: + +```sql +SELECT PARSE_NUMERIC(".1234 ") as parsed; + +/*--------* + | parsed | + +--------+ + | 0.1234 | + *--------*/ +``` + +**Examples of invalid inputs** + +This example is invalid because the whole number part contains no digits: + +```sql +SELECT PARSE_NUMERIC(",,,.1234 ") as parsed; +``` + +This example is invalid because there are whitespaces between digits: + +```sql +SELECT PARSE_NUMERIC("1 23.4 5 ") as parsed; +``` + +This example is invalid because the number is empty except for an exponent: + +```sql +SELECT PARSE_NUMERIC(" e1 ") as parsed; +``` + +This example is invalid because the string contains multiple signs: + +```sql +SELECT PARSE_NUMERIC(" - 12.3 - ") as parsed; +``` + +This example is invalid because the value of the number falls outside the range +of `BIGNUMERIC`: + +```sql +SELECT PARSE_NUMERIC("12.34E100 ") as parsed; +``` + +This example is invalid because the string contains invalid characters: + +```sql +SELECT PARSE_NUMERIC("$12.34") as parsed; +``` + +[half-from-zero-wikipedia]: https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero + +[cast-numeric]: #cast_numeric + +[numeric-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#decimal_types + ### `SAFE_CAST` @@ -9983,24 +11340,6 @@ SELECT CURRENT_DATE AS the_date; *--------------*/ ``` -When a column named `current_date` is present, the column name and the function -call without parentheses are ambiguous. To ensure the function call, add -parentheses; to ensure the column name, qualify it with its -[range variable][date-range-variables]. For example, the -following query will select the function in the `the_date` column and the table -column in the `current_date` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_date`) -SELECT current_date() AS the_date, t.current_date FROM t; - -/*------------+--------------* - | the_date | current_date | - +------------+--------------+ - | 2016-12-25 | column value | - *------------+--------------*/ -``` - [date-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables [date-timezone-definitions]: https://github.com/google/zetasql/blob/master/docs/data-types.md#time_zones @@ -10114,7 +11453,9 @@ Gets the number of unit boundaries between two `DATE` values (`end_date` - + `start_date`: The starting `DATE` value. + `end_date`: The ending `DATE` value. -+ `granularity`: The date part that represents the granularity. This can be: ++ `granularity`: The date part that represents the granularity. If + you have passed in `DATE` values for the first arguments, `granularity` can + be: + `DAY` + `WEEK` This date part begins on Sunday. @@ -10134,6 +11475,10 @@ Gets the number of unit boundaries between two `DATE` values (`end_date` - If `end_date` is earlier than `start_date`, the output is negative. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `DATE_DIFF(TIMESTAMP, TIMESTAMP, PART)` +behaves like `TIMESTAMP_DIFF(TIMESTAMP, TIMESTAMP, PART)`. + **Return Data Type** `INT64` @@ -10279,38 +11624,51 @@ SELECT DATE_SUB(DATE '2008-12-25', INTERVAL 5 DAY) AS five_days_ago; ### `DATE_TRUNC` ```sql -DATE_TRUNC(date_expression, date_part) +DATE_TRUNC(date_expression, granularity) ``` **Description** -Truncates a `DATE` value to the granularity of `date_part`. The `DATE` value -is always rounded to the beginning of `date_part`, which can be one of the -following: +Truncates a `DATE` value at a particular time granularity. The `DATE` value +is always rounded to the beginning of `granularity`. -+ `DAY`: The day in the Gregorian calendar year that contains the - `DATE` value. -+ `WEEK`: The first day of the week in the week that contains the - `DATE` value. Weeks begin on Sundays. `WEEK` is equivalent to - `WEEK(SUNDAY)`. -+ `WEEK(WEEKDAY)`: The first day of the week in the week that contains the - `DATE` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the - following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, - or `SATURDAY`. -+ `ISOWEEK`: The first day of the [ISO 8601 week][ISO-8601-week] in the - ISO week that contains the `DATE` value. The ISO week begins on - Monday. The first ISO week of each ISO year contains the first Thursday of the - corresponding Gregorian calendar year. -+ `MONTH`: The first day of the month in the month that contains the - `DATE` value. -+ `QUARTER`: The first day of the quarter in the quarter that contains the - `DATE` value. -+ `YEAR`: The first day of the year in the year that contains the - `DATE` value. -+ `ISOYEAR`: The first day of the [ISO 8601][ISO-8601] week-numbering year - in the ISO year that contains the `DATE` value. The ISO year is the - Monday of the first week whose Thursday belongs to the corresponding - Gregorian calendar year. +**Definitions** + ++ `date_expression`: The `DATE` value to truncate. ++ `granularity`: The date part that represents the granularity. If + you passed in a `DATE` value for the first argument, `granularity` can + be: + + + `DAY`: The day in the Gregorian calendar year that contains the + `DATE` value. + + + `WEEK`: The first day in the week that contains the + `DATE` value. Weeks begin on Sundays. `WEEK` is equivalent to + `WEEK(SUNDAY)`. + + + `WEEK(WEEKDAY)`: The first day in the week that contains the + `DATE` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the + following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, + or `SATURDAY`. + + + `ISOWEEK`: The first day in the [ISO 8601 week][ISO-8601-week] that contains + the `DATE` value. The ISO week begins on + Monday. The first ISO week of each ISO year contains the first Thursday of the + corresponding Gregorian calendar year. + + + `MONTH`: The first day in the month that contains the + `DATE` value. + + + `QUARTER`: The first day in the quarter that contains the + `DATE` value. + + + `YEAR`: The first day in the year that contains the + `DATE` value. + + + `ISOYEAR`: The first day in the [ISO 8601][ISO-8601] week-numbering year + that contains the `DATE` value. The ISO year is the + Monday of the first week where Thursday belongs to the corresponding + Gregorian calendar year. @@ -10322,7 +11680,7 @@ following: **Return Data Type** -DATE +`DATE` **Examples** @@ -10883,24 +12241,6 @@ SELECT CURRENT_DATETIME() as now; *----------------------------*/ ``` -When a column named `current_datetime` is present, the column name and the -function call without parentheses are ambiguous. To ensure the function call, -add parentheses; to ensure the column name, qualify it with its -[range variable][datetime-range-variables]. For example, the -following query will select the function in the `now` column and the table -column in the `current_datetime` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_datetime`) -SELECT current_datetime() as now, t.current_datetime FROM t; - -/*----------------------------+------------------* - | now | current_datetime | - +----------------------------+------------------+ - | 2016-05-19 10:38:47.046465 | column value | - *----------------------------+------------------*/ -``` - [datetime-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables [datetime-timezone-definitions]: #timezone_definitions @@ -11008,8 +12348,9 @@ Gets the number of unit boundaries between two `DATETIME` values + `start_datetime`: The starting `DATETIME` value. + `end_datetime`: The ending `DATETIME` value. -+ `granularity`: The datetime part that represents the granularity. - This can be: ++ `granularity`: The datetime part that represents the granularity. If + you have passed in `DATETIME` values for the first arguments, `granularity` + can be: + `NANOSECOND` @@ -11041,6 +12382,10 @@ Produces an error if the computation overflows, such as if the difference in nanoseconds between the two `DATETIME` values overflows. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `DATETIME_DIFF(TIMESTAMP, TIMESTAMP, PART)` +behaves like `TIMESTAMP_DIFF(TIMESTAMP, TIMESTAMP, PART)`. + **Return Data Type** `INT64` @@ -11178,44 +12523,63 @@ SELECT ### `DATETIME_TRUNC` ```sql -DATETIME_TRUNC(datetime_expression, date_time_part) -``` - -**Description** - -Truncates a `DATETIME` value to the granularity of `date_time_part`. -The `DATETIME` value is always rounded to the beginning of `date_time_part`, -which can be one of the following: - -+ `NANOSECOND`: If used, nothing is truncated from the value. -+ `MICROSECOND`: The nearest lessor or equal microsecond. -+ `MILLISECOND`: The nearest lessor or equal millisecond. -+ `SECOND`: The nearest lessor or equal second. -+ `MINUTE`: The nearest lessor or equal minute. -+ `HOUR`: The nearest lessor or equal hour. -+ `DAY`: The day in the Gregorian calendar year that contains the - `DATETIME` value. -+ `WEEK`: The first day of the week in the week that contains the - `DATETIME` value. Weeks begin on Sundays. `WEEK` is equivalent to - `WEEK(SUNDAY)`. -+ `WEEK(WEEKDAY)`: The first day of the week in the week that contains the - `DATETIME` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the - following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, - or `SATURDAY`. -+ `ISOWEEK`: The first day of the [ISO 8601 week][ISO-8601-week] in the - ISO week that contains the `DATETIME` value. The ISO week begins on - Monday. The first ISO week of each ISO year contains the first Thursday of the - corresponding Gregorian calendar year. -+ `MONTH`: The first day of the month in the month that contains the - `DATETIME` value. -+ `QUARTER`: The first day of the quarter in the quarter that contains the - `DATETIME` value. -+ `YEAR`: The first day of the year in the year that contains the - `DATETIME` value. -+ `ISOYEAR`: The first day of the [ISO 8601][ISO-8601] week-numbering year - in the ISO year that contains the `DATETIME` value. The ISO year is the - Monday of the first week whose Thursday belongs to the corresponding - Gregorian calendar year. +DATETIME_TRUNC(datetime_expression, granularity) +``` + +**Description** + +Truncates a `DATETIME` value at a particular time granularity. The `DATETIME` +value is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `datetime_expression`: The `DATETIME` value to truncate. ++ `granularity`: The datetime part that represents the granularity. If + you passed in a `DATETIME` value for the first argument, `granularity` can + be: + + + `NANOSECOND`: If used, nothing is truncated from the value. + + + `MICROSECOND`: The nearest lesser than or equal microsecond. + + + `MILLISECOND`: The nearest lesser than or equal millisecond. + + + `SECOND`: The nearest lesser than or equal second. + + + `MINUTE`: The nearest lesser than or equal minute. + + + `HOUR`: The nearest lesser than or equal hour. + + + `DAY`: The day in the Gregorian calendar year that contains the + `DATETIME` value. + + + `WEEK`: The first day in the week that contains the + `DATETIME` value. Weeks begin on Sundays. `WEEK` is equivalent to + `WEEK(SUNDAY)`. + + + `WEEK(WEEKDAY)`: The first day in the week that contains the + `DATETIME` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the + following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, + or `SATURDAY`. + + + `ISOWEEK`: The first day in the [ISO 8601 week][ISO-8601-week] that contains + the `DATETIME` value. The ISO week begins on + Monday. The first ISO week of each ISO year contains the first Thursday of the + corresponding Gregorian calendar year. + + + `MONTH`: The first day in the month that contains the + `DATETIME` value. + + + `QUARTER`: The first day in the quarter that contains the + `DATETIME` value. + + + `YEAR`: The first day in the year that contains the + `DATETIME` value. + + + `ISOYEAR`: The first day in the [ISO 8601][ISO-8601] week-numbering year + that contains the `DATETIME` value. The ISO year is the + Monday of the first week where Thursday belongs to the corresponding + Gregorian calendar year. @@ -11728,7 +13092,8 @@ Returns an error. **Definitions** + `error_message`: A `STRING` value that represents the error message to - produce. + produce. Any whitespace characters beyond a + single space are trimmed from the results. **Details** @@ -11741,6 +13106,13 @@ ZetaSQL infers the return type in context. **Examples** +In the following example, the query produces an error message: + +```sql +-- ERROR: Show this error message (while evaluating error("Show this error message")) +SELECT ERROR('Show this error message') +``` + In the following example, the query returns an error message if the value of the row does not match one of two defined values. @@ -11824,7 +13196,7 @@ The [supertype][supertype] for `try_expression` and **Example** -In the following examples, the query successfully evaluates `try_expression`. +In the following example, the query successfully evaluates `try_expression`. ```sql SELECT IFERROR('a', 'b') AS result @@ -11836,6 +13208,9 @@ SELECT IFERROR('a', 'b') AS result *--------*/ ``` +In the following example, the query successfully evaluates the +`try_expression` subquery. + ```sql SELECT IFERROR((SELECT [1,2,3][OFFSET(0)]), -1) AS result @@ -11846,7 +13221,7 @@ SELECT IFERROR((SELECT [1,2,3][OFFSET(0)]), -1) AS result *--------*/ ``` -In the following examples, `IFERROR` catches an evaluation error in the +In the following example, `IFERROR` catches an evaluation error in the `try_expression` and successfully evaluates `catch_expression`. ```sql @@ -11859,6 +13234,9 @@ SELECT IFERROR(ERROR('a'), 'b') AS result *--------*/ ``` +In the following example, `IFERROR` catches an evaluation error in the +`try_expression` subquery and successfully evaluates `catch_expression`. + ```sql SELECT IFERROR((SELECT [1,2,3][OFFSET(9)]), -1) AS result @@ -12053,7 +13431,7 @@ The data type for `try_expression` or `NULL` **Example** -In the following examples, `NULLIFERROR` successfully evaluates +In the following example, `NULLIFERROR` successfully evaluates `try_expression`. ```sql @@ -12066,6 +13444,9 @@ SELECT NULLIFERROR('a') AS result *--------*/ ``` +In the following example, `NULLIFERROR` successfully evaluates +the `try_expression` subquery. + ```sql SELECT NULLIFERROR((SELECT [1,2,3][OFFSET(0)])) AS result @@ -12076,7 +13457,7 @@ SELECT NULLIFERROR((SELECT [1,2,3][OFFSET(0)])) AS result *--------*/ ``` -In the following examples, `NULLIFERROR` catches an evaluation error in +In the following example, `NULLIFERROR` catches an evaluation error in `try_expression`. ```sql @@ -12089,6 +13470,9 @@ SELECT NULLIFERROR(ERROR('a')) AS result *--------*/ ``` +In the following example, `NULLIFERROR` catches an evaluation error in +the `try_expression` subquery. + ```sql SELECT NULLIFERROR((SELECT [1,2,3][OFFSET(9)])) AS result @@ -14108,6 +15492,17 @@ behavior: + + S2 functions + + S2_CELLIDFROMPOINT
+ S2_COVERINGCELLIDS
+ + + Functions for working with S2 cell coverings of GEOGRAPHY. + + + @@ -14122,6 +15517,24 @@ behavior: + + S2_CELLIDFROMPOINT + + + + Gets the S2 cell ID covering a point GEOGRAPHY value. + + + + + S2_COVERINGCELLIDS + + + + Gets an array of S2 cell IDs that cover a GEOGRAPHY value. + + + ST_ACCUM @@ -14801,6 +16214,135 @@ behavior: +### `S2_CELLIDFROMPOINT` + +```sql +S2_CELLIDFROMPOINT(point_geography[, level => cell_level]) +``` + +**Description** + +Returns the [S2 cell ID][s2-cells-link] covering a point `GEOGRAPHY`. + ++ The optional `INT64` parameter `level` specifies the S2 cell level for the + returned cell. Naming this argument is optional. + +This is advanced functionality for interoperability with systems utilizing the +[S2 Geometry Library][s2-root-link]. + +**Constraints** + ++ Returns the cell ID as a signed `INT64` bit-equivalent to + [unsigned 64-bit integer representation][s2-cells-link]. ++ Can return negative cell IDs. ++ Valid S2 cell levels are 0 to 30. ++ `level` defaults to 30 if not explicitly specified. ++ The function only supports a single point GEOGRAPHY. Use the `SAFE` prefix if + the input can be multipoint, linestring, polygon, or an empty `GEOGRAPHY`. ++ To compute the covering of a complex `GEOGRAPHY`, use + [S2_COVERINGCELLIDS][s2-coveringcellids]. + +**Return type** + +`INT64` + +**Example** + +```sql +WITH data AS ( + SELECT 1 AS id, ST_GEOGPOINT(-122, 47) AS geo + UNION ALL + -- empty geography is not supported + SELECT 2 AS id, ST_GEOGFROMTEXT('POINT EMPTY') AS geo + UNION ALL + -- only points are supported + SELECT 3 AS id, ST_GEOGFROMTEXT('LINESTRING(1 2, 3 4)') AS geo +) +SELECT id, + SAFE.S2_CELLIDFROMPOINT(geo) cell30, + SAFE.S2_CELLIDFROMPOINT(geo, level => 10) cell10 +FROM data; + +/*----+---------------------+---------------------* + | id | cell30 | cell10 | + +----+---------------------+---------------------+ + | 1 | 6093613931972369317 | 6093613287902019584 | + | 2 | NULL | NULL | + | 3 | NULL | NULL | + *----+---------------------+---------------------*/ +``` + +[s2-cells-link]: https://s2geometry.io/devguide/s2cell_hierarchy + +[s2-root-link]: https://s2geometry.io/ + +[s2-coveringcellids]: #s2_coveringcellids + +### `S2_COVERINGCELLIDS` + +```sql +S2_COVERINGCELLIDS( + geography + [, min_level => cell_level] + [, max_level => cell_level] + [, max_cells => max_cells] + [, buffer => buffer]) +``` + +**Description** + +Returns an array of [S2 cell IDs][s2-cells-link] that cover the input +`GEOGRAPHY`. The function returns at most `max_cells` cells. The optional +arguments `min_level` and `max_level` specify minimum and maximum levels for +returned S2 cells. The array size is limited by the optional `max_cells` +argument. The optional `buffer` argument specifies a buffering factor in +meters; the region being covered is expanded from the extent of the +input geography by this amount. + +This is advanced functionality for interoperability with systems utilizing the +[S2 Geometry Library][s2-root-link]. + +**Constraints** + ++ Returns the cell ID as a signed `INT64` bit-equivalent to + [unsigned 64-bit integer representation][s2-cells-link]. ++ Can return negative cell IDs. ++ Valid S2 cell levels are 0 to 30. ++ `max_cells` defaults to 8 if not explicitly specified. ++ `buffer` should be nonnegative. It defaults to 0.0 meters if not explicitly + specified. + +**Return type** + +`ARRAY` + +**Example** + +```sql +WITH data AS ( + SELECT 1 AS id, ST_GEOGPOINT(-122, 47) AS geo + UNION ALL + SELECT 2 AS id, ST_GEOGFROMTEXT('POINT EMPTY') AS geo + UNION ALL + SELECT 3 AS id, ST_GEOGFROMTEXT('LINESTRING(-122.12 47.67, -122.19 47.69)') AS geo +) +SELECT id, S2_COVERINGCELLIDS(geo, min_level => 12) cells +FROM data; + +/*----+--------------------------------------------------------------------------------------* + | id | cells | + +----+--------------------------------------------------------------------------------------+ + | 1 | [6093613931972369317] | + | 2 | [] | + | 3 | [6093384954555662336, 6093390709811838976, 6093390735581642752, 6093390740145045504, | + | | 6093390791416217600, 6093390812891054080, 6093390817187069952, 6093496378892222464] | + *----+--------------------------------------------------------------------------------------*/ +``` + +[s2-cells-link]: https://s2geometry.io/devguide/s2cell_hierarchy + +[s2-root-link]: https://s2geometry.io/ + ### `ST_ACCUM` ```sql @@ -18671,6 +20213,8 @@ behavior: JSON_EXTRACT_SCALAR
+ JSON_EXTRACT_ARRAY
+ JSON_EXTRACT_STRING_ARRAY
@@ -18898,7 +20442,7 @@ behavior: BOOL - Converts a JSON boolean to a SQL ARRAY<BOOL> value. + Converts a JSON array of booleans to a SQL ARRAY<BOOL> value. @@ -18921,7 +20465,7 @@ behavior: - Converts a JSON number to a SQL ARRAY<DOUBLE> value. + Converts a JSON array of numbers to a SQL ARRAY<DOUBLE> value. @@ -18941,7 +20485,7 @@ behavior: - Converts a JSON number to a SQL ARRAY<FLOAT> value. + Converts a JSON array of numbers to a SQL ARRAY<FLOAT> value. @@ -18973,7 +20517,7 @@ behavior: INT64_ARRAY - Converts a JSON number to a SQL ARRAY<INT64> value. + Converts a JSON array of numbers to a SQL ARRAY<INT64> value. @@ -19012,6 +20556,21 @@ behavior: + + JSON_EXTRACT_ARRAY + + + + (Deprecated) + Extracts a JSON array and converts it to + a SQL ARRAY<JSON-formatted STRING> + or + ARRAY<JSON> + + value. + + + JSON_EXTRACT_SCALAR @@ -19023,6 +20582,17 @@ behavior: + + JSON_EXTRACT_STRING_ARRAY + + + + (Deprecated) + Extracts a JSON array of scalar values and converts it to a SQL + ARRAY<STRING> value. + + + JSON_OBJECT @@ -19397,7 +20967,7 @@ BOOL_ARRAY(json_expr) **Description** -Converts a JSON boolean to a SQL `ARRAY` value. +Converts a JSON array of booleans to a SQL `ARRAY` value. Arguments: @@ -19540,7 +21110,7 @@ DOUBLE_ARRAY(json_expr[, wide_number_mode=>{ 'exact' | 'round' }]) **Description** -Converts a JSON number to a SQL `ARRAY` value. +Converts a JSON array of numbers to a SQL `ARRAY` value. Arguments: @@ -19720,7 +21290,7 @@ FLOAT_ARRAY(json_expr[, wide_number_mode=>{ 'exact' | 'round' }]) **Description** -Converts a JSON number to a SQL `ARRAY` value. +Converts a JSON array of numbers to a SQL `ARRAY` value. Arguments: @@ -20001,7 +21571,7 @@ INT64_ARRAY(json_expr) **Description** -Converts a JSON number to a SQL `INT64_ARRAY` value. +Converts a JSON array of numbers to a SQL `INT64_ARRAY` value. Arguments: @@ -20072,18 +21642,6 @@ Arguments: **Examples** -You can create an empty JSON array. For example: - -```sql -SELECT JSON_ARRAY() AS json_data - -/*-----------* - | json_data | - +-----------+ - | [] | - *-----------*/ -``` - The following query creates a JSON array with one value in it: ```sql @@ -20148,6 +21706,18 @@ SELECT JSON_ARRAY(10, [JSON '20', JSON '"foo"']) AS json_data *-----------------*/ ``` +You can create an empty JSON array. For example: + +```sql +SELECT JSON_ARRAY() AS json_data + +/*-----------* + | json_data | + +-----------+ + | [] | + *-----------*/ +``` + ### `JSON_ARRAY_APPEND` ```sql @@ -20601,71 +22171,157 @@ In the following examples, JSON data is extracted and returned as JSON-formatted strings. ```sql -SELECT JSON_EXTRACT(json_text, '$') AS json_text_string -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + '$') AS json_text_string; /*-----------------------------------------------------------* | json_text_string | +-----------------------------------------------------------+ | {"class":{"students":[{"name":"Jane"}]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[{"name":"John"},{"name":"Jamie"}]}} | *-----------------------------------------------------------*/ ``` ```sql -SELECT JSON_EXTRACT(json_text, '$.class.students[0]') AS first_student -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + '$.class.students[0]') AS first_student; /*-----------------* | first_student | +-----------------+ | {"name":"Jane"} | + *-----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | NULL | + *-----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | {"name":"John"} | *-----------------*/ ``` ```sql -SELECT JSON_EXTRACT(json_text, '$.class.students[1].name') AS second_student_name -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": null}]}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + '$.class.students[1].name') AS second_student; /*----------------* | second_student | +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": null}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | "Jamie" | *----------------*/ ``` ```sql -SELECT JSON_EXTRACT(json_text, "$.class['students']") AS student_names -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + "$.class['students']") AS student_names; /*------------------------------------* | student_names | +------------------------------------+ | [{"name":"Jane"}] | + *------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + "$.class['students']") AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [] | + *------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + "$.class['students']") AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [{"name":"John"},{"name":"Jamie"}] | *------------------------------------*/ ``` @@ -20686,6 +22342,210 @@ SELECT JSON_EXTRACT(JSON '{"a": null}', "$.b"); -- Returns a SQL NULL [differences-json-and-string]: #differences_json_and_string +### `JSON_EXTRACT_ARRAY` + +Note: This function is deprecated. Consider using +[JSON_QUERY_ARRAY][json-query-array]. + +```sql +JSON_EXTRACT_ARRAY(json_string_expr[, json_path]) +``` + +```sql +JSON_EXTRACT_ARRAY(json_expr[, json_path]) +``` + +**Description** + +Extracts a JSON array and converts it to +a SQL `ARRAY` or +`ARRAY` value. +This function uses single quotes and brackets to escape invalid +[JSONPath][JSONPath-format] characters in JSON keys. For example: `['a.b']`. + +Arguments: + ++ `json_string_expr`: A JSON-formatted string. For example: + + ``` + '["a", "b", {"key": "c"}]' + ``` ++ `json_expr`: JSON. For example: + + ``` + JSON '["a", "b", {"key": "c"}]' + ``` ++ `json_path`: The [JSONPath][JSONPath-format]. This identifies the data that + you want to obtain from the input. If this optional parameter is not + provided, then the JSONPath `$` symbol is applied, which means that all of + the data is analyzed. + +There are differences between the JSON-formatted string and JSON input types. +For details, see [Differences between the JSON and JSON-formatted STRING types][differences-json-and-string]. + +**Return type** + ++ `json_string_expr`: `ARRAY` ++ `json_expr`: `ARRAY` + +**Examples** + +This extracts items in JSON to an array of `JSON` values: + +```sql +SELECT JSON_EXTRACT_ARRAY( + JSON '{"fruits":["apples","oranges","grapes"]}','$.fruits' + ) AS json_array; + +/*---------------------------------* + | json_array | + +---------------------------------+ + | ["apples", "oranges", "grapes"] | + *---------------------------------*/ +``` + +This extracts the items in a JSON-formatted string to a string array: + +```sql +SELECT JSON_EXTRACT_ARRAY('[1,2,3]') AS string_array; + +/*--------------* + | string_array | + +--------------+ + | [1, 2, 3] | + *--------------*/ +``` + +This extracts a string array and converts it to an integer array: + +```sql +SELECT ARRAY( + SELECT CAST(integer_element AS INT64) + FROM UNNEST( + JSON_EXTRACT_ARRAY('[1,2,3]','$') + ) AS integer_element +) AS integer_array; + +/*---------------* + | integer_array | + +---------------+ + | [1, 2, 3] | + *---------------*/ +``` + +This extracts string values in a JSON-formatted string to an array: + +```sql +-- Doesn't strip the double quotes +SELECT JSON_EXTRACT_ARRAY('["apples", "oranges", "grapes"]', '$') AS string_array; + +/*---------------------------------* + | string_array | + +---------------------------------+ + | ["apples", "oranges", "grapes"] | + *---------------------------------*/ + +-- Strips the double quotes +SELECT ARRAY( + SELECT JSON_EXTRACT_SCALAR(string_element, '$') + FROM UNNEST(JSON_EXTRACT_ARRAY('["apples","oranges","grapes"]','$')) AS string_element +) AS string_array; + +/*---------------------------* + | string_array | + +---------------------------+ + | [apples, oranges, grapes] | + *---------------------------*/ +``` + +This extracts only the items in the `fruit` property to an array: + +```sql +SELECT JSON_EXTRACT_ARRAY( + '{"fruit": [{"apples": 5, "oranges": 10}, {"apples": 2, "oranges": 4}], "vegetables": [{"lettuce": 7, "kale": 8}]}', + '$.fruit' +) AS string_array; + +/*-------------------------------------------------------* + | string_array | + +-------------------------------------------------------+ + | [{"apples":5,"oranges":10}, {"apples":2,"oranges":4}] | + *-------------------------------------------------------*/ +``` + +These are equivalent: + +```sql +SELECT JSON_EXTRACT_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$[fruits]') AS string_array; + +SELECT JSON_EXTRACT_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits') AS string_array; + +-- The queries above produce the following result: +/*---------------------------------* + | string_array | + +---------------------------------+ + | ["apples", "oranges", "grapes"] | + *---------------------------------*/ +``` + +In cases where a JSON key uses invalid JSONPath characters, you can escape those +characters using single quotes and brackets, `[' ']`. For example: + +```sql +SELECT JSON_EXTRACT_ARRAY('{"a.b": {"c": ["world"]}}', "$['a.b'].c") AS hello; + +/*-----------* + | hello | + +-----------+ + | ["world"] | + *-----------*/ +``` + +The following examples explore how invalid requests and empty arrays are +handled: + ++ If a JSONPath is invalid, an error is thrown. ++ If a JSON-formatted string is invalid, the output is NULL. ++ It is okay to have empty arrays in the JSON-formatted string. + +```sql +-- An error is thrown if you provide an invalid JSONPath. +SELECT JSON_EXTRACT_ARRAY('["foo", "bar", "baz"]', 'INVALID_JSONPath') AS result; + +-- If the JSONPath does not refer to an array, then NULL is returned. +SELECT JSON_EXTRACT_ARRAY('{"a": "foo"}', '$.a') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a key that does not exist is specified, then the result is NULL. +SELECT JSON_EXTRACT_ARRAY('{"a": "foo"}', '$.b') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- Empty arrays in JSON-formatted strings are supported. +SELECT JSON_EXTRACT_ARRAY('{"a": "foo", "b": []}', '$.b') AS result; + +/*--------* + | result | + +--------+ + | [] | + *--------*/ +``` + +[json-query-array]: #json_query_array + +[JSONPath-format]: #JSONPath_format + +[differences-json-and-string]: #differences_json_and_string + ### `JSON_EXTRACT_SCALAR` Note: This function is deprecated. Consider using [JSON_VALUE][json-value]. @@ -20796,6 +22656,237 @@ SELECT JSON_EXTRACT_SCALAR('{"a.b": {"c": "world"}}', "$['a.b'].c") AS hello; [differences-json-and-string]: #differences_json_and_string +### `JSON_EXTRACT_STRING_ARRAY` + +Note: This function is deprecated. Consider using +[JSON_VALUE_ARRAY][json-value-array]. + +```sql +JSON_EXTRACT_STRING_ARRAY(json_string_expr[, json_path]) +``` + +```sql +JSON_EXTRACT_STRING_ARRAY(json_expr[, json_path]) +``` + +**Description** + +Extracts a JSON array of scalar values and converts it to a SQL `ARRAY` +value. In addition, this function: + ++ Removes the outermost quotes and unescapes the values. ++ Returns a SQL `NULL` if the selected value is not an array or + not an array containing only scalar values. ++ Uses single quotes and brackets to escape invalid [JSONPath][JSONPath-format] + characters in JSON keys. For example: `['a.b']`. + +Arguments: + ++ `json_string_expr`: A JSON-formatted string. For example: + + ``` + '["apples", "oranges", "grapes"]' + ``` ++ `json_expr`: JSON. For example: + + ``` + JSON '["apples", "oranges", "grapes"]' + ``` ++ `json_path`: The [JSONPath][JSONPath-format]. This identifies the data that + you want to obtain from the input. If this optional parameter is not + provided, then the JSONPath `$` symbol is applied, which means that all of + the data is analyzed. + +There are differences between the JSON-formatted string and JSON input types. +For details, see [Differences between the JSON and JSON-formatted STRING types][differences-json-and-string]. + +Caveats: + ++ A JSON `null` in the input array produces a SQL `NULL` as the output for that + JSON `null`. ++ If a JSONPath matches an array that contains scalar objects and a JSON `null`, + then the output is an array of the scalar objects and a SQL `NULL`. + +**Return type** + +`ARRAY` + +**Examples** + +This extracts items in JSON to a string array: + +```sql +SELECT JSON_EXTRACT_STRING_ARRAY( + JSON '{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits' + ) AS string_array; + +/*---------------------------* + | string_array | + +---------------------------+ + | [apples, oranges, grapes] | + *---------------------------*/ +``` + +The following example compares how results are returned for the +`JSON_EXTRACT_ARRAY` and `JSON_EXTRACT_STRING_ARRAY` functions. + +```sql +SELECT JSON_EXTRACT_ARRAY('["apples", "oranges"]') AS json_array, +JSON_EXTRACT_STRING_ARRAY('["apples", "oranges"]') AS string_array; + +/*-----------------------+-------------------* + | json_array | string_array | + +-----------------------+-------------------+ + | ["apples", "oranges"] | [apples, oranges] | + *-----------------------+-------------------*/ +``` + +This extracts the items in a JSON-formatted string to a string array: + +```sql +-- Strips the double quotes +SELECT JSON_EXTRACT_STRING_ARRAY('["foo", "bar", "baz"]', '$') AS string_array; + +/*-----------------* + | string_array | + +-----------------+ + | [foo, bar, baz] | + *-----------------*/ +``` + +This extracts a string array and converts it to an integer array: + +```sql +SELECT ARRAY( + SELECT CAST(integer_element AS INT64) + FROM UNNEST( + JSON_EXTRACT_STRING_ARRAY('[1, 2, 3]', '$') + ) AS integer_element +) AS integer_array; + +/*---------------* + | integer_array | + +---------------+ + | [1, 2, 3] | + *---------------*/ +``` + +These are equivalent: + +```sql +SELECT JSON_EXTRACT_STRING_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$[fruits]') AS string_array; + +SELECT JSON_EXTRACT_STRING_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits') AS string_array; + +-- The queries above produce the following result: +/*---------------------------* + | string_array | + +---------------------------+ + | [apples, oranges, grapes] | + *---------------------------*/ +``` + +In cases where a JSON key uses invalid JSONPath characters, you can escape those +characters using single quotes and brackets: `[' ']`. For example: + +```sql +SELECT JSON_EXTRACT_STRING_ARRAY('{"a.b": {"c": ["world"]}}', "$['a.b'].c") AS hello; + +/*---------* + | hello | + +---------+ + | [world] | + *---------*/ +``` + +The following examples explore how invalid requests and empty arrays are +handled: + +```sql +-- An error is thrown if you provide an invalid JSONPath. +SELECT JSON_EXTRACT_STRING_ARRAY('["foo", "bar", "baz"]', 'INVALID_JSONPath') AS result; + +-- If the JSON formatted string is invalid, then NULL is returned. +SELECT JSON_EXTRACT_STRING_ARRAY('}}', '$') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If the JSON document is NULL, then NULL is returned. +SELECT JSON_EXTRACT_STRING_ARRAY(NULL, '$') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a JSONPath does not match anything, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": ["foo", "bar", "baz"]}', '$.b') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a JSONPath matches an object that is not an array, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": "foo"}', '$') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a JSONPath matches an array of non-scalar objects, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": [{"b": "foo", "c": 1}, {"b": "bar", "c":2}], "d": "baz"}', '$.a') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a JSONPath matches an array of mixed scalar and non-scalar objects, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": [10, {"b": 20}]', '$.a') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a JSONPath matches an empty JSON array, then the output is an empty array instead of NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": "foo", "b": []}', '$.b') AS result; + +/*--------* + | result | + +--------+ + | [] | + *--------*/ + +-- In the following query, the JSON null input is returned as a +-- SQL NULL in the output. +SELECT JSON_EXTRACT_STRING_ARRAY('["world", 1, null]') AS result; + +/*------------------* + | result | + +------------------+ + | [world, 1, NULL] | + *------------------*/ + +``` + +[json-value-array]: #json_value_array + +[JSONPath-format]: #JSONPath_format + +[differences-json-and-string]: #differences_json_and_string + ### `JSON_OBJECT` + [Signature 1](#json_object_signature1): @@ -21113,8 +23204,9 @@ In the following example, JSON data is extracted and returned as JSON. ```sql SELECT - JSON_QUERY(JSON '{"class": {"students": [{"id": 5}, {"id": 12}]}}', '$.class') - AS json_data; + JSON_QUERY( + JSON '{"class": {"students": [{"id": 5}, {"id": 12}]}}', + '$.class') AS json_data; /*-----------------------------------* | json_data | @@ -21127,71 +23219,163 @@ In the following examples, JSON data is extracted and returned as JSON-formatted strings. ```sql -SELECT JSON_QUERY(json_text, '$') AS json_text_string -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY('{"class": {"students": [{"name": "Jane"}]}}', '$') AS json_text_string; /*-----------------------------------------------------------* | json_text_string | +-----------------------------------------------------------+ | {"class":{"students":[{"name":"Jane"}]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT JSON_QUERY('{"class": {"students": []}}', '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"},{"name": "Jamie"}]}}', + '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[{"name":"John"},{"name":"Jamie"}]}} | *-----------------------------------------------------------*/ ``` ```sql -SELECT JSON_QUERY(json_text, '$.class.students[0]') AS first_student -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "Jane"}]}}', + '$.class.students[0]') AS first_student; /*-----------------* | first_student | +-----------------+ | {"name":"Jane"} | + *-----------------*/ +``` + +```sql +SELECT + JSON_QUERY('{"class": {"students": []}}', '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | NULL | + *-----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | {"name":"John"} | *-----------------*/ ``` ```sql -SELECT JSON_QUERY(json_text, '$.class.students[1].name') AS second_student_name -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": null}]}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "Jane"}]}}', + '$.class.students[1].name') AS second_student; /*----------------* | second_student | +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": []}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": null}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | "Jamie" | *----------------*/ ``` ```sql -SELECT JSON_QUERY(json_text, '$.class."students"') AS student_names -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "Jane"}]}}', + '$.class."students"') AS student_names; /*------------------------------------* | student_names | +------------------------------------+ | [{"name":"Jane"}] | + *------------------------------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": []}}', + '$.class."students"') AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [] | + *------------------------------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class."students"') AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [{"name":"John"},{"name":"Jamie"}] | *------------------------------------*/ ``` @@ -21210,6 +23394,8 @@ SELECT JSON_QUERY(JSON '{"a": null}', "$.b"); -- Returns a SQL NULL [differences-json-and-string]: #differences_json_and_string +[JSONPath-mode]: #JSONPath_mode + ### `JSON_QUERY_ARRAY` ```sql @@ -21309,7 +23495,9 @@ SELECT JSON_QUERY_ARRAY('["apples", "oranges", "grapes"]', '$') AS string_array; +---------------------------------+ | ["apples", "oranges", "grapes"] | *---------------------------------*/ +``` +```sql -- Strips the double quotes SELECT ARRAY( SELECT JSON_VALUE(string_element, '$') @@ -25678,53 +27866,35 @@ A JSON-formatted `STRING` **Examples** -Convert rows in a table to JSON-formatted strings. +The following query converts a `STRUCT` value to a JSON-formatted string: ```sql -With CoordinatesTable AS ( - (SELECT 1 AS id, [10, 20] AS coordinates) UNION ALL - (SELECT 2 AS id, [30, 40] AS coordinates) UNION ALL - (SELECT 3 AS id, [50, 60] AS coordinates)) -SELECT id, coordinates, TO_JSON_STRING(t) AS json_data -FROM CoordinatesTable AS t; +SELECT TO_JSON_STRING(STRUCT(1 AS id, [10,20] AS coordinates)) AS json_data -/*----+-------------+--------------------------------* - | id | coordinates | json_data | - +----+-------------+--------------------------------+ - | 1 | [10, 20] | {"id":1,"coordinates":[10,20]} | - | 2 | [30, 40] | {"id":2,"coordinates":[30,40]} | - | 3 | [50, 60] | {"id":3,"coordinates":[50,60]} | - *----+-------------+--------------------------------*/ +/*--------------------------------* + | json_data | + +--------------------------------+ + | {"id":1,"coordinates":[10,20]} | + *--------------------------------*/ ``` -Convert rows in a table to JSON-formatted strings that are easy to read. +The following query converts a `STRUCT` value to a JSON-formatted string that is +easy to read: ```sql -With CoordinatesTable AS ( - (SELECT 1 AS id, [10, 20] AS coordinates) UNION ALL - (SELECT 2 AS id, [30, 40] AS coordinates)) -SELECT id, coordinates, TO_JSON_STRING(t, true) AS json_data -FROM CoordinatesTable AS t; +SELECT TO_JSON_STRING(STRUCT(1 AS id, [10,20] AS coordinates), true) AS json_data -/*----+-------------+--------------------* - | id | coordinates | json_data | - +----+-------------+--------------------+ - | 1 | [10, 20] | { | - | | | "id": 1, | - | | | "coordinates": [ | - | | | 10, | - | | | 20 | - | | | ] | - | | | } | - +----+-------------+--------------------+ - | 2 | [30, 40] | { | - | | | "id": 2, | - | | | "coordinates": [ | - | | | 30, | - | | | 40 | - | | | ] | - | | | } | - *----+-------------+--------------------*/ +/*--------------------* + | json_data | + +--------------------+ + | { | + | "id": 1, | + | "coordinates": [ | + | 10, | + | 20 | + | ] | + | } | + *--------------------*/ ``` [json-encodings]: #json_encodings @@ -26440,6 +28610,27 @@ The following SQL to JSON encodings are supported: + + + + RANGE + +

range

+

+ Encoded as an object with a start and end + value. Any unbounded part of the range is represented as + null. +

+ + + SQL input: RANGE<DATE> '[2024-07-24, 2024-07-25)'
+ JSON output: {"start":"2024-07-24","end":"2024-07-25"}
+
+ SQL input: RANGE<DATETIME> '[2024-07-24 10:00:00, UNBOUNDED)'
+ JSON output: {"start":"2024-07-24T10:00:00","end":null}
+ + + @@ -26447,8 +28638,19 @@ The following SQL to JSON encodings are supported: With the JSONPath format, you can identify the values you want to -obtain from a JSON-formatted string. The JSONPath format supports these -operators: +obtain from a JSON-formatted string. + +If a key in a JSON functions contains a JSON format operator, refer to each +JSON function for how to escape them. + +A JSON function returns `NULL` if the JSONPath format does not match a value in +a JSON-formatted string. If the selected value for a scalar function is not +scalar, such as an object or an array, the function returns `NULL`. If the +JSONPath format is invalid, an error is produced. + +#### Operators for JSONPath + +The JSONPath format supports these operators: @@ -26552,14 +28754,6 @@ operators:
-If a key in a JSON functions contains a JSON format operator, refer to each -JSON function for how to escape them. - -A JSON function returns `NULL` if the JSONPath format does not match a value in -a JSON-formatted string. If the selected value for a scalar function is not -scalar, such as an object or an array, the function returns `NULL`. If the -JSONPath format is invalid, an error is produced. - ### Differences between the JSON and JSON-formatted STRING types @@ -26660,6 +28854,8 @@ FROM t; [JSON-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#json_type +[JSONPath-mode]: #JSONPath_mode + ## Mathematical functions ZetaSQL supports mathematical functions. @@ -26752,6 +28948,9 @@ All mathematical functions have the following behaviors: + + + COSINE_DISTANCE   EUCLIDEAN_DISTANCE   @@ -29510,7 +31709,7 @@ GROUP BY 1 ### `ROUND` ``` -ROUND(X [, N]) +ROUND(X [, N [, rounding_mode]]) ``` **Description** @@ -29520,6 +31719,18 @@ rounds X to N decimal places after the decimal point. If N is negative, rounds off digits to the left of the decimal point. Rounds halfway cases away from zero. Generates an error if overflow occurs. +If X is a `NUMERIC` or `BIGNUMERIC` type, then you can +explicitly set `rounding_mode` +to one of the following: + ++ [`"ROUND_HALF_AWAY_FROM_ZERO"`][round-half-away-from-zero]: (Default) Rounds + halfway cases away from zero. ++ [`"ROUND_HALF_EVEN"`][round-half-even]: Rounds halfway cases + towards the nearest even digit. + +If you set the `rounding_mode` and X is not a `NUMERIC` or `BIGNUMERIC` type, +then the function generates an error. + @@ -29580,6 +31791,30 @@ away from zero. Generates an error if overflow occurs. + + + + + + + + + + + + + + + + + + + + + + + +
ROUND(1.235, 2) 1.24
ROUND(NUMERIC "2.25", 1, "ROUND_HALF_EVEN")2.2
ROUND(NUMERIC "2.35", 1, "ROUND_HALF_EVEN")2.4
ROUND(NUMERIC "2.251", 1, "ROUND_HALF_EVEN")2.3
ROUND(NUMERIC "-2.5", 0, "ROUND_HALF_EVEN")-2
ROUND(NUMERIC "2.5", 0, "ROUND_HALF_AWAY_FROM_ZERO")3
ROUND(NUMERIC "-2.5", 0, "ROUND_HALF_AWAY_FROM_ZERO")-3
@@ -29598,6 +31833,10 @@ away from zero. Generates an error if overflow occurs. +[round-half-away-from-zero]: https://en.wikipedia.org/wiki/Rounding#Rounding_half_away_from_zero + +[round-half-even]: https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even + ### `SAFE_ADD` ``` @@ -35215,10 +37454,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -35386,10 +37630,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -35506,10 +37755,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -35732,10 +37986,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -36159,6 +38418,15 @@ canonical equivalence. + + REGEXP_SUBSTR + + + + Synonym for REGEXP_EXTRACT. + + + REPEAT @@ -36413,21 +38681,23 @@ regardless of whether the value is a `STRING` or `BYTES` type. **Examples** ```sql -WITH example AS - (SELECT 'абвгд' AS characters, b'абвгд' AS bytes) +SELECT BYTE_LENGTH('абвгд') AS string_example; -SELECT - characters, - BYTE_LENGTH(characters) AS string_example, - bytes, - BYTE_LENGTH(bytes) AS bytes_example -FROM example; +/*----------------* + | string_example | + +----------------+ + | 10 | + *----------------*/ +``` -/*------------+----------------+-------+---------------* - | characters | string_example | bytes | bytes_example | - +------------+----------------+-------+---------------+ - | абвгд | 10 | абвгд | 10 | - *------------+----------------+-------+---------------*/ +```sql +SELECT BYTE_LENGTH(b'абвгд') AS bytes_example; + +/*----------------* + | bytes_example | + +----------------+ + | 10 | + *----------------*/ ``` ### `CHAR_LENGTH` @@ -36447,19 +38717,13 @@ Gets the number of characters in a `STRING` value. **Examples** ```sql -WITH example AS - (SELECT 'абвгд' AS characters) +SELECT CHAR_LENGTH('абвгд') AS char_length; -SELECT - characters, - CHAR_LENGTH(characters) AS char_length_example -FROM example; - -/*------------+---------------------* - | characters | char_length_example | - +------------+---------------------+ - | абвгд | 5 | - *------------+---------------------*/ +/*-------------* + | char_length | + +-------------+ + | 5 | + *------------ */ ``` ### `CHARACTER_LENGTH` @@ -36479,13 +38743,9 @@ Synonym for [CHAR_LENGTH][string-link-to-char-length]. **Examples** ```sql -WITH example AS - (SELECT 'абвгд' AS characters) - SELECT - characters, - CHARACTER_LENGTH(characters) AS char_length_example -FROM example; + 'абвгд' AS characters, + CHARACTER_LENGTH('абвгд') AS char_length_example /*------------+---------------------* | characters | char_length_example | @@ -36946,23 +39206,12 @@ This function supports specifying [collation][collation]. **Examples** ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - ENDS_WITH(item, 'e') as example -FROM items; +SELECT ENDS_WITH('apple', 'e') as example /*---------* | example | +---------+ | True | - | False | - | True | *---------*/ ``` @@ -37557,6 +39806,8 @@ JSON ' + + ##### %t and %T behavior @@ -37941,40 +40192,27 @@ If `value` or `delimiters` is `NULL`, the function returns `NULL`. **Examples** ```sql -WITH example AS -( - SELECT 'Hello World-everyone!' AS value UNION ALL - SELECT 'tHe dog BARKS loudly+friendly' AS value UNION ALL - SELECT 'apples&oranges;&pears' AS value UNION ALL - SELECT 'καθίσματα ταινιών' AS value -) -SELECT value, INITCAP(value) AS initcap_value FROM example +SELECT + 'Hello World-everyone!' AS value, + INITCAP('Hello World-everyone!') AS initcap_value /*-------------------------------+-------------------------------* | value | initcap_value | +-------------------------------+-------------------------------+ | Hello World-everyone! | Hello World-Everyone! | - | tHe dog BARKS loudly+friendly | The Dog Barks Loudly+Friendly | - | apples&oranges;&pears | Apples&Oranges;&Pears | - | καθίσματα ταινιών | Καθίσματα Ταινιών | *-------------------------------+-------------------------------*/ +``` -WITH example AS -( - SELECT 'hello WORLD!' AS value, '' AS delimiters UNION ALL - SELECT 'καθίσματα ταιντιώ@ν' AS value, 'τ@' AS delimiters UNION ALL - SELECT 'Apples1oranges2pears' AS value, '12' AS delimiters UNION ALL - SELECT 'tHisEisEaESentence' AS value, 'E' AS delimiters -) -SELECT value, delimiters, INITCAP(value, delimiters) AS initcap_value FROM example; +```sql +SELECT + 'Apples1oranges2pears' as value, + '12' AS delimiters, + INITCAP('Apples1oranges2pears' , '12') AS initcap_value /*----------------------+------------+----------------------* | value | delimiters | initcap_value | +----------------------+------------+----------------------+ - | hello WORLD! | | Hello world! | - | καθίσματα ταιντιώ@ν | τ@ | ΚαθίσματΑ τΑιντΙώ@Ν | | Apples1oranges2pears | 12 | Apples1Oranges2Pears | - | tHisEisEaESentence | E | ThisEIsEAESentence | *----------------------+------------+----------------------*/ ``` @@ -38028,41 +40266,109 @@ Returns an error if: **Examples** ```sql -WITH example AS -(SELECT 'banana' as value, 'an' as subvalue, 1 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, 1 as position, 2 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, 1 as position, 3 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, 3 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, -1 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, -3 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'ann' as subvalue, 1 as position, 1 as -occurrence UNION ALL -SELECT 'helloooo' as value, 'oo' as subvalue, 1 as position, 1 as -occurrence UNION ALL -SELECT 'helloooo' as value, 'oo' as subvalue, 1 as position, 2 as -occurrence -) -SELECT value, subvalue, position, occurrence, INSTR(value, -subvalue, position, occurrence) AS instr -FROM example; +SELECT + 'banana' AS value, 'an' AS subvalue, 1 AS position, 1 AS occurrence, + INSTR('banana', 'an', 1, 1) AS instr; /*--------------+--------------+----------+------------+-------* | value | subvalue | position | occurrence | instr | +--------------+--------------+----------+------------+-------+ | banana | an | 1 | 1 | 2 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, 1 AS position, 2 AS occurrence, + INSTR('banana', 'an', 1, 2) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | 1 | 2 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, 1 AS position, 3 AS occurrence, + INSTR('banana', 'an', 1, 3) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | 1 | 3 | 0 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, 3 AS position, 1 AS occurrence, + INSTR('banana', 'an', 3, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | 3 | 1 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, -1 AS position, 1 AS occurrence, + INSTR('banana', 'an', -1, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | -1 | 1 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, -3 AS position, 1 AS occurrence, + INSTR('banana', 'an', -3, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | -3 | 1 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'ann' AS subvalue, 1 AS position, 1 AS occurrence, + INSTR('banana', 'ann', 1, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | ann | 1 | 1 | 0 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'helloooo' AS value, 'oo' AS subvalue, 1 AS position, 1 AS occurrence, + INSTR('helloooo', 'oo', 1, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | helloooo | oo | 1 | 1 | 5 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'helloooo' AS value, 'oo' AS subvalue, 1 AS position, 2 AS occurrence, + INSTR('helloooo', 'oo', 1, 2) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | helloooo | oo | 1 | 2 | 6 | *--------------+--------------+----------+------------+-------*/ ``` @@ -38094,43 +40400,23 @@ will be returned. **Examples** ```sql -WITH examples AS -(SELECT 'apple' as example -UNION ALL -SELECT 'banana' as example -UNION ALL -SELECT 'абвгд' as example -) -SELECT example, LEFT(example, 3) AS left_example -FROM examples; +SELECT LEFT('banana', 3) AS results -/*---------+--------------* - | example | left_example | - +---------+--------------+ - | apple | app | - | banana | ban | - | абвгд | абв | - *---------+--------------*/ +/*---------* + | results | + +--------+ + | ban | + *---------*/ ``` ```sql -WITH examples AS -(SELECT b'apple' as example -UNION ALL -SELECT b'banana' as example -UNION ALL -SELECT b'\xab\xcd\xef\xaa\xbb' as example -) -SELECT example, LEFT(example, 3) AS left_example -FROM examples; +SELECT LEFT(b'\xab\xcd\xef\xaa\xbb', 3) AS results -/*----------------------+--------------* - | example | left_example | - +----------------------+--------------+ - | apple | app | - | banana | ban | - | \xab\xcd\xef\xaa\xbb | \xab\xcd\xef | - *----------------------+--------------*/ +/*--------------* + | results | + +--------------+ + | \xab\xcd\xef | + *--------------*/ ``` ### `LENGTH` @@ -38152,21 +40438,15 @@ argument. **Examples** ```sql - -WITH example AS - (SELECT 'абвгд' AS characters) - SELECT - characters, - LENGTH(characters) AS string_example, - LENGTH(CAST(characters AS BYTES)) AS bytes_example -FROM example; + LENGTH('абвгд') AS string_example, + LENGTH(CAST('абвгд' AS BYTES)) AS bytes_example; -/*------------+----------------+---------------* - | characters | string_example | bytes_example | - +------------+----------------+---------------+ - | абвгд | 5 | 10 | - *------------+----------------+---------------*/ +/*----------------+---------------* + | string_example | bytes_example | + +----------------+---------------+ + | 5 | 10 | + *----------------+---------------*/ ``` ### `LOWER` @@ -38193,28 +40473,15 @@ greater than 127 left intact. **Examples** ```sql - -WITH items AS - (SELECT - 'FOO' as item - UNION ALL - SELECT - 'BAR' as item - UNION ALL - SELECT - 'BAZ' as item) - SELECT - LOWER(item) AS example + LOWER('FOO BAR BAZ') AS example FROM items; -/*---------* - | example | - +---------+ - | foo | - | bar | - | baz | - *---------*/ +/*-------------* + | example | + +-------------+ + | foo bar baz | + *-------------*/ ``` [string-link-to-unicode-character-definitions]: http://unicode.org/ucd/ @@ -38256,72 +40523,53 @@ This function returns an error if: **Examples** ```sql -SELECT t, len, FORMAT('%T', LPAD(t, len)) AS LPAD FROM UNNEST([ - STRUCT('abc' AS t, 5 AS len), - ('abc', 2), - ('例子', 4) -]); +SELECT FORMAT('%T', LPAD('c', 5)) AS results -/*------+-----+----------* - | t | len | LPAD | - |------|-----|----------| - | abc | 5 | " abc" | - | abc | 2 | "ab" | - | 例子 | 4 | " 例子" | - *------+-----+----------*/ +/*---------* + | results | + +---------+ + | " c" | + *---------*/ ``` ```sql -SELECT t, len, pattern, FORMAT('%T', LPAD(t, len, pattern)) AS LPAD FROM UNNEST([ - STRUCT('abc' AS t, 8 AS len, 'def' AS pattern), - ('abc', 5, '-'), - ('例子', 5, '中文') -]); +SELECT LPAD('b', 5, 'a') AS results -/*------+-----+---------+--------------* - | t | len | pattern | LPAD | - |------|-----|---------|--------------| - | abc | 8 | def | "defdeabc" | - | abc | 5 | - | "--abc" | - | 例子 | 5 | 中文 | "中文中例子" | - *------+-----+---------+--------------*/ +/*---------* + | results | + +---------+ + | aaaab | + *---------*/ ``` ```sql -SELECT FORMAT('%T', t) AS t, len, FORMAT('%T', LPAD(t, len)) AS LPAD FROM UNNEST([ - STRUCT(b'abc' AS t, 5 AS len), - (b'abc', 2), - (b'\xab\xcd\xef', 4) -]); +SELECT LPAD('abc', 10, 'ghd') AS results -/*-----------------+-----+------------------* - | t | len | LPAD | - |-----------------|-----|------------------| - | b"abc" | 5 | b" abc" | - | b"abc" | 2 | b"ab" | - | b"\xab\xcd\xef" | 4 | b" \xab\xcd\xef" | - *-----------------+-----+------------------*/ +/*------------* + | results | + +------------+ + | ghdghdgabc | + *------------*/ ``` ```sql -SELECT - FORMAT('%T', t) AS t, - len, - FORMAT('%T', pattern) AS pattern, - FORMAT('%T', LPAD(t, len, pattern)) AS LPAD -FROM UNNEST([ - STRUCT(b'abc' AS t, 8 AS len, b'def' AS pattern), - (b'abc', 5, b'-'), - (b'\xab\xcd\xef', 5, b'\x00') -]); +SELECT LPAD('abc', 2, 'd') AS results + +/*---------* + | results | + +---------+ + | ab | + *---------*/ +``` + +```sql +SELECT FORMAT('%T', LPAD(b'abc', 10, b'ghd')) AS results -/*-----------------+-----+---------+-------------------------* - | t | len | pattern | LPAD | - |-----------------|-----|---------|-------------------------| - | b"abc" | 8 | b"def" | b"defdeabc" | - | b"abc" | 5 | b"-" | b"--abc" | - | b"\xab\xcd\xef" | 5 | b"\x00" | b"\x00\x00\xab\xcd\xef" | - *-----------------+-----+---------+-------------------------*/ +/*---------------* + | results | + +---------------+ + | b"ghdghdgabc" | + *---------------*/ ``` ### `LTRIM` @@ -38341,68 +40589,32 @@ Identical to [TRIM][string-link-to-trim], but only removes leading characters. **Examples** ```sql -WITH items AS - (SELECT ' apple ' as item - UNION ALL - SELECT ' banana ' as item - UNION ALL - SELECT ' orange ' as item) - -SELECT - CONCAT('#', LTRIM(item), '#') as example -FROM items; +SELECT CONCAT('#', LTRIM(' apple '), '#') AS example /*-------------* | example | +-------------+ - | #apple # | - | #banana # | - | #orange # | + | #apple # | *-------------*/ ``` ```sql -WITH items AS - (SELECT '***apple***' as item - UNION ALL - SELECT '***banana***' as item - UNION ALL - SELECT '***orange***' as item) - -SELECT - LTRIM(item, '*') as example -FROM items; +SELECT LTRIM('***apple***', '*') AS example /*-----------* | example | +-----------+ | apple*** | - | banana*** | - | orange*** | *-----------*/ ``` ```sql -WITH items AS - (SELECT 'xxxapplexxx' as item - UNION ALL - SELECT 'yyybananayyy' as item - UNION ALL - SELECT 'zzzorangezzz' as item - UNION ALL - SELECT 'xyzpearxyz' as item) - -SELECT - LTRIM(item, 'xyz') as example -FROM items; +SELECT LTRIM('xxxapplexxx', 'xyz') AS example /*-----------* | example | +-----------+ | applexxx | - | bananayyy | - | orangezzz | - | pearxyz | *-----------*/ ``` @@ -38439,40 +40651,60 @@ points. **Examples** +The following example normalizes different language characters: + ```sql -SELECT a, b, a = b as normalized -FROM (SELECT NORMALIZE('\u00ea') as a, NORMALIZE('\u0065\u0302') as b); +SELECT + NORMALIZE('\u00ea') as a, + NORMALIZE('\u0065\u0302') as b, + NORMALIZE('\u00ea') = NORMALIZE('\u0065\u0302') as normalized; /*---+---+------------* | a | b | normalized | +---+---+------------+ - | ê | ê | true | + | ê | ê | TRUE | *---+---+------------*/ ``` -The following example normalizes different space characters. +The following examples normalize different space characters: ```sql -WITH EquivalentNames AS ( - SELECT name - FROM UNNEST([ - 'Jane\u2004Doe', - 'John\u2004Smith', - 'Jane\u2005Doe', - 'Jane\u2006Doe', - 'John Smith']) AS name -) -SELECT - NORMALIZE(name, NFKC) AS normalized_name, - COUNT(*) AS name_count -FROM EquivalentNames -GROUP BY 1; +SELECT NORMALIZE('Raha\u2004Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ +``` + +```sql +SELECT NORMALIZE('Raha\u2005Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ +``` + +```sql +SELECT NORMALIZE('Raha\u2006Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ +``` + +```sql +SELECT NORMALIZE('Raha Mahan', NFKC) AS normalized_name -/*-----------------+------------* - | normalized_name | name_count | - +-----------------+------------+ - | John Smith | 2 | - | Jane Doe | 3 | - *-----------------+------------*/ +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ ``` [string-link-to-normalization-wikipedia]: https://en.wikipedia.org/wiki/Unicode_equivalence#Normalization @@ -38515,34 +40747,45 @@ considered, use `NORMALIZE_AND_CASEFOLD`, otherwise use ```sql SELECT - a, b, - NORMALIZE(a) = NORMALIZE(b) as normalized, - NORMALIZE_AND_CASEFOLD(a) = NORMALIZE_AND_CASEFOLD(b) as normalized_with_case_folding -FROM (SELECT 'The red barn' AS a, 'The Red Barn' AS b); + NORMALIZE('The red barn') = NORMALIZE('The Red Barn') AS normalized, + NORMALIZE_AND_CASEFOLD('The red barn') + = NORMALIZE_AND_CASEFOLD('The Red Barn') AS normalized_with_case_folding; -/*--------------+--------------+------------+------------------------------* - | a | b | normalized | normalized_with_case_folding | - +--------------+--------------+------------+------------------------------+ - | The red barn | The Red Barn | false | true | - *--------------+--------------+------------+------------------------------*/ +/*------------+------------------------------* + | normalized | normalized_with_case_folding | + +------------+------------------------------+ + | FALSE | TRUE | + *------------+------------------------------*/ ``` ```sql -WITH Strings AS ( - SELECT '\u2168' AS a, 'IX' AS b UNION ALL - SELECT '\u0041\u030A', '\u00C5' -) -SELECT a, b, - NORMALIZE_AND_CASEFOLD(a, NFD)=NORMALIZE_AND_CASEFOLD(b, NFD) AS nfd, - NORMALIZE_AND_CASEFOLD(a, NFC)=NORMALIZE_AND_CASEFOLD(b, NFC) AS nfc, - NORMALIZE_AND_CASEFOLD(a, NFKD)=NORMALIZE_AND_CASEFOLD(b, NFKD) AS nkfd, - NORMALIZE_AND_CASEFOLD(a, NFKC)=NORMALIZE_AND_CASEFOLD(b, NFKC) AS nkfc -FROM Strings; +SELECT + '\u2168' AS a, + 'IX' AS b, + NORMALIZE_AND_CASEFOLD('\u2168', NFD)=NORMALIZE_AND_CASEFOLD('IX', NFD) AS nfd, + NORMALIZE_AND_CASEFOLD('\u2168', NFC)=NORMALIZE_AND_CASEFOLD('IX', NFC) AS nfc, + NORMALIZE_AND_CASEFOLD('\u2168', NFKD)=NORMALIZE_AND_CASEFOLD('IX', NFKD) AS nkfd, + NORMALIZE_AND_CASEFOLD('\u2168', NFKC)=NORMALIZE_AND_CASEFOLD('IX', NFKC) AS nkfc; /*---+----+-------+-------+------+------* | a | b | nfd | nfc | nkfd | nkfc | +---+----+-------+-------+------+------+ | Ⅸ | IX | false | false | true | true | + *---+----+-------+-------+------+------*/ +``` + +```sql +SELECT + '\u0041\u030A' AS a, + '\u00C5' AS b, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFD)=NORMALIZE_AND_CASEFOLD('\u00C5', NFD) AS nfd, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFC)=NORMALIZE_AND_CASEFOLD('\u00C5', NFC) AS nfc, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFKD)=NORMALIZE_AND_CASEFOLD('\u00C5', NFKD) AS nkfd, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFKC)=NORMALIZE_AND_CASEFOLD('\u00C5', NFKC) AS nkfc; + +/*---+----+-------+-------+------+------* + | a | b | nfd | nfc | nkfd | nkfc | + +---+----+-------+-------+------+------+ | Å | Å | true | true | true | true | *---+----+-------+-------+------+------*/ ``` @@ -38590,46 +40833,98 @@ regular expression syntax. **Examples** +The following queries check to see if an email is valid: + ```sql SELECT - email, - REGEXP_CONTAINS(email, r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid -FROM - (SELECT - ['foo@example.com', 'bar@example.org', 'www.example.net'] - AS addresses), - UNNEST(addresses) AS email; + 'foo@example.com' AS email, + REGEXP_CONTAINS('foo@example.com', r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid /*-----------------+----------* | email | is_valid | +-----------------+----------+ - | foo@example.com | true | - | bar@example.org | true | - | www.example.net | false | + | foo@example.com | TRUE | *-----------------+----------*/ + ``` --- Performs a full match, using ^ and $. Due to regular expression operator --- precedence, it is good practice to use parentheses around everything between ^ --- and $. + ```sql SELECT - email, - REGEXP_CONTAINS(email, r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') - AS valid_email_address, - REGEXP_CONTAINS(email, r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') - AS without_parentheses -FROM - (SELECT - ['a@foo.com', 'a@foo.computer', 'b@bar.org', '!b@bar.org', 'c@buz.net'] - AS addresses), - UNNEST(addresses) AS email; + 'www.example.net' AS email, + REGEXP_CONTAINS('www.example.net', r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid + +/*-----------------+----------* + | email | is_valid | + +-----------------+----------+ + | www.example.net | FALSE | + *-----------------+----------*/ + ``` + +The following queries check to see if an email is valid. They +perform a full match, using `^` and `$`. Due to regular expression operator +precedence, it is good practice to use parentheses around everything between `^` +and `$`. + +```sql +SELECT + 'a@foo.com' AS email, + REGEXP_CONTAINS('a@foo.com', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('a@foo.com', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; /*----------------+---------------------+---------------------* | email | valid_email_address | without_parentheses | +----------------+---------------------+---------------------+ | a@foo.com | true | true | + *----------------+---------------------+---------------------*/ +``` + +```sql +SELECT + 'a@foo.computer' AS email, + REGEXP_CONTAINS('a@foo.computer', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('a@foo.computer', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; + +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ | a@foo.computer | false | true | + *----------------+---------------------+---------------------*/ +``` + +```sql +SELECT + 'b@bar.org' AS email, + REGEXP_CONTAINS('b@bar.org', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('b@bar.org', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; + +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ | b@bar.org | true | true | + *----------------+---------------------+---------------------*/ +``` + +```sql +SELECT + '!b@bar.org' AS email, + REGEXP_CONTAINS('!b@bar.org', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('!b@bar.org', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; + +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ | !b@bar.org | false | true | + *----------------+---------------------+---------------------*/ +``` + +```sql +SELECT + 'c@buz.net' AS email, + REGEXP_CONTAINS('c@buz.net', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('c@buz.net', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; + +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ | c@buz.net | false | false | *----------------+---------------------+---------------------*/ ``` @@ -38639,23 +40934,36 @@ FROM ### `REGEXP_EXTRACT` ```sql -REGEXP_EXTRACT(value, regexp) +REGEXP_EXTRACT(value, regexp[, position[, occurrence]]) ``` **Description** -Returns the first substring in `value` that matches the -[re2 regular expression][string-link-to-re2], -`regexp`. Returns `NULL` if there is no match. +Returns the substring in `value` that matches the +[re2 regular expression][string-link-to-re2], `regexp`. +Returns `NULL` if there is no match. If the regular expression contains a capturing group (`(...)`), and there is a match for that capturing group, that match is returned. If there are multiple matches for a capturing group, the first match is returned. +If `position` is specified, the search starts at this +position in `value`, otherwise it starts at the beginning of `value`. The +`position` must be a positive integer and cannot be 0. If `position` is greater +than the length of `value`, `NULL` is returned. + +If `occurrence` is specified, the search returns a specific occurrence of the +`regexp` in `value`, otherwise returns the first match. If `occurrence` is +greater than the number of matches found, `NULL` is returned. For +`occurrence` > 1, the function searches for additional occurrences beginning +with the character following the previous occurrence. + Returns an error if: + The regular expression is invalid + The regular expression has more than one capturing group ++ The `position` is not a positive integer ++ The `occurrence` is not a positive integer **Return type** @@ -38664,67 +40972,72 @@ Returns an error if: **Examples** ```sql -WITH email_addresses AS - (SELECT 'foo@example.com' as email - UNION ALL - SELECT 'bar@example.org' as email - UNION ALL - SELECT 'baz@example.net' as email) - -SELECT - REGEXP_EXTRACT(email, r'^[a-zA-Z0-9_.+-]+') - AS user_name -FROM email_addresses; +SELECT REGEXP_EXTRACT('foo@example.com', r'^[a-zA-Z0-9_.+-]+') AS user_name /*-----------* | user_name | +-----------+ | foo | - | bar | - | baz | *-----------*/ ``` ```sql -WITH email_addresses AS - (SELECT 'foo@example.com' as email - UNION ALL - SELECT 'bar@example.org' as email - UNION ALL - SELECT 'baz@example.net' as email) - -SELECT - REGEXP_EXTRACT(email, r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.([a-zA-Z0-9-.]+$)') - AS top_level_domain -FROM email_addresses; +SELECT REGEXP_EXTRACT('foo@example.com', r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.([a-zA-Z0-9-.]+$)') /*------------------* | top_level_domain | +------------------+ | com | - | org | - | net | *------------------*/ ``` ```sql -WITH - characters AS ( - SELECT 'ab' AS value, '.b' AS regex UNION ALL - SELECT 'ab' AS value, '(.)b' AS regex UNION ALL - SELECT 'xyztb' AS value, '(.)+b' AS regex UNION ALL - SELECT 'ab' AS value, '(z)?b' AS regex - ) -SELECT value, regex, REGEXP_EXTRACT(value, regex) AS result FROM characters; +SELECT + REGEXP_EXTRACT('ab', '.b') AS result_a, + REGEXP_EXTRACT('ab', '(.)b') AS result_b, + REGEXP_EXTRACT('xyztb', '(.)+b') AS result_c, + REGEXP_EXTRACT('ab', '(z)?b') AS result_d -/*-------+---------+----------* - | value | regex | result | - +-------+---------+----------+ - | ab | .b | ab | - | ab | (.)b | a | - | xyztb | (.)+b | t | - | ab | (z)?b | NULL | - *-------+---------+----------*/ +/*-------------------------------------------* + | result_a | result_b | result_c | result_d | + +-------------------------------------------+ + | ab | a | t | NULL | + *-------------------------------------------*/ +``` + +```sql +WITH example AS +(SELECT 'Hello Helloo and Hellooo' AS value, 'H?ello+' AS regex, 1 as position, +1 AS occurrence UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 1, 2 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 1, 3 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 1, 4 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 2, 1 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 3, 1 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 3, 2 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 3, 3 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 20, 1 UNION ALL +SELECT 'cats&dogs&rabbits' ,'\\w+&', 1, 2 UNION ALL +SELECT 'cats&dogs&rabbits', '\\w+&', 2, 3 +) +SELECT value, regex, position, occurrence, REGEXP_EXTRACT(value, regex, +position, occurrence) AS regexp_value FROM example; + +/*--------------------------+---------+----------+------------+--------------* + | value | regex | position | occurrence | regexp_value | + +--------------------------+---------+----------+------------+--------------+ + | Hello Helloo and Hellooo | H?ello+ | 1 | 1 | Hello | + | Hello Helloo and Hellooo | H?ello+ | 1 | 2 | Helloo | + | Hello Helloo and Hellooo | H?ello+ | 1 | 3 | Hellooo | + | Hello Helloo and Hellooo | H?ello+ | 1 | 4 | NULL | + | Hello Helloo and Hellooo | H?ello+ | 2 | 1 | ello | + | Hello Helloo and Hellooo | H?ello+ | 3 | 1 | Helloo | + | Hello Helloo and Hellooo | H?ello+ | 3 | 2 | Hellooo | + | Hello Helloo and Hellooo | H?ello+ | 3 | 3 | NULL | + | Hello Helloo and Hellooo | H?ello+ | 20 | 1 | NULL | + | cats&dogs&rabbits | \w+& | 1 | 2 | dogs& | + | cats&dogs&rabbits | \w+& | 2 | 3 | NULL | + *--------------------------+---------+----------+------------+--------------*/ ``` [string-link-to-re2]: https://github.com/google/re2/wiki/Syntax @@ -38760,18 +41073,13 @@ Returns an error if: **Examples** ```sql -WITH code_markdown AS - (SELECT 'Try `function(x)` or `function(y)`' as code) - -SELECT - REGEXP_EXTRACT_ALL(code, '`(.+?)`') AS example -FROM code_markdown; +SELECT REGEXP_EXTRACT_ALL('Try `func(x)` or `func(y)`', '`(.+?)`') AS example -/*----------------------------* - | example | - +----------------------------+ - | [function(x), function(y)] | - *----------------------------*/ +/*--------------------* + | example | + +--------------------+ + | [func(x), func(y)] | + *--------------------*/ ``` [string-link-to-re2]: https://github.com/google/re2/wiki/Syntax @@ -38833,81 +41141,56 @@ Returns an error if: **Examples** ```sql -WITH example AS ( - SELECT 'ab@cd-ef' AS source_value, '@[^-]*' AS regexp UNION ALL - SELECT 'ab@d-ef', '@[^-]*' UNION ALL - SELECT 'abc@cd-ef', '@[^-]*' UNION ALL - SELECT 'abc-ef', '@[^-]*') -SELECT source_value, regexp, REGEXP_INSTR(source_value, regexp) AS instr -FROM example; +SELECT + REGEXP_INSTR('ab@cd-ef', '@[^-]*') AS instr_a, + REGEXP_INSTR('ab@d-ef', '@[^-]*') AS instr_b, + REGEXP_INSTR('abc@cd-ef', '@[^-]*') AS instr_c, + REGEXP_INSTR('abc-ef', '@[^-]*') AS instr_d, -/*--------------+--------+-------* - | source_value | regexp | instr | - +--------------+--------+-------+ - | ab@cd-ef | @[^-]* | 3 | - | ab@d-ef | @[^-]* | 3 | - | abc@cd-ef | @[^-]* | 4 | - | abc-ef | @[^-]* | 0 | - *--------------+--------+-------*/ +/*---------------------------------------* + | instr_a | instr_b | instr_c | instr_d | + +---------------------------------------+ + | 3 | 3 | 4 | 0 | + *---------------------------------------*/ ``` ```sql -WITH example AS ( - SELECT 'a@cd-ef b@cd-ef' AS source_value, '@[^-]*' AS regexp, 1 AS position UNION ALL - SELECT 'a@cd-ef b@cd-ef', '@[^-]*', 2 UNION ALL - SELECT 'a@cd-ef b@cd-ef', '@[^-]*', 3 UNION ALL - SELECT 'a@cd-ef b@cd-ef', '@[^-]*', 4) SELECT - source_value, regexp, position, - REGEXP_INSTR(source_value, regexp, position) AS instr -FROM example; + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 1) AS instr_a, + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 2) AS instr_b, + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 3) AS instr_c, + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 4) AS instr_d, -/*-----------------+--------+----------+-------* - | source_value | regexp | position | instr | - +-----------------+--------+----------+-------+ - | a@cd-ef b@cd-ef | @[^-]* | 1 | 2 | - | a@cd-ef b@cd-ef | @[^-]* | 2 | 2 | - | a@cd-ef b@cd-ef | @[^-]* | 3 | 10 | - | a@cd-ef b@cd-ef | @[^-]* | 4 | 10 | - *-----------------+--------+----------+-------*/ +/*---------------------------------------* + | instr_a | instr_b | instr_c | instr_d | + +---------------------------------------+ + | 2 | 2 | 10 | 10 | + *---------------------------------------*/ ``` ```sql -WITH example AS ( - SELECT 'a@cd-ef b@cd-ef c@cd-ef' AS source_value, - '@[^-]*' AS regexp, 1 AS position, 1 AS occurrence UNION ALL - SELECT 'a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 2 UNION ALL - SELECT 'a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 3) SELECT - source_value, regexp, position, occurrence, - REGEXP_INSTR(source_value, regexp, position, occurrence) AS instr -FROM example; + REGEXP_INSTR('a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 1) AS instr_a, + REGEXP_INSTR('a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 2) AS instr_b, + REGEXP_INSTR('a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 3) AS instr_c -/*-------------------------+--------+----------+------------+-------* - | source_value | regexp | position | occurrence | instr | - +-------------------------+--------+----------+------------+-------+ - | a@cd-ef b@cd-ef c@cd-ef | @[^-]* | 1 | 1 | 2 | - | a@cd-ef b@cd-ef c@cd-ef | @[^-]* | 1 | 2 | 10 | - | a@cd-ef b@cd-ef c@cd-ef | @[^-]* | 1 | 3 | 18 | - *-------------------------+--------+----------+------------+-------*/ +/*-----------------------------* + | instr_a | instr_b | instr_c | + +-----------------------------+ + | 2 | 10 | 18 | + *-----------------------------*/ ``` ```sql -WITH example AS ( - SELECT 'a@cd-ef' AS source_value, '@[^-]*' AS regexp, - 1 AS position, 1 AS occurrence, 0 AS o_position UNION ALL - SELECT 'a@cd-ef', '@[^-]*', 1, 1, 1) SELECT - source_value, regexp, position, occurrence, o_position, - REGEXP_INSTR(source_value, regexp, position, occurrence, o_position) AS instr -FROM example; + REGEXP_INSTR('a@cd-ef', '@[^-]*', 1, 1, 0) AS instr_a, + REGEXP_INSTR('a@cd-ef', '@[^-]*', 1, 1, 1) AS instr_b -/*--------------+--------+----------+------------+------------+-------* - | source_value | regexp | position | occurrence | o_position | instr | - +--------------+--------+----------+------------+------------+-------+ - | a@cd-ef | @[^-]* | 1 | 1 | 0 | 2 | - | a@cd-ef | @[^-]* | 1 | 1 | 1 | 5 | - *--------------+--------+----------+------------+------------+-------*/ +/*-------------------* + | instr_a | instr_b | + +-------------------+ + | 2 | 5 | + *-------------------*/ ``` ### `REGEXP_MATCH` (Deprecated) @@ -39003,21 +41286,12 @@ regular expression syntax. **Examples** ```sql -WITH markdown AS - (SELECT '# Heading' as heading - UNION ALL - SELECT '# Another heading' as heading) - -SELECT - REGEXP_REPLACE(heading, r'^# ([a-zA-Z0-9\s]+$)', '

\\1

') - AS html -FROM markdown; +SELECT REGEXP_REPLACE('# Heading', r'^# ([a-zA-Z0-9\s]+$)', '

\\1

') AS html /*--------------------------* | html | +--------------------------+ |

Heading

| - |

Another heading

| *--------------------------*/ ``` @@ -39025,6 +41299,39 @@ FROM markdown; [string-link-to-lexical-literals]: https://github.com/google/zetasql/blob/master/docs/lexical.md#string_and_bytes_literals +### `REGEXP_SUBSTR` + +```sql +REGEXP_SUBSTR(value, regexp[, position[, occurrence]]) +``` + +**Description** + +Synonym for [REGEXP_EXTRACT][string-link-to-regex]. + +**Return type** + +`STRING` or `BYTES` + +**Examples** + +```sql +WITH example AS +(SELECT 'Hello World Helloo' AS value, 'H?ello+' AS regex, 1 AS position, 1 AS +occurrence +) +SELECT value, regex, position, occurrence, REGEXP_SUBSTR(value, regex, +position, occurrence) AS regexp_value FROM example; + +/*--------------------+---------+----------+------------+--------------* + | value | regex | position | occurrence | regexp_value | + +--------------------+---------+----------+------------+--------------+ + | Hello World Helloo | H?ello+ | 1 | 1 | Hello | + *--------------------+---------+----------+------------+--------------*/ +``` + +[string-link-to-regex]: #regexp_extract + ### `REPEAT` ```sql @@ -39047,21 +41354,33 @@ This function returns an error if the `repetitions` value is negative. **Examples** ```sql -SELECT t, n, REPEAT(t, n) AS REPEAT FROM UNNEST([ - STRUCT('abc' AS t, 3 AS n), - ('例子', 2), - ('abc', null), - (null, 3) -]); +SELECT REPEAT('abc', 3) AS results -/*------+------+-----------* - | t | n | REPEAT | - |------|------|-----------| - | abc | 3 | abcabcabc | - | 例子 | 2 | 例子例子 | - | abc | NULL | NULL | - | NULL | 3 | NULL | - *------+------+-----------*/ +/*-----------* + | results | + |-----------| + | abcabcabc | + *-----------*/ +``` + +```sql +SELECT REPEAT('abc', NULL) AS results + +/*---------* + | results | + |---------| + | NULL | + *---------*/ +``` + +```sql +SELECT REPEAT(NULL, 3) AS results + +/*---------* + | results | + |---------| + | NULL | + *---------*/ ``` ### `REPLACE` @@ -39123,23 +41442,23 @@ Returns the reverse of the input `STRING` or `BYTES`. **Examples** ```sql -WITH example AS ( - SELECT 'foo' AS sample_string, b'bar' AS sample_bytes UNION ALL - SELECT 'абвгд' AS sample_string, b'123' AS sample_bytes -) -SELECT - sample_string, - REVERSE(sample_string) AS reverse_string, - sample_bytes, - REVERSE(sample_bytes) AS reverse_bytes -FROM example; +SELECT REVERSE('abc') AS results + +/*---------* + | results | + +---------+ + | cba | + *---------*/ +``` -/*---------------+----------------+--------------+---------------* - | sample_string | reverse_string | sample_bytes | reverse_bytes | - +---------------+----------------+--------------+---------------+ - | foo | oof | bar | rab | - | абвгд | дгвба | 123 | 321 | - *---------------+----------------+--------------+---------------*/ +```sql +SELECT FORMAT('%T', REVERSE(b'1a3')) AS results + +/*---------* + | results | + +---------+ + | b"3a1" | + *---------*/ ``` ### `RIGHT` @@ -39169,42 +41488,22 @@ will be returned. **Examples** ```sql -WITH examples AS -(SELECT 'apple' as example -UNION ALL -SELECT 'banana' as example -UNION ALL -SELECT 'абвгд' as example -) -SELECT example, RIGHT(example, 3) AS right_example -FROM examples; +SELECT 'apple' AS example, RIGHT('apple', 3) AS right_example /*---------+---------------* | example | right_example | +---------+---------------+ | apple | ple | - | banana | ana | - | абвгд | вгд | *---------+---------------*/ ``` ```sql -WITH examples AS -(SELECT b'apple' as example -UNION ALL -SELECT b'banana' as example -UNION ALL -SELECT b'\xab\xcd\xef\xaa\xbb' as example -) -SELECT example, RIGHT(example, 3) AS right_example -FROM examples; +SELECT b'apple' AS example, RIGHT(b'apple', 3) AS right_example /*----------------------+---------------* | example | right_example | +----------------------+---------------+ | apple | ple | - | banana | ana | - | \xab\xcd\xef\xaa\xbb | \xef\xaa\xbb | *----------------------+---------------* ``` @@ -39246,72 +41545,53 @@ This function returns an error if: **Examples** ```sql -SELECT t, len, FORMAT('%T', RPAD(t, len)) AS RPAD FROM UNNEST([ - STRUCT('abc' AS t, 5 AS len), - ('abc', 2), - ('例子', 4) -]); +SELECT FORMAT('%T', RPAD('c', 5)) AS results -/*------+-----+----------* - | t | len | RPAD | - +------+-----+----------+ - | abc | 5 | "abc " | - | abc | 2 | "ab" | - | 例子 | 4 | "例子 " | - *------+-----+----------*/ +/*---------* + | results | + +---------+ + | "c " | + *---------*/ ``` ```sql -SELECT t, len, pattern, FORMAT('%T', RPAD(t, len, pattern)) AS RPAD FROM UNNEST([ - STRUCT('abc' AS t, 8 AS len, 'def' AS pattern), - ('abc', 5, '-'), - ('例子', 5, '中文') -]); +SELECT RPAD('b', 5, 'a') AS results -/*------+-----+---------+--------------* - | t | len | pattern | RPAD | - +------+-----+---------+--------------+ - | abc | 8 | def | "abcdefde" | - | abc | 5 | - | "abc--" | - | 例子 | 5 | 中文 | "例子中文中" | - *------+-----+---------+--------------*/ +/*---------* + | results | + +---------+ + | baaaa | + *---------*/ ``` ```sql -SELECT FORMAT('%T', t) AS t, len, FORMAT('%T', RPAD(t, len)) AS RPAD FROM UNNEST([ - STRUCT(b'abc' AS t, 5 AS len), - (b'abc', 2), - (b'\xab\xcd\xef', 4) -]); +SELECT RPAD('abc', 10, 'ghd') AS results -/*-----------------+-----+------------------* - | t | len | RPAD | - +-----------------+-----+------------------+ - | b"abc" | 5 | b"abc " | - | b"abc" | 2 | b"ab" | - | b"\xab\xcd\xef" | 4 | b"\xab\xcd\xef " | - *-----------------+-----+------------------*/ +/*------------* + | results | + +------------+ + | abcghdghdg | + *------------*/ ``` ```sql -SELECT - FORMAT('%T', t) AS t, - len, - FORMAT('%T', pattern) AS pattern, - FORMAT('%T', RPAD(t, len, pattern)) AS RPAD -FROM UNNEST([ - STRUCT(b'abc' AS t, 8 AS len, b'def' AS pattern), - (b'abc', 5, b'-'), - (b'\xab\xcd\xef', 5, b'\x00') -]); +SELECT RPAD('abc', 2, 'd') AS results + +/*---------* + | results | + +---------+ + | ab | + *---------*/ +``` + +```sql +SELECT FORMAT('%T', RPAD(b'abc', 10, b'ghd')) AS results -/*-----------------+-----+---------+-------------------------* - | t | len | pattern | RPAD | - +-----------------+-----+---------+-------------------------+ - | b"abc" | 8 | b"def" | b"abcdefde" | - | b"abc" | 5 | b"-" | b"abc--" | - | b"\xab\xcd\xef" | 5 | b"\x00" | b"\xab\xcd\xef\x00\x00" | - *-----------------+-----+---------+-------------------------*/ +/*---------------* + | results | + +---------------+ + | b"abcghdghdg" | + *---------------*/ ``` ### `RTRIM` @@ -39331,47 +41611,22 @@ Identical to [TRIM][string-link-to-trim], but only removes trailing characters. **Examples** ```sql -WITH items AS - (SELECT '***apple***' as item - UNION ALL - SELECT '***banana***' as item - UNION ALL - SELECT '***orange***' as item) - -SELECT - RTRIM(item, '*') as example -FROM items; +SELECT RTRIM('***apple***', '*') AS example /*-----------* | example | +-----------+ | ***apple | - | ***banana | - | ***orange | *-----------*/ ``` ```sql -WITH items AS - (SELECT 'applexxx' as item - UNION ALL - SELECT 'bananayyy' as item - UNION ALL - SELECT 'orangezzz' as item - UNION ALL - SELECT 'pearxyz' as item) - -SELECT - RTRIM(item, 'xyz') as example -FROM items; +SELECT RTRIM('applexxz', 'xyz') AS example /*---------* | example | +---------+ | apple | - | banana | - | orange | - | pear | *---------*/ ``` @@ -39427,30 +41682,12 @@ non-Latin characters, an empty `STRING` is returned. **Examples** ```sql -WITH example AS ( - SELECT 'Ashcraft' AS value UNION ALL - SELECT 'Raven' AS value UNION ALL - SELECT 'Ribbon' AS value UNION ALL - SELECT 'apple' AS value UNION ALL - SELECT 'Hello world!' AS value UNION ALL - SELECT ' H3##!@llo w00orld!' AS value UNION ALL - SELECT '#1' AS value UNION ALL - SELECT NULL AS value -) -SELECT value, SOUNDEX(value) AS soundex -FROM example; +SELECT 'Ashcraft' AS value, SOUNDEX('Ashcraft') AS soundex /*----------------------+---------* | value | soundex | +----------------------+---------+ | Ashcraft | A261 | - | Raven | R150 | - | Ribbon | R150 | - | apple | a140 | - | Hello world! | H464 | - | H3##!@llo w00orld! | H464 | - | #1 | | - | NULL | NULL | *----------------------+---------*/ ``` @@ -39529,22 +41766,11 @@ This function supports specifying [collation][collation]. **Examples** ```sql -WITH items AS - (SELECT 'foo' as item - UNION ALL - SELECT 'bar' as item - UNION ALL - SELECT 'baz' as item) - -SELECT - STARTS_WITH(item, 'b') as example -FROM items; +SELECT STARTS_WITH('bar', 'b') AS example /*---------* | example | +---------+ - | False | - | True | | True | *---------*/ ``` @@ -39571,30 +41797,12 @@ This function supports specifying [collation][collation]. **Examples** ```sql -WITH email_addresses AS - (SELECT - 'foo@example.com' AS email_address - UNION ALL - SELECT - 'foobar@example.com' AS email_address - UNION ALL - SELECT - 'foobarbaz@example.com' AS email_address - UNION ALL - SELECT - 'quxexample.com' AS email_address) - -SELECT - STRPOS(email_address, '@') AS example -FROM email_addresses; +SELECT STRPOS('foo@example.com', '@') AS example /*---------* | example | +---------+ | 4 | - | 7 | - | 10 | - | 0 | *---------*/ ``` @@ -39637,128 +41845,62 @@ return. **Examples** ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 2) as example -FROM items; +SELECT SUBSTR('apple', 2) AS example /*---------* | example | +---------+ | pple | - | anana | - | range | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 2, 2) as example -FROM items; +SELECT SUBSTR('apple', 2, 2) AS example /*---------* | example | +---------+ | pp | - | an | - | ra | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, -2) as example -FROM items; +SELECT SUBSTR('apple', -2) AS example /*---------* | example | +---------+ | le | - | na | - | ge | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 1, 123) as example -FROM items; +SELECT SUBSTR('apple', 1, 123) AS example /*---------* | example | +---------+ | apple | - | banana | - | orange | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 123) as example -FROM items; +SELECT SUBSTR('apple', 123) AS example /*---------* | example | +---------+ | | - | | - | | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 123, 5) as example -FROM items; +SELECT SUBSTR('apple', 123, 5) AS example /*---------* | example | +---------+ | | - | | - | | *---------*/ ``` @@ -39880,41 +42022,101 @@ To convert from an array of code points to a `STRING` or `BYTES`, see **Examples** -The following example gets the code points for each element in an array of +The following examples get the code points for each element in an array of words. ```sql -SELECT word, TO_CODE_POINTS(word) AS code_points -FROM UNNEST(['foo', 'bar', 'baz', 'giraffe', 'llama']) AS word; +SELECT + 'foo' AS word, + TO_CODE_POINTS('foo') AS code_points /*---------+------------------------------------* | word | code_points | +---------+------------------------------------+ | foo | [102, 111, 111] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'bar' AS word, + TO_CODE_POINTS('bar') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | bar | [98, 97, 114] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'baz' AS word, + TO_CODE_POINTS('baz') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | baz | [98, 97, 122] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'giraffe' AS word, + TO_CODE_POINTS('giraffe') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | giraffe | [103, 105, 114, 97, 102, 102, 101] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'llama' AS word, + TO_CODE_POINTS('llama') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | llama | [108, 108, 97, 109, 97] | *---------+------------------------------------*/ ``` -The following example converts integer representations of `BYTES` to their +The following examples convert integer representations of `BYTES` to their corresponding ASCII character values. ```sql -SELECT word, TO_CODE_POINTS(word) AS bytes_value_as_integer -FROM UNNEST([b'\x00\x01\x10\xff', b'\x66\x6f\x6f']) AS word; +SELECT + b'\x66\x6f\x6f' AS bytes_value, + TO_CODE_POINTS(b'\x66\x6f\x6f') AS bytes_value_as_integer /*------------------+------------------------* - | word | bytes_value_as_integer | + | bytes_value | bytes_value_as_integer | +------------------+------------------------+ - | \x00\x01\x10\xff | [0, 1, 16, 255] | | foo | [102, 111, 111] | *------------------+------------------------*/ ``` +```sql +SELECT + b'\x00\x01\x10\xff' AS bytes_value, + TO_CODE_POINTS(b'\x00\x01\x10\xff') AS bytes_value_as_integer + +/*------------------+------------------------* + | bytes_value | bytes_value_as_integer | + +------------------+------------------------+ + | \x00\x01\x10\xff | [0, 1, 16, 255] | + *------------------+------------------------*/ +``` + The following example demonstrates the difference between a `BYTES` result and a -`STRING` result. +`STRING` result. Notice that the character `Ā` is represented as a two-byte +Unicode sequence. As a result, the `BYTES` version of `TO_CODE_POINTS` returns +an array with two elements, while the `STRING` version returns an array with a +single element. ```sql SELECT TO_CODE_POINTS(b'Ā') AS b_result, TO_CODE_POINTS('Ā') AS s_result; @@ -39926,10 +42128,6 @@ SELECT TO_CODE_POINTS(b'Ā') AS b_result, TO_CODE_POINTS('Ā') AS s_result; *------------+----------*/ ``` -Notice that the character, Ā, is represented as a two-byte Unicode sequence. As -a result, the `BYTES` version of `TO_CODE_POINTS` returns an array with two -elements, while the `STRING` version returns an array with a single element. - [string-link-to-code-points-wikipedia]: https://en.wikipedia.org/wiki/Code_point [string-link-to-codepoints-to-string]: #code_points_to_string @@ -39956,18 +42154,14 @@ in the `STRING` as two hexadecimal characters in the range **Example** ```sql -WITH Input AS ( - SELECT b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF' AS byte_str UNION ALL - SELECT b'foobar' -) -SELECT byte_str, TO_HEX(byte_str) AS hex_str -FROM Input; +SELECT + b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF' AS byte_string, + TO_HEX(b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF') AS hex_string /*----------------------------------+------------------* | byte_string | hex_string | +----------------------------------+------------------+ | \x00\x01\x02\x03\xaa\xee\xef\xff | 00010203aaeeefff | - | foobar | 666f6f626172 | *----------------------------------+------------------*/ ``` @@ -39999,22 +42193,13 @@ type, either `STRING` or `BYTES`. **Examples** ```sql -WITH example AS ( - SELECT 'This is a cookie' AS expression, 'sco' AS source_characters, 'zku' AS - target_characters UNION ALL - SELECT 'A coaster' AS expression, 'co' AS source_characters, 'k' as - target_characters -) -SELECT expression, source_characters, target_characters, TRANSLATE(expression, -source_characters, target_characters) AS translate -FROM example; +SELECT TRANSLATE('This is a cookie', 'sco', 'zku') AS translate -/*------------------+-------------------+-------------------+------------------* - | expression | source_characters | target_characters | translate | - +------------------+-------------------+-------------------+------------------+ - | This is a cookie | sco | zku | Thiz iz a kuukie | - | A coaster | co | k | A kaster | - *------------------+-------------------+-------------------+------------------*/ +/*------------------* + | translate | + +------------------+ + | Thiz iz a kuukie | + *------------------*/ ``` ### `TRIM` @@ -40047,74 +42232,38 @@ In the following example, all leading and trailing whitespace characters are removed from `item` because `set_of_characters_to_remove` is not specified. ```sql -WITH items AS - (SELECT ' apple ' as item - UNION ALL - SELECT ' banana ' as item - UNION ALL - SELECT ' orange ' as item) - -SELECT - CONCAT('#', TRIM(item), '#') as example -FROM items; +SELECT CONCAT('#', TRIM( ' apple '), '#') AS example /*----------* | example | +----------+ | #apple# | - | #banana# | - | #orange# | *----------*/ ``` In the following example, all leading and trailing `*` characters are removed -from `item`. +from '***apple***'. ```sql -WITH items AS - (SELECT '***apple***' as item - UNION ALL - SELECT '***banana***' as item - UNION ALL - SELECT '***orange***' as item) - -SELECT - TRIM(item, '*') as example -FROM items; +SELECT TRIM('***apple***', '*') AS example /*---------* | example | +---------+ | apple | - | banana | - | orange | *---------*/ ``` In the following example, all leading and trailing `x`, `y`, and `z` characters -are removed from `item`. +are removed from 'xzxapplexxy'. ```sql -WITH items AS - (SELECT 'xxxapplexxx' as item - UNION ALL - SELECT 'yyybananayyy' as item - UNION ALL - SELECT 'zzzorangezzz' as item - UNION ALL - SELECT 'xyzpearxyz' as item) - -SELECT - TRIM(item, 'xyz') as example -FROM items; +SELECT TRIM('xzxapplexxy', 'xyz') as example /*---------* | example | +---------+ | apple | - | banana | - | orange | - | pear | *---------*/ ``` @@ -40128,7 +42277,7 @@ SELECT TRIM('abaW̊', 'Y̊') AS a, TRIM('W̊aba', 'Y̊') AS b, TRIM('abaŪ̊', 'Y̊') AS c, - TRIM('Ū̊aba', 'Y̊') AS d; + TRIM('Ū̊aba', 'Y̊') AS d /*------+------+------+------* | a | b | c | d | @@ -40141,21 +42290,12 @@ In the following example, all leading and trailing `b'n'`, `b'a'`, `b'\xab'` bytes are removed from `item`. ```sql -WITH items AS -( - SELECT b'apple' as item UNION ALL - SELECT b'banana' as item UNION ALL - SELECT b'\xab\xcd\xef\xaa\xbb' as item -) -SELECT item, TRIM(item, b'na\xab') AS examples -FROM items; +SELECT b'apple', TRIM(b'apple', b'na\xab') AS example /*----------------------+------------------* | item | example | +----------------------+------------------+ | apple | pple | - | banana | b | - | \xab\xcd\xef\xaa\xbb | \xcd\xef\xaa\xbb | *----------------------+------------------*/ ``` @@ -40213,26 +42353,12 @@ greater than 127 left intact. **Examples** ```sql -WITH items AS - (SELECT - 'foo' as item - UNION ALL - SELECT - 'bar' as item - UNION ALL - SELECT - 'baz' as item) - -SELECT - UPPER(item) AS example -FROM items; +SELECT UPPER('foo') AS example /*---------* | example | +---------+ | FOO | - | BAR | - | BAZ | *---------*/ ``` @@ -40379,24 +42505,6 @@ SELECT CURRENT_TIME() as now; *----------------------------*/ ``` -When a column named `current_time` is present, the column name and the function -call without parentheses are ambiguous. To ensure the function call, add -parentheses; to ensure the column name, qualify it with its -[range variable][time-functions-link-to-range-variables]. For example, the -following query will select the function in the `now` column and the table -column in the `current_time` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_time`) -SELECT current_time() as now, t.current_time FROM t; - -/*-----------------+--------------* - | now | current_time | - +-----------------+--------------+ - | 15:31:38.776361 | column value | - *-----------------+--------------*/ -``` - [time-functions-link-to-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables [time-link-to-timezone-definitions]: #timezone_definitions @@ -40654,8 +42762,9 @@ Gets the number of unit boundaries between two `TIME` values (`end_time` - + `start_time`: The starting `TIME` value. + `end_time`: The ending `TIME` value. -+ `granularity`: The time part that represents the granularity. - This can be: ++ `granularity`: The time part that represents the granularity. If + you passed in `TIME` values for the first arguments, `granularity` can + be: + `NANOSECOND` @@ -40673,6 +42782,10 @@ Produces an error if the computation overflows, such as if the difference in nanoseconds between the two `TIME` values overflows. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `TIME_DIFF(TIMESTAMP, TIMESTAMP, PART)` +behaves like `TIMESTAMP_DIFF(TIMESTAMP, TIMESTAMP, PART)`. + **Return Data Type** `INT64` @@ -40737,21 +42850,32 @@ SELECT ### `TIME_TRUNC` ```sql -TIME_TRUNC(time_expression, time_part) +TIME_TRUNC(time_expression, granularity) ``` **Description** -Truncates a `TIME` value to the granularity of `time_part`. The `TIME` value -is always rounded to the beginning of `time_part`, which can be one of the -following: +Truncates a `TIME` value at a particular time granularity. The `TIME` value +is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `time_expression`: The `TIME` value to truncate. ++ `granularity`: The time part that represents the granularity. If + you passed in a `TIME` value for the first argument, `granularity` can + be: -+ `NANOSECOND`: If used, nothing is truncated from the value. -+ `MICROSECOND`: The nearest lessor or equal microsecond. -+ `MILLISECOND`: The nearest lessor or equal millisecond. -+ `SECOND`: The nearest lessor or equal second. -+ `MINUTE`: The nearest lessor or equal minute. -+ `HOUR`: The nearest lessor or equal hour. + + `NANOSECOND`: If used, nothing is truncated from the value. + + + `MICROSECOND`: The nearest lesser than or equal microsecond. + + + `MILLISECOND`: The nearest lesser than or equal millisecond. + + + `SECOND`: The nearest lesser than or equal second. + + + `MINUTE`: The nearest lesser than or equal minute. + + + `HOUR`: The nearest lesser than or equal hour. **Return Data Type** @@ -41418,24 +43542,6 @@ SELECT CURRENT_TIMESTAMP() AS now; *---------------------------------------------*/ ``` -When a column named `current_timestamp` is present, the column name and the -function call without parentheses are ambiguous. To ensure the function call, -add parentheses; to ensure the column name, qualify it with its -[range variable][timestamp-functions-link-to-range-variables]. For example, the -following query selects the function in the `now` column and the table -column in the `current_timestamp` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_timestamp`) -SELECT current_timestamp() AS now, t.current_timestamp FROM t; - -/*---------------------------------------------+-------------------* - | now | current_timestamp | - +---------------------------------------------+-------------------+ - | 2020-06-02 17:00:53.110 America/Los_Angeles | column value | - *---------------------------------------------+-------------------*/ -``` - [timestamp-functions-link-to-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables ### `EXTRACT` @@ -41500,11 +43606,15 @@ In the following example, `EXTRACT` returns a value corresponding to the `DAY` time part. ```sql -WITH Input AS (SELECT TIMESTAMP("2008-12-25 05:30:00+00") AS timestamp_value) SELECT - EXTRACT(DAY FROM timestamp_value AT TIME ZONE "UTC") AS the_day_utc, - EXTRACT(DAY FROM timestamp_value AT TIME ZONE "America/Los_Angeles") AS the_day_california -FROM Input + EXTRACT( + DAY + FROM TIMESTAMP('2008-12-25 05:30:00+00') AT TIME ZONE 'UTC') + AS the_day_utc, + EXTRACT( + DAY + FROM TIMESTAMP('2008-12-25 05:30:00+00') AT TIME ZONE 'America/Los_Angeles') + AS the_day_california /*-------------+--------------------* | the_day_utc | the_day_california | @@ -41513,38 +43623,108 @@ FROM Input *-------------+--------------------*/ ``` -In the following example, `EXTRACT` returns values corresponding to different +In the following examples, `EXTRACT` returns values corresponding to different time parts from a column of type `TIMESTAMP`. ```sql -WITH Timestamps AS ( - SELECT TIMESTAMP("2005-01-03 12:34:56+00") AS timestamp_value UNION ALL - SELECT TIMESTAMP("2007-12-31 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2009-01-01 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2009-12-31 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2017-01-02 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2017-05-26 12:00:00+00") -) SELECT - timestamp_value, - EXTRACT(ISOYEAR FROM timestamp_value) AS isoyear, - EXTRACT(ISOWEEK FROM timestamp_value) AS isoweek, - EXTRACT(YEAR FROM timestamp_value) AS year, - EXTRACT(WEEK FROM timestamp_value) AS week -FROM Timestamps -ORDER BY timestamp_value; + EXTRACT(ISOYEAR FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS week --- Display of results may differ, depending upon the environment and time zone where this query was executed. -/*---------------------------------------------+---------+---------+------+------* - | timestamp_value | isoyear | isoweek | year | week | - +---------------------------------------------+---------+---------+------+------+ - | 2005-01-03 04:34:56.000 America/Los_Angeles | 2005 | 1 | 2005 | 1 | - | 2007-12-31 04:00:00.000 America/Los_Angeles | 2008 | 1 | 2007 | 52 | - | 2009-01-01 04:00:00.000 America/Los_Angeles | 2009 | 1 | 2009 | 0 | - | 2009-12-31 04:00:00.000 America/Los_Angeles | 2009 | 53 | 2009 | 52 | - | 2017-01-02 04:00:00.000 America/Los_Angeles | 2017 | 1 | 2017 | 1 | - | 2017-05-26 05:00:00.000 America/Los_Angeles | 2017 | 21 | 2017 | 21 | - *---------------------------------------------+---------+---------+------+------*/ +-- Display of results may differ, depending upon the environment and +-- time zone where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2005 | 1 | 2005 | 1 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2007-12-31 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2008 | 1 | 2007 | 52 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2009-01-01 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2009 | 1 | 2009 | 0 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2009-12-31 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2009 | 53 | 2009 | 52 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2017-01-02 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2017 | 1 | 2017 | 1 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2017-05-26 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2017 | 21 | 2017 | 21 | + *---------+---------+------+------*/ ``` In the following example, `timestamp_expression` falls on a Monday. `EXTRACT` @@ -41552,19 +43732,17 @@ calculates the first column using weeks that begin on Sunday, and it calculates the second column using weeks that begin on Monday. ```sql -WITH table AS (SELECT TIMESTAMP("2017-11-06 00:00:00+00") AS timestamp_value) SELECT - timestamp_value, - EXTRACT(WEEK(SUNDAY) FROM timestamp_value) AS week_sunday, - EXTRACT(WEEK(MONDAY) FROM timestamp_value) AS week_monday -FROM table; + EXTRACT(WEEK(SUNDAY) FROM TIMESTAMP("2017-11-06 00:00:00+00")) AS week_sunday, + EXTRACT(WEEK(MONDAY) FROM TIMESTAMP("2017-11-06 00:00:00+00")) AS week_monday --- Display of results may differ, depending upon the environment and time zone where this query was executed. -/*---------------------------------------------+-------------+---------------* - | timestamp_value | week_sunday | week_monday | - +---------------------------------------------+-------------+---------------+ - | 2017-11-05 16:00:00.000 America/Los_Angeles | 45 | 44 | - *---------------------------------------------+-------------+---------------*/ +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*-------------+---------------* + | week_sunday | week_monday | + +-------------+---------------+ + | 45 | 44 | + *-------------+---------------*/ ``` [ISO-8601]: https://en.wikipedia.org/wiki/ISO_8601 @@ -41888,8 +44066,9 @@ Gets the number of unit boundaries between two `TIMESTAMP` values + `start_timestamp`: The starting `TIMESTAMP` value. + `end_timestamp`: The ending `TIMESTAMP` value. -+ `granularity`: The timestamp part that represents the granularity. - This can be: ++ `granularity`: The timestamp part that represents the granularity. If + you passed in `TIMESTAMP` values for the first arguments, `granularity` can + be: + `NANOSECOND` @@ -41908,6 +44087,10 @@ Produces an error if the computation overflows, such as if the difference in nanoseconds between the two `TIMESTAMP` values overflows. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `TIMESTAMP_DIFF(DATE, DATE, PART)` +behaves like `DATE_DIFF(DATE, DATE, PART)`. + **Return Data Type** `INT64` @@ -42181,44 +44364,63 @@ SELECT ### `TIMESTAMP_TRUNC` ```sql -TIMESTAMP_TRUNC(timestamp_expression, date_time_part[, time_zone]) -``` - -**Description** - -Truncates a timestamp to the granularity of `date_time_part`. -The timestamp is always rounded to the beginning of `date_time_part`, -which can be one of the following: - -+ `NANOSECOND`: If used, nothing is truncated from the value. -+ `MICROSECOND`: The nearest lessor or equal microsecond. -+ `MILLISECOND`: The nearest lessor or equal millisecond. -+ `SECOND`: The nearest lessor or equal second. -+ `MINUTE`: The nearest lessor or equal minute. -+ `HOUR`: The nearest lessor or equal hour. -+ `DAY`: The day in the Gregorian calendar year that contains the - `TIMESTAMP` value. -+ `WEEK`: The first day of the week in the week that contains the - `TIMESTAMP` value. Weeks begin on Sundays. `WEEK` is equivalent to - `WEEK(SUNDAY)`. -+ `WEEK(WEEKDAY)`: The first day of the week in the week that contains the - `TIMESTAMP` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the - following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, - or `SATURDAY`. -+ `ISOWEEK`: The first day of the [ISO 8601 week][ISO-8601-week] in the - ISO week that contains the `TIMESTAMP` value. The ISO week begins on - Monday. The first ISO week of each ISO year contains the first Thursday of the - corresponding Gregorian calendar year. -+ `MONTH`: The first day of the month in the month that contains the - `TIMESTAMP` value. -+ `QUARTER`: The first day of the quarter in the quarter that contains the - `TIMESTAMP` value. -+ `YEAR`: The first day of the year in the year that contains the - `TIMESTAMP` value. -+ `ISOYEAR`: The first day of the [ISO 8601][ISO-8601] week-numbering year - in the ISO year that contains the `TIMESTAMP` value. The ISO year is the - Monday of the first week whose Thursday belongs to the corresponding - Gregorian calendar year. +TIMESTAMP_TRUNC(timestamp_expression, granularity[, time_zone]) +``` + +**Description** + +Truncates a `TIMESTAMP` value at a particular time granularity. The `TIMESTAMP` +value is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `timestamp_expression`: The `TIMESTAMP` value to truncate. ++ `granularity`: The datetime part that represents the granularity. If + you passed in a `TIMESTAMP` value for the first argument, `granularity` can + be: + + + `NANOSECOND`: If used, nothing is truncated from the value. + + + `MICROSECOND`: The nearest lesser than or equal microsecond. + + + `MILLISECOND`: The nearest lesser than or equal millisecond. + + + `SECOND`: The nearest lesser than or equal second. + + + `MINUTE`: The nearest lesser than or equal minute. + + + `HOUR`: The nearest lesser than or equal hour. + + + `DAY`: The day in the Gregorian calendar year that contains the + `TIMESTAMP` value. + + + `WEEK`: The first day in the week that contains the + `TIMESTAMP` value. Weeks begin on Sundays. `WEEK` is equivalent to + `WEEK(SUNDAY)`. + + + `WEEK(WEEKDAY)`: The first day in the week that contains the + `TIMESTAMP` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the + following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, + or `SATURDAY`. + + + `ISOWEEK`: The first day in the [ISO 8601 week][ISO-8601-week] that contains + the `TIMESTAMP` value. The ISO week begins on + Monday. The first ISO week of each ISO year contains the first Thursday of the + corresponding Gregorian calendar year. + + + `MONTH`: The first day in the month that contains the + `TIMESTAMP` value. + + + `QUARTER`: The first day in the quarter that contains the + `TIMESTAMP` value. + + + `YEAR`: The first day in the year that contains the + `TIMESTAMP` value. + + + `ISOYEAR`: The first day in the [ISO 8601][ISO-8601] week-numbering year + that contains the `TIMESTAMP` value. The ISO year is the + Monday of the first week where Thursday belongs to the corresponding + Gregorian calendar year. @@ -42228,23 +44430,20 @@ which can be one of the following: -`TIMESTAMP_TRUNC` function supports an optional `time_zone` parameter. This -parameter applies to the following `date_time_part`: - -+ `MINUTE` -+ `HOUR` -+ `DAY` -+ `WEEK` -+ `WEEK()` -+ `ISOWEEK` -+ `MONTH` -+ `QUARTER` -+ `YEAR` -+ `ISOYEAR` ++ `time_zone`: Use this parameter if you want to use a time zone other than + the default time zone, which is implementation defined, as part of the + truncate operation. This can be: -Use this parameter if you want to use a time zone other than the -default time zone, which is implementation defined, as part of the -truncate operation. + + `MINUTE` + + `HOUR` + + `DAY` + + `WEEK` + + `WEEK()` + + `ISOWEEK` + + `MONTH` + + `QUARTER` + + `YEAR` + + `ISOYEAR` When truncating a timestamp to `MINUTE` or`HOUR` parts, `TIMESTAMP_TRUNC` determines the civil time of the diff --git a/docs/functions-reference.md b/docs/functions-reference.md index 490760266..989849b3d 100644 --- a/docs/functions-reference.md +++ b/docs/functions-reference.md @@ -17,6 +17,113 @@ explicitly indicated otherwise in the function description: + For functions that are time zone sensitive, the default time zone, which is implementation defined, is used when a time zone is not specified. +## Named arguments + +```sql +named_argument => value +``` + +You can provide parameter arguments by name when calling some functions and +procedures. These arguments are called _named arguments_. An argument that is +not named is called a _positional argument_. + ++ Named arguments are optional, unless specified as required in the + function signature. ++ Named arguments don't need to be in order. ++ You can specify positional arguments before named arguments. ++ You cannot specify positional arguments after named arguments. ++ An optional positional argument that is not used doesn't need to be added + before a named argument. + +**Examples** + +These examples reference a function called `CountTokensInText`, which counts +the number of tokens in a paragraph. The function signature looks like this: + +```sql +CountTokensInText(paragraph STRING, tokens ARRAY, delimiters STRING) +``` + +`CountTokensInText` contains three arguments: `paragraph`, `tokens`, and +`delimiters`. `paragraph` represents a body of text to analyze, +`tokens` represents the tokens to search for in the paragraph, +and `delimiters` represents the characters that specify a boundary +between tokens in the paragraph. + +This is a query that includes `CountTokensInText` +without named arguments: + +```sql +SELECT token, count +FROM CountTokensInText( + 'Would you prefer softball, baseball, or tennis? There is also swimming.', + ['baseball', 'football', 'tennis'], + ' .,!?()') +``` + +This is the query with named arguments: + +```sql +SELECT token, count +FROM CountTokensInText( + paragraph => 'Would you prefer softball, baseball, or tennis? There is also swimming.', + tokens => ['baseball', 'football', 'tennis'], + delimiters => ' .,!?()') +``` + +If named arguments are used, the order of the arguments doesn't matter. This +works: + +```sql +SELECT token, count +FROM CountTokensInText( + tokens => ['baseball', 'football', 'tennis'], + delimiters => ' .,!?()', + paragraph => 'Would you prefer softball, baseball, or tennis? There is also swimming.') +``` + +You can mix positional arguments and named arguments, as long as the positional +arguments in the function signature come first: + +```sql +SELECT token, count +FROM CountTokensInText( + 'Would you prefer softball, baseball, or tennis? There is also swimming.', + tokens => ['baseball', 'football', 'tennis'], + delimiters => ' .,!?()') +``` + +This doesn't work because a positional argument appears after a named argument: + +```sql +SELECT token, count +FROM CountTokensInText( + paragraph => 'Would you prefer softball, baseball, or tennis? There is also swimming.', + ['baseball', 'football', 'tennis'], + delimiters => ' .,!?()') +``` + +If you want to use `tokens` as a positional argument, any arguments that appear +before it in the function signature must also be positional arguments. +If you try to use a named argument for `paragraph` and a positional +argument for `tokens`, this will not work. + +```sql +-- This doesn't work. +SELECT token, count +FROM CountTokensInText( + ['baseball', 'football', 'tennis'], + delimiters => ' .,!?()', + paragraph => 'Would you prefer softball, baseball, or tennis? There is also swimming.') + +-- This works. +SELECT token, count +FROM CountTokensInText( + 'Would you prefer softball, baseball, or tennis? There is also swimming.', + ['baseball', 'football', 'tennis'], + delimiters => ' .,!?()') +``` + ## Lambdas diff --git a/docs/geography_functions.md b/docs/geography_functions.md index 4fc7ed404..793141828 100644 --- a/docs/geography_functions.md +++ b/docs/geography_functions.md @@ -183,6 +183,17 @@ behavior: + + S2 functions + + S2_CELLIDFROMPOINT
+ S2_COVERINGCELLIDS
+ + + Functions for working with S2 cell coverings of GEOGRAPHY. + + + @@ -197,6 +208,24 @@ behavior: + + S2_CELLIDFROMPOINT + + + + Gets the S2 cell ID covering a point GEOGRAPHY value. + + + + + S2_COVERINGCELLIDS + + + + Gets an array of S2 cell IDs that cover a GEOGRAPHY value. + + + ST_ACCUM @@ -876,6 +905,135 @@ behavior: +### `S2_CELLIDFROMPOINT` + +```sql +S2_CELLIDFROMPOINT(point_geography[, level => cell_level]) +``` + +**Description** + +Returns the [S2 cell ID][s2-cells-link] covering a point `GEOGRAPHY`. + ++ The optional `INT64` parameter `level` specifies the S2 cell level for the + returned cell. Naming this argument is optional. + +This is advanced functionality for interoperability with systems utilizing the +[S2 Geometry Library][s2-root-link]. + +**Constraints** + ++ Returns the cell ID as a signed `INT64` bit-equivalent to + [unsigned 64-bit integer representation][s2-cells-link]. ++ Can return negative cell IDs. ++ Valid S2 cell levels are 0 to 30. ++ `level` defaults to 30 if not explicitly specified. ++ The function only supports a single point GEOGRAPHY. Use the `SAFE` prefix if + the input can be multipoint, linestring, polygon, or an empty `GEOGRAPHY`. ++ To compute the covering of a complex `GEOGRAPHY`, use + [S2_COVERINGCELLIDS][s2-coveringcellids]. + +**Return type** + +`INT64` + +**Example** + +```sql +WITH data AS ( + SELECT 1 AS id, ST_GEOGPOINT(-122, 47) AS geo + UNION ALL + -- empty geography is not supported + SELECT 2 AS id, ST_GEOGFROMTEXT('POINT EMPTY') AS geo + UNION ALL + -- only points are supported + SELECT 3 AS id, ST_GEOGFROMTEXT('LINESTRING(1 2, 3 4)') AS geo +) +SELECT id, + SAFE.S2_CELLIDFROMPOINT(geo) cell30, + SAFE.S2_CELLIDFROMPOINT(geo, level => 10) cell10 +FROM data; + +/*----+---------------------+---------------------* + | id | cell30 | cell10 | + +----+---------------------+---------------------+ + | 1 | 6093613931972369317 | 6093613287902019584 | + | 2 | NULL | NULL | + | 3 | NULL | NULL | + *----+---------------------+---------------------*/ +``` + +[s2-cells-link]: https://s2geometry.io/devguide/s2cell_hierarchy + +[s2-root-link]: https://s2geometry.io/ + +[s2-coveringcellids]: #s2_coveringcellids + +### `S2_COVERINGCELLIDS` + +```sql +S2_COVERINGCELLIDS( + geography + [, min_level => cell_level] + [, max_level => cell_level] + [, max_cells => max_cells] + [, buffer => buffer]) +``` + +**Description** + +Returns an array of [S2 cell IDs][s2-cells-link] that cover the input +`GEOGRAPHY`. The function returns at most `max_cells` cells. The optional +arguments `min_level` and `max_level` specify minimum and maximum levels for +returned S2 cells. The array size is limited by the optional `max_cells` +argument. The optional `buffer` argument specifies a buffering factor in +meters; the region being covered is expanded from the extent of the +input geography by this amount. + +This is advanced functionality for interoperability with systems utilizing the +[S2 Geometry Library][s2-root-link]. + +**Constraints** + ++ Returns the cell ID as a signed `INT64` bit-equivalent to + [unsigned 64-bit integer representation][s2-cells-link]. ++ Can return negative cell IDs. ++ Valid S2 cell levels are 0 to 30. ++ `max_cells` defaults to 8 if not explicitly specified. ++ `buffer` should be nonnegative. It defaults to 0.0 meters if not explicitly + specified. + +**Return type** + +`ARRAY` + +**Example** + +```sql +WITH data AS ( + SELECT 1 AS id, ST_GEOGPOINT(-122, 47) AS geo + UNION ALL + SELECT 2 AS id, ST_GEOGFROMTEXT('POINT EMPTY') AS geo + UNION ALL + SELECT 3 AS id, ST_GEOGFROMTEXT('LINESTRING(-122.12 47.67, -122.19 47.69)') AS geo +) +SELECT id, S2_COVERINGCELLIDS(geo, min_level => 12) cells +FROM data; + +/*----+--------------------------------------------------------------------------------------* + | id | cells | + +----+--------------------------------------------------------------------------------------+ + | 1 | [6093613931972369317] | + | 2 | [] | + | 3 | [6093384954555662336, 6093390709811838976, 6093390735581642752, 6093390740145045504, | + | | 6093390791416217600, 6093390812891054080, 6093390817187069952, 6093496378892222464] | + *----+--------------------------------------------------------------------------------------*/ +``` + +[s2-cells-link]: https://s2geometry.io/devguide/s2cell_hierarchy + +[s2-root-link]: https://s2geometry.io/ + ### `ST_ACCUM` ```sql diff --git a/docs/interval_functions.md b/docs/interval_functions.md new file mode 100644 index 000000000..2df061a69 --- /dev/null +++ b/docs/interval_functions.md @@ -0,0 +1,267 @@ + + + + +# Interval functions + +ZetaSQL supports the following interval functions. + +### Function list + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSummary
EXTRACT + + + Extracts part of an INTERVAL value. +
JUSTIFY_DAYS + + + Normalizes the day part of an INTERVAL value. +
JUSTIFY_HOURS + + + Normalizes the time part of an INTERVAL value. +
JUSTIFY_INTERVAL + + + Normalizes the day and time parts of an INTERVAL value. +
MAKE_INTERVAL + + + Constructs an INTERVAL value. +
+ +### `EXTRACT` + +```sql +EXTRACT(part FROM interval_expression) +``` + +**Description** + +Returns the value corresponding to the specified date part. The `part` must be +one of `YEAR`, `MONTH`, `DAY`, `HOUR`, `MINUTE`, `SECOND`, `MILLISECOND` or +`MICROSECOND`. + +**Return Data Type** + +`INTERVAL` + +**Examples** + +In the following example, different parts of two intervals are extracted. + +```sql +SELECT + EXTRACT(YEAR FROM i) AS year, + EXTRACT(MONTH FROM i) AS month, + EXTRACT(DAY FROM i) AS day, + EXTRACT(HOUR FROM i) AS hour, + EXTRACT(MINUTE FROM i) AS minute, + EXTRACT(SECOND FROM i) AS second, + EXTRACT(MILLISECOND FROM i) AS milli, + EXTRACT(MICROSECOND FROM i) AS micro +FROM + UNNEST([INTERVAL '1-2 3 4:5:6.789999' YEAR TO SECOND, + INTERVAL '0-13 370 48:61:61' YEAR TO SECOND]) AS i + +/*------+-------+-----+------+--------+--------+-------+--------* + | year | month | day | hour | minute | second | milli | micro | + +------+-------+-----+------+--------+--------+-------+--------+ + | 1 | 2 | 3 | 4 | 5 | 6 | 789 | 789999 | + | 1 | 1 | 370 | 49 | 2 | 1 | 0 | 0 | + *------+-------+-----+------+--------+--------+-------+--------*/ +``` + +When a negative sign precedes the time part in an interval, the negative sign +distributes over the hours, minutes, and seconds. For example: + +```sql +SELECT + EXTRACT(HOUR FROM i) AS hour, + EXTRACT(MINUTE FROM i) AS minute +FROM + UNNEST([INTERVAL '10 -12:30' DAY TO MINUTE]) AS i + +/*------+--------* + | hour | minute | + +------+--------+ + | -12 | -30 | + *------+--------*/ +``` + +When a negative sign precedes the year and month part in an interval, the +negative sign distributes over the years and months. For example: + +```sql +SELECT + EXTRACT(YEAR FROM i) AS year, + EXTRACT(MONTH FROM i) AS month +FROM + UNNEST([INTERVAL '-22-6 10 -12:30' YEAR TO MINUTE]) AS i + +/*------+--------* + | year | month | + +------+--------+ + | -22 | -6 | + *------+--------*/ +``` + +### `JUSTIFY_DAYS` + +```sql +JUSTIFY_DAYS(interval_expression) +``` + +**Description** + +Normalizes the day part of the interval to the range from -29 to 29 by +incrementing/decrementing the month or year part of the interval. + +**Return Data Type** + +`INTERVAL` + +**Example** + +```sql +SELECT + JUSTIFY_DAYS(INTERVAL 29 DAY) AS i1, + JUSTIFY_DAYS(INTERVAL -30 DAY) AS i2, + JUSTIFY_DAYS(INTERVAL 31 DAY) AS i3, + JUSTIFY_DAYS(INTERVAL -65 DAY) AS i4, + JUSTIFY_DAYS(INTERVAL 370 DAY) AS i5 + +/*--------------+--------------+-------------+---------------+--------------* + | i1 | i2 | i3 | i4 | i5 | + +--------------+--------------+-------------+---------------+--------------+ + | 0-0 29 0:0:0 | -0-1 0 0:0:0 | 0-1 1 0:0:0 | -0-2 -5 0:0:0 | 1-0 10 0:0:0 | + *--------------+--------------+-------------+---------------+--------------*/ +``` + +### `JUSTIFY_HOURS` + +```sql +JUSTIFY_HOURS(interval_expression) +``` + +**Description** + +Normalizes the time part of the interval to the range from -23:59:59.999999 to +23:59:59.999999 by incrementing/decrementing the day part of the interval. + +**Return Data Type** + +`INTERVAL` + +**Example** + +```sql +SELECT + JUSTIFY_HOURS(INTERVAL 23 HOUR) AS i1, + JUSTIFY_HOURS(INTERVAL -24 HOUR) AS i2, + JUSTIFY_HOURS(INTERVAL 47 HOUR) AS i3, + JUSTIFY_HOURS(INTERVAL -12345 MINUTE) AS i4 + +/*--------------+--------------+--------------+-----------------* + | i1 | i2 | i3 | i4 | + +--------------+--------------+--------------+-----------------+ + | 0-0 0 23:0:0 | 0-0 -1 0:0:0 | 0-0 1 23:0:0 | 0-0 -8 -13:45:0 | + *--------------+--------------+--------------+-----------------*/ +``` + +### `JUSTIFY_INTERVAL` + +```sql +JUSTIFY_INTERVAL(interval_expression) +``` + +**Description** + +Normalizes the days and time parts of the interval. + +**Return Data Type** + +`INTERVAL` + +**Example** + +```sql +SELECT JUSTIFY_INTERVAL(INTERVAL '29 49:00:00' DAY TO SECOND) AS i + +/*-------------* + | i | + +-------------+ + | 0-1 1 1:0:0 | + *-------------*/ +``` + +### `MAKE_INTERVAL` + +```sql +MAKE_INTERVAL([year][, month][, day][, hour][, minute][, second]) +``` + +**Description** + +Constructs an [`INTERVAL`][interval-type] object using `INT64` values +representing the year, month, day, hour, minute, and second. All arguments are +optional, `0` by default, and can be [named arguments][named-arguments]. + +**Return Data Type** + +`INTERVAL` + +**Example** + +```sql +SELECT + MAKE_INTERVAL(1, 6, 15) AS i1, + MAKE_INTERVAL(hour => 10, second => 20) AS i2, + MAKE_INTERVAL(1, minute => 5, day => 2) AS i3 + +/*--------------+---------------+-------------* + | i1 | i2 | i3 | + +--------------+---------------+-------------+ + | 1-6 15 0:0:0 | 0-0 0 10:0:20 | 1-0 2 0:5:0 | + *--------------+---------------+-------------*/ +``` + +[interval-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#interval_type + +[named-arguments]: https://github.com/google/zetasql/blob/master/docs/functions-reference.md#named_arguments + diff --git a/docs/json_functions.md b/docs/json_functions.md index 6abb8ca68..f06427fb4 100644 --- a/docs/json_functions.md +++ b/docs/json_functions.md @@ -58,6 +58,8 @@ behavior: JSON_EXTRACT_SCALAR
+ JSON_EXTRACT_ARRAY
+ JSON_EXTRACT_STRING_ARRAY
@@ -285,7 +287,7 @@ behavior: BOOL - Converts a JSON boolean to a SQL ARRAY<BOOL> value. + Converts a JSON array of booleans to a SQL ARRAY<BOOL> value. @@ -308,7 +310,7 @@ behavior: - Converts a JSON number to a SQL ARRAY<DOUBLE> value. + Converts a JSON array of numbers to a SQL ARRAY<DOUBLE> value. @@ -328,7 +330,7 @@ behavior: - Converts a JSON number to a SQL ARRAY<FLOAT> value. + Converts a JSON array of numbers to a SQL ARRAY<FLOAT> value. @@ -360,7 +362,7 @@ behavior: INT64_ARRAY - Converts a JSON number to a SQL ARRAY<INT64> value. + Converts a JSON array of numbers to a SQL ARRAY<INT64> value. @@ -399,6 +401,21 @@ behavior: + + JSON_EXTRACT_ARRAY + + + + (Deprecated) + Extracts a JSON array and converts it to + a SQL ARRAY<JSON-formatted STRING> + or + ARRAY<JSON> + + value. + + + JSON_EXTRACT_SCALAR @@ -410,6 +427,17 @@ behavior: + + JSON_EXTRACT_STRING_ARRAY + + + + (Deprecated) + Extracts a JSON array of scalar values and converts it to a SQL + ARRAY<STRING> value. + + + JSON_OBJECT @@ -784,7 +812,7 @@ BOOL_ARRAY(json_expr) **Description** -Converts a JSON boolean to a SQL `ARRAY` value. +Converts a JSON array of booleans to a SQL `ARRAY` value. Arguments: @@ -927,7 +955,7 @@ DOUBLE_ARRAY(json_expr[, wide_number_mode=>{ 'exact' | 'round' }]) **Description** -Converts a JSON number to a SQL `ARRAY` value. +Converts a JSON array of numbers to a SQL `ARRAY` value. Arguments: @@ -1107,7 +1135,7 @@ FLOAT_ARRAY(json_expr[, wide_number_mode=>{ 'exact' | 'round' }]) **Description** -Converts a JSON number to a SQL `ARRAY` value. +Converts a JSON array of numbers to a SQL `ARRAY` value. Arguments: @@ -1388,7 +1416,7 @@ INT64_ARRAY(json_expr) **Description** -Converts a JSON number to a SQL `INT64_ARRAY` value. +Converts a JSON array of numbers to a SQL `INT64_ARRAY` value. Arguments: @@ -1459,18 +1487,6 @@ Arguments: **Examples** -You can create an empty JSON array. For example: - -```sql -SELECT JSON_ARRAY() AS json_data - -/*-----------* - | json_data | - +-----------+ - | [] | - *-----------*/ -``` - The following query creates a JSON array with one value in it: ```sql @@ -1535,6 +1551,18 @@ SELECT JSON_ARRAY(10, [JSON '20', JSON '"foo"']) AS json_data *-----------------*/ ``` +You can create an empty JSON array. For example: + +```sql +SELECT JSON_ARRAY() AS json_data + +/*-----------* + | json_data | + +-----------+ + | [] | + *-----------*/ +``` + ### `JSON_ARRAY_APPEND` ```sql @@ -1988,71 +2016,157 @@ In the following examples, JSON data is extracted and returned as JSON-formatted strings. ```sql -SELECT JSON_EXTRACT(json_text, '$') AS json_text_string -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + '$') AS json_text_string; /*-----------------------------------------------------------* | json_text_string | +-----------------------------------------------------------+ | {"class":{"students":[{"name":"Jane"}]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[{"name":"John"},{"name":"Jamie"}]}} | *-----------------------------------------------------------*/ ``` ```sql -SELECT JSON_EXTRACT(json_text, '$.class.students[0]') AS first_student -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + '$.class.students[0]') AS first_student; /*-----------------* | first_student | +-----------------+ | {"name":"Jane"} | + *-----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | NULL | + *-----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | {"name":"John"} | *-----------------*/ ``` ```sql -SELECT JSON_EXTRACT(json_text, '$.class.students[1].name') AS second_student_name -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": null}]}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + '$.class.students[1].name') AS second_student; /*----------------* | second_student | +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": null}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | "Jamie" | *----------------*/ ``` ```sql -SELECT JSON_EXTRACT(json_text, "$.class['students']") AS student_names -FROM UNNEST([ +SELECT JSON_EXTRACT( '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; + "$.class['students']") AS student_names; /*------------------------------------* | student_names | +------------------------------------+ | [{"name":"Jane"}] | + *------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": []}}', + "$.class['students']") AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [] | + *------------------------------------*/ +``` + +```sql +SELECT JSON_EXTRACT( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + "$.class['students']") AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [{"name":"John"},{"name":"Jamie"}] | *------------------------------------*/ ``` @@ -2073,111 +2187,546 @@ SELECT JSON_EXTRACT(JSON '{"a": null}', "$.b"); -- Returns a SQL NULL [differences-json-and-string]: #differences_json_and_string -### `JSON_EXTRACT_SCALAR` +### `JSON_EXTRACT_ARRAY` -Note: This function is deprecated. Consider using [JSON_VALUE][json-value]. +Note: This function is deprecated. Consider using +[JSON_QUERY_ARRAY][json-query-array]. ```sql -JSON_EXTRACT_SCALAR(json_string_expr[, json_path]) +JSON_EXTRACT_ARRAY(json_string_expr[, json_path]) ``` ```sql -JSON_EXTRACT_SCALAR(json_expr[, json_path]) +JSON_EXTRACT_ARRAY(json_expr[, json_path]) ``` **Description** -Extracts a JSON scalar value and converts it to a SQL `STRING` value. -In addition, this function: - -+ Removes the outermost quotes and unescapes the return values. -+ Returns a SQL `NULL` if a non-scalar value is selected. -+ Uses single quotes and brackets to escape invalid [JSONPath][JSONPath-format] - characters in JSON keys. For example: `['a.b']`. +Extracts a JSON array and converts it to +a SQL `ARRAY` or +`ARRAY` value. +This function uses single quotes and brackets to escape invalid +[JSONPath][JSONPath-format] characters in JSON keys. For example: `['a.b']`. Arguments: -+ `json_string_expr`: A JSON-formatted string. For example: ++ `json_string_expr`: A JSON-formatted string. For example: + + ``` + '["a", "b", {"key": "c"}]' + ``` ++ `json_expr`: JSON. For example: + + ``` + JSON '["a", "b", {"key": "c"}]' + ``` ++ `json_path`: The [JSONPath][JSONPath-format]. This identifies the data that + you want to obtain from the input. If this optional parameter is not + provided, then the JSONPath `$` symbol is applied, which means that all of + the data is analyzed. + +There are differences between the JSON-formatted string and JSON input types. +For details, see [Differences between the JSON and JSON-formatted STRING types][differences-json-and-string]. + +**Return type** + ++ `json_string_expr`: `ARRAY` ++ `json_expr`: `ARRAY` + +**Examples** + +This extracts items in JSON to an array of `JSON` values: + +```sql +SELECT JSON_EXTRACT_ARRAY( + JSON '{"fruits":["apples","oranges","grapes"]}','$.fruits' + ) AS json_array; + +/*---------------------------------* + | json_array | + +---------------------------------+ + | ["apples", "oranges", "grapes"] | + *---------------------------------*/ +``` + +This extracts the items in a JSON-formatted string to a string array: + +```sql +SELECT JSON_EXTRACT_ARRAY('[1,2,3]') AS string_array; + +/*--------------* + | string_array | + +--------------+ + | [1, 2, 3] | + *--------------*/ +``` + +This extracts a string array and converts it to an integer array: + +```sql +SELECT ARRAY( + SELECT CAST(integer_element AS INT64) + FROM UNNEST( + JSON_EXTRACT_ARRAY('[1,2,3]','$') + ) AS integer_element +) AS integer_array; + +/*---------------* + | integer_array | + +---------------+ + | [1, 2, 3] | + *---------------*/ +``` + +This extracts string values in a JSON-formatted string to an array: + +```sql +-- Doesn't strip the double quotes +SELECT JSON_EXTRACT_ARRAY('["apples", "oranges", "grapes"]', '$') AS string_array; + +/*---------------------------------* + | string_array | + +---------------------------------+ + | ["apples", "oranges", "grapes"] | + *---------------------------------*/ + +-- Strips the double quotes +SELECT ARRAY( + SELECT JSON_EXTRACT_SCALAR(string_element, '$') + FROM UNNEST(JSON_EXTRACT_ARRAY('["apples","oranges","grapes"]','$')) AS string_element +) AS string_array; + +/*---------------------------* + | string_array | + +---------------------------+ + | [apples, oranges, grapes] | + *---------------------------*/ +``` + +This extracts only the items in the `fruit` property to an array: + +```sql +SELECT JSON_EXTRACT_ARRAY( + '{"fruit": [{"apples": 5, "oranges": 10}, {"apples": 2, "oranges": 4}], "vegetables": [{"lettuce": 7, "kale": 8}]}', + '$.fruit' +) AS string_array; + +/*-------------------------------------------------------* + | string_array | + +-------------------------------------------------------+ + | [{"apples":5,"oranges":10}, {"apples":2,"oranges":4}] | + *-------------------------------------------------------*/ +``` + +These are equivalent: + +```sql +SELECT JSON_EXTRACT_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$[fruits]') AS string_array; + +SELECT JSON_EXTRACT_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits') AS string_array; + +-- The queries above produce the following result: +/*---------------------------------* + | string_array | + +---------------------------------+ + | ["apples", "oranges", "grapes"] | + *---------------------------------*/ +``` + +In cases where a JSON key uses invalid JSONPath characters, you can escape those +characters using single quotes and brackets, `[' ']`. For example: + +```sql +SELECT JSON_EXTRACT_ARRAY('{"a.b": {"c": ["world"]}}', "$['a.b'].c") AS hello; + +/*-----------* + | hello | + +-----------+ + | ["world"] | + *-----------*/ +``` + +The following examples explore how invalid requests and empty arrays are +handled: + ++ If a JSONPath is invalid, an error is thrown. ++ If a JSON-formatted string is invalid, the output is NULL. ++ It is okay to have empty arrays in the JSON-formatted string. + +```sql +-- An error is thrown if you provide an invalid JSONPath. +SELECT JSON_EXTRACT_ARRAY('["foo", "bar", "baz"]', 'INVALID_JSONPath') AS result; + +-- If the JSONPath does not refer to an array, then NULL is returned. +SELECT JSON_EXTRACT_ARRAY('{"a": "foo"}', '$.a') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- If a key that does not exist is specified, then the result is NULL. +SELECT JSON_EXTRACT_ARRAY('{"a": "foo"}', '$.b') AS result; + +/*--------* + | result | + +--------+ + | NULL | + *--------*/ + +-- Empty arrays in JSON-formatted strings are supported. +SELECT JSON_EXTRACT_ARRAY('{"a": "foo", "b": []}', '$.b') AS result; + +/*--------* + | result | + +--------+ + | [] | + *--------*/ +``` + +[json-query-array]: #json_query_array + +[JSONPath-format]: #JSONPath_format + +[differences-json-and-string]: #differences_json_and_string + +### `JSON_EXTRACT_SCALAR` + +Note: This function is deprecated. Consider using [JSON_VALUE][json-value]. + +```sql +JSON_EXTRACT_SCALAR(json_string_expr[, json_path]) +``` + +```sql +JSON_EXTRACT_SCALAR(json_expr[, json_path]) +``` + +**Description** + +Extracts a JSON scalar value and converts it to a SQL `STRING` value. +In addition, this function: + ++ Removes the outermost quotes and unescapes the return values. ++ Returns a SQL `NULL` if a non-scalar value is selected. ++ Uses single quotes and brackets to escape invalid [JSONPath][JSONPath-format] + characters in JSON keys. For example: `['a.b']`. + +Arguments: + ++ `json_string_expr`: A JSON-formatted string. For example: + + ``` + '{"name": "Jane", "age": "6"}' + ``` ++ `json_expr`: JSON. For example: + + ``` + JSON '{"name": "Jane", "age": "6"}' + ``` ++ `json_path`: The [JSONPath][JSONPath-format]. This identifies the data that + you want to obtain from the input. If this optional parameter is not + provided, then the JSONPath `$` symbol is applied, which means that all of + the data is analyzed. + + If `json_path` returns a JSON `null` or a non-scalar value (in other words, + if `json_path` refers to an object or an array), then a SQL `NULL` is + returned. + +There are differences between the JSON-formatted string and JSON input types. +For details, see [Differences between the JSON and JSON-formatted STRING types][differences-json-and-string]. + +**Return type** + +`STRING` + +**Examples** + +In the following example, `age` is extracted. + +```sql +SELECT JSON_EXTRACT_SCALAR(JSON '{"name": "Jakob", "age": "6" }', '$.age') AS scalar_age; + +/*------------* + | scalar_age | + +------------+ + | 6 | + *------------*/ +``` + +The following example compares how results are returned for the `JSON_EXTRACT` +and `JSON_EXTRACT_SCALAR` functions. + +```sql +SELECT JSON_EXTRACT('{"name": "Jakob", "age": "6" }', '$.name') AS json_name, + JSON_EXTRACT_SCALAR('{"name": "Jakob", "age": "6" }', '$.name') AS scalar_name, + JSON_EXTRACT('{"name": "Jakob", "age": "6" }', '$.age') AS json_age, + JSON_EXTRACT_SCALAR('{"name": "Jakob", "age": "6" }', '$.age') AS scalar_age; + +/*-----------+-------------+----------+------------* + | json_name | scalar_name | json_age | scalar_age | + +-----------+-------------+----------+------------+ + | "Jakob" | Jakob | "6" | 6 | + *-----------+-------------+----------+------------*/ +``` + +```sql +SELECT JSON_EXTRACT('{"fruits": ["apple", "banana"]}', '$.fruits') AS json_extract, + JSON_EXTRACT_SCALAR('{"fruits": ["apple", "banana"]}', '$.fruits') AS json_extract_scalar; + +/*--------------------+---------------------* + | json_extract | json_extract_scalar | + +--------------------+---------------------+ + | ["apple","banana"] | NULL | + *--------------------+---------------------*/ +``` + +In cases where a JSON key uses invalid JSONPath characters, you can escape those +characters using single quotes and brackets, `[' ']`. For example: + +```sql +SELECT JSON_EXTRACT_SCALAR('{"a.b": {"c": "world"}}', "$['a.b'].c") AS hello; + +/*-------* + | hello | + +-------+ + | world | + *-------*/ +``` + +[json-value]: #json_value + +[JSONPath-format]: #JSONPath_format + +[differences-json-and-string]: #differences_json_and_string + +### `JSON_EXTRACT_STRING_ARRAY` + +Note: This function is deprecated. Consider using +[JSON_VALUE_ARRAY][json-value-array]. + +```sql +JSON_EXTRACT_STRING_ARRAY(json_string_expr[, json_path]) +``` + +```sql +JSON_EXTRACT_STRING_ARRAY(json_expr[, json_path]) +``` + +**Description** + +Extracts a JSON array of scalar values and converts it to a SQL `ARRAY` +value. In addition, this function: + ++ Removes the outermost quotes and unescapes the values. ++ Returns a SQL `NULL` if the selected value is not an array or + not an array containing only scalar values. ++ Uses single quotes and brackets to escape invalid [JSONPath][JSONPath-format] + characters in JSON keys. For example: `['a.b']`. + +Arguments: + ++ `json_string_expr`: A JSON-formatted string. For example: + + ``` + '["apples", "oranges", "grapes"]' + ``` ++ `json_expr`: JSON. For example: + + ``` + JSON '["apples", "oranges", "grapes"]' + ``` ++ `json_path`: The [JSONPath][JSONPath-format]. This identifies the data that + you want to obtain from the input. If this optional parameter is not + provided, then the JSONPath `$` symbol is applied, which means that all of + the data is analyzed. + +There are differences between the JSON-formatted string and JSON input types. +For details, see [Differences between the JSON and JSON-formatted STRING types][differences-json-and-string]. + +Caveats: + ++ A JSON `null` in the input array produces a SQL `NULL` as the output for that + JSON `null`. ++ If a JSONPath matches an array that contains scalar objects and a JSON `null`, + then the output is an array of the scalar objects and a SQL `NULL`. + +**Return type** + +`ARRAY` + +**Examples** + +This extracts items in JSON to a string array: + +```sql +SELECT JSON_EXTRACT_STRING_ARRAY( + JSON '{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits' + ) AS string_array; + +/*---------------------------* + | string_array | + +---------------------------+ + | [apples, oranges, grapes] | + *---------------------------*/ +``` + +The following example compares how results are returned for the +`JSON_EXTRACT_ARRAY` and `JSON_EXTRACT_STRING_ARRAY` functions. + +```sql +SELECT JSON_EXTRACT_ARRAY('["apples", "oranges"]') AS json_array, +JSON_EXTRACT_STRING_ARRAY('["apples", "oranges"]') AS string_array; + +/*-----------------------+-------------------* + | json_array | string_array | + +-----------------------+-------------------+ + | ["apples", "oranges"] | [apples, oranges] | + *-----------------------+-------------------*/ +``` + +This extracts the items in a JSON-formatted string to a string array: + +```sql +-- Strips the double quotes +SELECT JSON_EXTRACT_STRING_ARRAY('["foo", "bar", "baz"]', '$') AS string_array; + +/*-----------------* + | string_array | + +-----------------+ + | [foo, bar, baz] | + *-----------------*/ +``` + +This extracts a string array and converts it to an integer array: + +```sql +SELECT ARRAY( + SELECT CAST(integer_element AS INT64) + FROM UNNEST( + JSON_EXTRACT_STRING_ARRAY('[1, 2, 3]', '$') + ) AS integer_element +) AS integer_array; + +/*---------------* + | integer_array | + +---------------+ + | [1, 2, 3] | + *---------------*/ +``` + +These are equivalent: + +```sql +SELECT JSON_EXTRACT_STRING_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$[fruits]') AS string_array; + +SELECT JSON_EXTRACT_STRING_ARRAY('{"fruits": ["apples", "oranges", "grapes"]}', '$.fruits') AS string_array; + +-- The queries above produce the following result: +/*---------------------------* + | string_array | + +---------------------------+ + | [apples, oranges, grapes] | + *---------------------------*/ +``` + +In cases where a JSON key uses invalid JSONPath characters, you can escape those +characters using single quotes and brackets: `[' ']`. For example: + +```sql +SELECT JSON_EXTRACT_STRING_ARRAY('{"a.b": {"c": ["world"]}}', "$['a.b'].c") AS hello; + +/*---------* + | hello | + +---------+ + | [world] | + *---------*/ +``` + +The following examples explore how invalid requests and empty arrays are +handled: - ``` - '{"name": "Jane", "age": "6"}' - ``` -+ `json_expr`: JSON. For example: +```sql +-- An error is thrown if you provide an invalid JSONPath. +SELECT JSON_EXTRACT_STRING_ARRAY('["foo", "bar", "baz"]', 'INVALID_JSONPath') AS result; - ``` - JSON '{"name": "Jane", "age": "6"}' - ``` -+ `json_path`: The [JSONPath][JSONPath-format]. This identifies the data that - you want to obtain from the input. If this optional parameter is not - provided, then the JSONPath `$` symbol is applied, which means that all of - the data is analyzed. +-- If the JSON formatted string is invalid, then NULL is returned. +SELECT JSON_EXTRACT_STRING_ARRAY('}}', '$') AS result; - If `json_path` returns a JSON `null` or a non-scalar value (in other words, - if `json_path` refers to an object or an array), then a SQL `NULL` is - returned. +/*--------* + | result | + +--------+ + | NULL | + *--------*/ -There are differences between the JSON-formatted string and JSON input types. -For details, see [Differences between the JSON and JSON-formatted STRING types][differences-json-and-string]. +-- If the JSON document is NULL, then NULL is returned. +SELECT JSON_EXTRACT_STRING_ARRAY(NULL, '$') AS result; -**Return type** +/*--------* + | result | + +--------+ + | NULL | + *--------*/ -`STRING` +-- If a JSONPath does not match anything, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": ["foo", "bar", "baz"]}', '$.b') AS result; -**Examples** +/*--------* + | result | + +--------+ + | NULL | + *--------*/ -In the following example, `age` is extracted. +-- If a JSONPath matches an object that is not an array, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": "foo"}', '$') AS result; -```sql -SELECT JSON_EXTRACT_SCALAR(JSON '{"name": "Jakob", "age": "6" }', '$.age') AS scalar_age; +/*--------* + | result | + +--------+ + | NULL | + *--------*/ -/*------------* - | scalar_age | - +------------+ - | 6 | - *------------*/ -``` +-- If a JSONPath matches an array of non-scalar objects, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": [{"b": "foo", "c": 1}, {"b": "bar", "c":2}], "d": "baz"}', '$.a') AS result; -The following example compares how results are returned for the `JSON_EXTRACT` -and `JSON_EXTRACT_SCALAR` functions. +/*--------* + | result | + +--------+ + | NULL | + *--------*/ -```sql -SELECT JSON_EXTRACT('{"name": "Jakob", "age": "6" }', '$.name') AS json_name, - JSON_EXTRACT_SCALAR('{"name": "Jakob", "age": "6" }', '$.name') AS scalar_name, - JSON_EXTRACT('{"name": "Jakob", "age": "6" }', '$.age') AS json_age, - JSON_EXTRACT_SCALAR('{"name": "Jakob", "age": "6" }', '$.age') AS scalar_age; +-- If a JSONPath matches an array of mixed scalar and non-scalar objects, then the output is NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": [10, {"b": 20}]', '$.a') AS result; -/*-----------+-------------+----------+------------* - | json_name | scalar_name | json_age | scalar_age | - +-----------+-------------+----------+------------+ - | "Jakob" | Jakob | "6" | 6 | - *-----------+-------------+----------+------------*/ -``` +/*--------* + | result | + +--------+ + | NULL | + *--------*/ -```sql -SELECT JSON_EXTRACT('{"fruits": ["apple", "banana"]}', '$.fruits') AS json_extract, - JSON_EXTRACT_SCALAR('{"fruits": ["apple", "banana"]}', '$.fruits') AS json_extract_scalar; +-- If a JSONPath matches an empty JSON array, then the output is an empty array instead of NULL. +SELECT JSON_EXTRACT_STRING_ARRAY('{"a": "foo", "b": []}', '$.b') AS result; -/*--------------------+---------------------* - | json_extract | json_extract_scalar | - +--------------------+---------------------+ - | ["apple","banana"] | NULL | - *--------------------+---------------------*/ -``` +/*--------* + | result | + +--------+ + | [] | + *--------*/ -In cases where a JSON key uses invalid JSONPath characters, you can escape those -characters using single quotes and brackets, `[' ']`. For example: +-- In the following query, the JSON null input is returned as a +-- SQL NULL in the output. +SELECT JSON_EXTRACT_STRING_ARRAY('["world", 1, null]') AS result; -```sql -SELECT JSON_EXTRACT_SCALAR('{"a.b": {"c": "world"}}', "$['a.b'].c") AS hello; +/*------------------* + | result | + +------------------+ + | [world, 1, NULL] | + *------------------*/ -/*-------* - | hello | - +-------+ - | world | - *-------*/ ``` -[json-value]: #json_value +[json-value-array]: #json_value_array [JSONPath-format]: #JSONPath_format @@ -2500,8 +3049,9 @@ In the following example, JSON data is extracted and returned as JSON. ```sql SELECT - JSON_QUERY(JSON '{"class": {"students": [{"id": 5}, {"id": 12}]}}', '$.class') - AS json_data; + JSON_QUERY( + JSON '{"class": {"students": [{"id": 5}, {"id": 12}]}}', + '$.class') AS json_data; /*-----------------------------------* | json_data | @@ -2514,71 +3064,163 @@ In the following examples, JSON data is extracted and returned as JSON-formatted strings. ```sql -SELECT JSON_QUERY(json_text, '$') AS json_text_string -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY('{"class": {"students": [{"name": "Jane"}]}}', '$') AS json_text_string; /*-----------------------------------------------------------* | json_text_string | +-----------------------------------------------------------+ | {"class":{"students":[{"name":"Jane"}]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT JSON_QUERY('{"class": {"students": []}}', '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[]}} | + *-----------------------------------------------------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"},{"name": "Jamie"}]}}', + '$') AS json_text_string; + +/*-----------------------------------------------------------* + | json_text_string | + +-----------------------------------------------------------+ | {"class":{"students":[{"name":"John"},{"name":"Jamie"}]}} | *-----------------------------------------------------------*/ ``` ```sql -SELECT JSON_QUERY(json_text, '$.class.students[0]') AS first_student -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "Jane"}]}}', + '$.class.students[0]') AS first_student; /*-----------------* | first_student | +-----------------+ | {"name":"Jane"} | + *-----------------*/ +``` + +```sql +SELECT + JSON_QUERY('{"class": {"students": []}}', '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | NULL | + *-----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[0]') AS first_student; + +/*-----------------* + | first_student | + +-----------------+ | {"name":"John"} | *-----------------*/ ``` ```sql -SELECT JSON_QUERY(json_text, '$.class.students[1].name') AS second_student_name -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": null}]}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "Jane"}]}}', + '$.class.students[1].name') AS second_student; /*----------------* | second_student | +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": []}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": null}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | NULL | + *----------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class.students[1].name') AS second_student; + +/*----------------* + | second_student | + +----------------+ | "Jamie" | *----------------*/ ``` ```sql -SELECT JSON_QUERY(json_text, '$.class."students"') AS student_names -FROM UNNEST([ - '{"class": {"students": [{"name": "Jane"}]}}', - '{"class": {"students": []}}', - '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}' - ]) AS json_text; +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "Jane"}]}}', + '$.class."students"') AS student_names; /*------------------------------------* | student_names | +------------------------------------+ | [{"name":"Jane"}] | + *------------------------------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": []}}', + '$.class."students"') AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [] | + *------------------------------------*/ +``` + +```sql +SELECT + JSON_QUERY( + '{"class": {"students": [{"name": "John"}, {"name": "Jamie"}]}}', + '$.class."students"') AS student_names; + +/*------------------------------------* + | student_names | + +------------------------------------+ | [{"name":"John"},{"name":"Jamie"}] | *------------------------------------*/ ``` @@ -2597,6 +3239,8 @@ SELECT JSON_QUERY(JSON '{"a": null}', "$.b"); -- Returns a SQL NULL [differences-json-and-string]: #differences_json_and_string +[JSONPath-mode]: #JSONPath_mode + ### `JSON_QUERY_ARRAY` ```sql @@ -2696,7 +3340,9 @@ SELECT JSON_QUERY_ARRAY('["apples", "oranges", "grapes"]', '$') AS string_array; +---------------------------------+ | ["apples", "oranges", "grapes"] | *---------------------------------*/ +``` +```sql -- Strips the double quotes SELECT ARRAY( SELECT JSON_VALUE(string_element, '$') @@ -7065,53 +7711,35 @@ A JSON-formatted `STRING` **Examples** -Convert rows in a table to JSON-formatted strings. +The following query converts a `STRUCT` value to a JSON-formatted string: ```sql -With CoordinatesTable AS ( - (SELECT 1 AS id, [10, 20] AS coordinates) UNION ALL - (SELECT 2 AS id, [30, 40] AS coordinates) UNION ALL - (SELECT 3 AS id, [50, 60] AS coordinates)) -SELECT id, coordinates, TO_JSON_STRING(t) AS json_data -FROM CoordinatesTable AS t; +SELECT TO_JSON_STRING(STRUCT(1 AS id, [10,20] AS coordinates)) AS json_data -/*----+-------------+--------------------------------* - | id | coordinates | json_data | - +----+-------------+--------------------------------+ - | 1 | [10, 20] | {"id":1,"coordinates":[10,20]} | - | 2 | [30, 40] | {"id":2,"coordinates":[30,40]} | - | 3 | [50, 60] | {"id":3,"coordinates":[50,60]} | - *----+-------------+--------------------------------*/ +/*--------------------------------* + | json_data | + +--------------------------------+ + | {"id":1,"coordinates":[10,20]} | + *--------------------------------*/ ``` -Convert rows in a table to JSON-formatted strings that are easy to read. +The following query converts a `STRUCT` value to a JSON-formatted string that is +easy to read: ```sql -With CoordinatesTable AS ( - (SELECT 1 AS id, [10, 20] AS coordinates) UNION ALL - (SELECT 2 AS id, [30, 40] AS coordinates)) -SELECT id, coordinates, TO_JSON_STRING(t, true) AS json_data -FROM CoordinatesTable AS t; +SELECT TO_JSON_STRING(STRUCT(1 AS id, [10,20] AS coordinates), true) AS json_data -/*----+-------------+--------------------* - | id | coordinates | json_data | - +----+-------------+--------------------+ - | 1 | [10, 20] | { | - | | | "id": 1, | - | | | "coordinates": [ | - | | | 10, | - | | | 20 | - | | | ] | - | | | } | - +----+-------------+--------------------+ - | 2 | [30, 40] | { | - | | | "id": 2, | - | | | "coordinates": [ | - | | | 30, | - | | | 40 | - | | | ] | - | | | } | - *----+-------------+--------------------*/ +/*--------------------* + | json_data | + +--------------------+ + | { | + | "id": 1, | + | "coordinates": [ | + | 10, | + | 20 | + | ] | + | } | + *--------------------*/ ``` [json-encodings]: #json_encodings @@ -7827,6 +8455,27 @@ The following SQL to JSON encodings are supported: + + + + RANGE + +

range

+

+ Encoded as an object with a start and end + value. Any unbounded part of the range is represented as + null. +

+ + + SQL input: RANGE<DATE> '[2024-07-24, 2024-07-25)'
+ JSON output: {"start":"2024-07-24","end":"2024-07-25"}
+
+ SQL input: RANGE<DATETIME> '[2024-07-24 10:00:00, UNBOUNDED)'
+ JSON output: {"start":"2024-07-24T10:00:00","end":null}
+ + + @@ -7834,8 +8483,19 @@ The following SQL to JSON encodings are supported: With the JSONPath format, you can identify the values you want to -obtain from a JSON-formatted string. The JSONPath format supports these -operators: +obtain from a JSON-formatted string. + +If a key in a JSON functions contains a JSON format operator, refer to each +JSON function for how to escape them. + +A JSON function returns `NULL` if the JSONPath format does not match a value in +a JSON-formatted string. If the selected value for a scalar function is not +scalar, such as an object or an array, the function returns `NULL`. If the +JSONPath format is invalid, an error is produced. + +#### Operators for JSONPath + +The JSONPath format supports these operators: @@ -7939,14 +8599,6 @@ operators:
-If a key in a JSON functions contains a JSON format operator, refer to each -JSON function for how to escape them. - -A JSON function returns `NULL` if the JSONPath format does not match a value in -a JSON-formatted string. If the selected value for a scalar function is not -scalar, such as an object or an array, the function returns `NULL`. If the -JSONPath format is invalid, an error is produced. - ### Differences between the JSON and JSON-formatted STRING types @@ -8047,3 +8699,5 @@ FROM t; [JSON-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#json_type +[JSONPath-mode]: #JSONPath_mode + diff --git a/docs/mathematical_functions.md b/docs/mathematical_functions.md index 1ce970205..d6d6170ad 100644 --- a/docs/mathematical_functions.md +++ b/docs/mathematical_functions.md @@ -94,6 +94,9 @@ All mathematical functions have the following behaviors: + + + COSINE_DISTANCE   EUCLIDEAN_DISTANCE   @@ -2852,7 +2855,7 @@ GROUP BY 1 ### `ROUND` ``` -ROUND(X [, N]) +ROUND(X [, N [, rounding_mode]]) ``` **Description** @@ -2862,6 +2865,18 @@ rounds X to N decimal places after the decimal point. If N is negative, rounds off digits to the left of the decimal point. Rounds halfway cases away from zero. Generates an error if overflow occurs. +If X is a `NUMERIC` or `BIGNUMERIC` type, then you can +explicitly set `rounding_mode` +to one of the following: + ++ [`"ROUND_HALF_AWAY_FROM_ZERO"`][round-half-away-from-zero]: (Default) Rounds + halfway cases away from zero. ++ [`"ROUND_HALF_EVEN"`][round-half-even]: Rounds halfway cases + towards the nearest even digit. + +If you set the `rounding_mode` and X is not a `NUMERIC` or `BIGNUMERIC` type, +then the function generates an error. + @@ -2922,6 +2937,30 @@ away from zero. Generates an error if overflow occurs. + + + + + + + + + + + + + + + + + + + + + + + +
ROUND(1.235, 2) 1.24
ROUND(NUMERIC "2.25", 1, "ROUND_HALF_EVEN")2.2
ROUND(NUMERIC "2.35", 1, "ROUND_HALF_EVEN")2.4
ROUND(NUMERIC "2.251", 1, "ROUND_HALF_EVEN")2.3
ROUND(NUMERIC "-2.5", 0, "ROUND_HALF_EVEN")-2
ROUND(NUMERIC "2.5", 0, "ROUND_HALF_AWAY_FROM_ZERO")3
ROUND(NUMERIC "-2.5", 0, "ROUND_HALF_AWAY_FROM_ZERO")-3
@@ -2940,6 +2979,10 @@ away from zero. Generates an error if overflow occurs. +[round-half-away-from-zero]: https://en.wikipedia.org/wiki/Rounding#Rounding_half_away_from_zero + +[round-half-even]: https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even + ### `SAFE_ADD` ``` diff --git a/docs/operators.md b/docs/operators.md index c5b7864f3..a3b0efa9d 100644 --- a/docs/operators.md +++ b/docs/operators.md @@ -558,18 +558,14 @@ a field by position is useful when fields are un-named or have ambiguous names. **Example** -In the following example, the expression is `t.customer` and the -field access operations are `.address` and `.country`. An operation is an -application of an operator (`.`) to specific operands (in this case, -`address` and `country`, or more specifically, `t.customer` and `address`, -for the first operation, and `t.customer.address` and `country` for the -second operation). +In the following example, the field access operations are `.address` and +`.country`. ```sql -WITH orders AS ( - SELECT STRUCT(STRUCT('Yonge Street' AS street, 'Canada' AS country) AS address) AS customer -) -SELECT t.customer.address.country FROM orders AS t; +SELECT + STRUCT( + STRUCT('Yonge Street' AS street, 'Canada' AS country) + AS address).address.country /*---------* | country | @@ -583,8 +579,10 @@ SELECT t.customer.address.country FROM orders AS t; ### Array subscript operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` -array_expression[array_subscript_specifier] +array_expression "[" array_subscript_specifier "]" array_subscript_specifier: { index | position_keyword(index) } @@ -593,9 +591,6 @@ position_keyword: { OFFSET | SAFE_OFFSET | ORDINAL | SAFE_ORDINAL } ``` -Note: The brackets (`[]`) around `array_subscript_specifier` are part of the -syntax; they do not represent an optional part. - **Description** Gets a value from an array at a specific position. @@ -634,14 +629,12 @@ reference an index (`6`) in an array that is out of range. If the `SAFE` prefix is included, `NULL` is returned, otherwise an error is produced. ```sql -WITH Items AS (SELECT ["coffee", "tea", "milk"] AS item_array) SELECT - item_array, - item_array[0] AS item_index, - item_array[OFFSET(0)] AS item_offset, - item_array[ORDINAL(1)] AS item_ordinal, - item_array[SAFE_OFFSET(6)] AS item_safe_offset -FROM Items + ["coffee", "tea", "milk"] AS item_array, + ["coffee", "tea", "milk"][0] AS item_index, + ["coffee", "tea", "milk"][OFFSET(0)] AS item_offset, + ["coffee", "tea", "milk"][ORDINAL(1)] AS item_ordinal, + ["coffee", "tea", "milk"][SAFE_OFFSET(6)] AS item_safe_offset /*---------------------+------------+-------------+--------------+------------------* | item_array | item_index | item_offset | item_ordinal | item_safe_offset | @@ -655,28 +648,22 @@ keyword that begins with `SAFE` is not included, an error is produced. For example: ```sql -WITH Items AS (SELECT ["coffee", "tea", "milk"] AS item_array) -SELECT - item_array[6] AS item_offset -FROM Items - -- Error. Array index 6 is out of bounds. +SELECT ["coffee", "tea", "milk"][6] AS item_offset ``` ```sql -WITH Items AS (SELECT ["coffee", "tea", "milk"] AS item_array) -SELECT - item_array[OFFSET(6)] AS item_offset -FROM Items - -- Error. Array index 6 is out of bounds. +SELECT ["coffee", "tea", "milk"][OFFSET(6)] AS item_offset ``` ### Struct subscript operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` -struct_expression[struct_subscript_specifier] +struct_expression "[" struct_subscript_specifier "]" struct_subscript_specifier: { index | position_keyword(index) } @@ -685,9 +672,6 @@ position_keyword: { OFFSET | ORDINAL } ``` -Note: The brackets (`[]`) around `struct_subscript_specifier` are part of the -syntax; they do not represent an optional part. - **Description** Gets the value of a field at a selected position in a struct. @@ -718,12 +702,10 @@ shows what happens when you reference an index (`6`) in an struct that is out of range. ```sql -WITH Items AS (SELECT STRUCT(23, "tea", FALSE) AS item_struct) SELECT - item_struct[0] AS field_index, - item_struct[OFFSET(0)] AS field_offset, - item_struct[ORDINAL(1)] AS field_ordinal -FROM Items + STRUCT(23, "tea", FALSE)[0] AS field_index, + STRUCT(23, "tea", FALSE)[OFFSET(0)] AS field_offset, + STRUCT(23, "tea", FALSE)[ORDINAL(1)] AS field_ordinal /*-------------+--------------+---------------* | field_index | field_offset | field_ordinal | @@ -736,37 +718,28 @@ When you reference an index that is out of range in a struct, an error is produced. For example: ```sql -WITH Items AS (SELECT STRUCT(23, "tea", FALSE) AS item_struct) -SELECT - item_struct[6] AS field_offset -FROM Items - --- Error. Field ordinal 6 is out of bounds in STRUCT +-- Error: Field ordinal 6 is out of bounds in STRUCT +SELECT STRUCT(23, "tea", FALSE)[6] AS field_offset ``` ```sql -WITH Items AS (SELECT STRUCT(23, "tea", FALSE) AS item_struct) -SELECT - item_struct[OFFSET(6)] AS field_offset -FROM Items - --- Error. Field ordinal 6 is out of bounds in STRUCT +-- Error: Field ordinal 6 is out of bounds in STRUCT +SELECT STRUCT(23, "tea", FALSE)[OFFSET(6)] AS field_offset ``` ### JSON subscript operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` -json_expression[array_element_id] +json_expression "[" array_element_id "]" ``` ``` -json_expression[field_name] +json_expression "[" field_name "]" ``` -Note: The brackets (`[]`) around `array_element_id` and `field_name` are part -of the syntax; they do not represent an optional part. - **Description** Gets a value of an array element or field in a JSON expression. Can be @@ -947,6 +920,8 @@ FROM ### Array elements field access operator +Note: Syntax wrapped in double quotes (`""`) is required. + ``` array_expression.field_or_element[. ...] @@ -954,12 +929,9 @@ field_or_element: { fieldname | array_element } array_element: - array_fieldname[array_subscript_specifier] + array_fieldname "[" array_subscript_specifier "]" ``` -Note: The brackets (`[]`) around `array_subscript_specifier` are part of the -syntax; they do not represent an optional part. - **Description** The array elements field access operation lets you traverse through the diff --git a/docs/pipe-syntax.md b/docs/pipe-syntax.md new file mode 100644 index 000000000..4cc6777f6 --- /dev/null +++ b/docs/pipe-syntax.md @@ -0,0 +1,660 @@ + + + + +# Pipe query syntax + +ZetaSQL supports pipe query syntax, which is a simpler and more +concise alternative to [standard query syntax][query-syntax]. Pipe syntax +supports the same operators as standard syntax, but follows these rules. + +Syntax: + +* Pipe syntax consists of a pipe and an angle bracket `|>`, an operator name, + and any arguments: \ + `|> operator_name argument_list` +* Pipe operators can be added on the end of any valid query. +* Pipe operators can be applied in any order, any number of times. +* Pipe syntax works anywhere standard syntax is supported, in queries, views, + table-valued functions (TVFs), and other contexts. +* Pipe syntax can be mixed with standard syntax in the same query. For + example, subqueries can use different syntax from the parent query. + +`FROM` queries: + +* A query can start with a [`FROM` clause][from-clause] and use any standard + `FROM` syntax, including tables, joins, subqueries, table aliases with `AS`, + and other usual elements. +* Pipe operators can optionally be added after the `FROM` clause. + +Semantics: + +* Each pipe operator is self-contained and can only reference columns + available from its immediate input table. A pipe operator can't reference + columns from earlier in the same query. +* Each pipe operator produces a new result table as its output. + +Compare the following equivalent queries that count open tickets +assigned to a user: + +**Standard syntax** + +```sql +SELECT component_id, COUNT(*) +FROM ticketing_system_table +WHERE + assignee_user.email = 'username@email.com' + AND status IN ('NEW', 'ASSIGNED', 'ACCEPTED') +GROUP BY component_id +ORDER BY component_id DESC; +``` + +**Pipe syntax** + +```sql +FROM ticketing_system_table +|> WHERE + assignee_user.email = 'username@email.com' + AND status IN ('NEW', 'ASSIGNED', 'ACCEPTED') +|> AGGREGATE COUNT(*) + GROUP AND ORDER BY component_id DESC; +``` + +## Pipe operators + +ZetaSQL supports the following pipe operators. Each operator +description includes a link to documentation for the corresponding operation in +standard syntax, where applicable. That documentation describes syntax features +and behavior in more detail. This pipe operator documentation highlights any +differences between the pipe syntax and the standard syntax for each operator. + +### `SELECT` + +
+|> SELECT expression [[AS] alias] [, ...]
+
+ +**Description** + +Produces a new table with exactly the listed columns, similar to the outermost +[`SELECT` clause][select-clause] in a table subquery in standard syntax. +Supports standard output modifiers like `SELECT AS STRUCT`, and supports +[window functions][window-functions]. Doesn't support aggregations or +`WITH ANONYMIZATION`. + +In pipe syntax, using `SELECT` operators in a query is optional. `SELECT` can be +used near the end of a query to specify the list of output columns. The final +query result includes all columns from the output of the last pipe operator. If +`SELECT` isn't used, the output is similar to what would be produced by +[`SELECT *`][select-star]. + +For some operations normally done with `SELECT` in standard syntax, pipe syntax +supports other operators that might be more convenient. For example, aggregation +is always done with [`AGGREGATE`][aggregate-pipes-operator]. To compute new +columns, you can use [`EXTEND`][extend-pipes-operator]. To update specific +columns, you can use [`SET`][set-pipes-operator] or +[`DROP`][drop-pipes-operator]. + +**Example** + +
+|> SELECT account_id AS Account
+
+ +### `EXTEND` + +
+|> EXTEND expression [[AS] alias] [, ...]
+
+ +**Description** + +Propagates the existing table and adds a computed column, similar to +[`SELECT *, new_column`][select-star] in standard syntax. Supports +[window functions][window-functions]. + +**Examples** + +
+|> EXTEND status IN ('NEW', 'ASSIGNED', 'ACCEPTED') AS is_open
+
+ +
+-- Window function, with `OVER`
+|> EXTEND SUM(val) OVER (ORDER BY k) AS val_over_k
+
+ +### `SET` + +
+|> SET column_name = expression [, ...]
+
+ +**Description** + +Replaces the value of a column in the current table, similar to +[`SELECT * REPLACE (expression AS column)`][select-replace] in standard syntax. +Each reference column must exist exactly once in the input table. Table aliases +(like `t.x`) still point to the original column. + +This pipe operator doesn't correspond to the [set operators][set-operators] in +standard syntax, which combine input query results. + +**Example** + +
+|> SET x = 5, y = CAST(y AS INT32)
+
+ +### `DROP` + +
+|> DROP column_name [, ...]
+
+ +**Description** + +Removes listed columns from the current table, similar to +[`SELECT * EXCEPT (column)`][select-except] in standard syntax. Each reference +column must exist at least once in the input table. + +This pipe operator doesn't correspond to [`DROP` statements][drop-statement] in +data definition language (DDL), which delete persistent schema objects. + +**Example** + +
+|> DROP account_id, user_id
+
+ +### `RENAME` + +
+|> RENAME old_column_name [AS] new_column_name [, ...]
+
+ +**Description** + +Renames specified columns. Each old column name must exist exactly once in the +input table. The `RENAME` operator can't rename value table fields, +pseudo-columns, range variables, or objects that aren't columns on the pipe +input table. + +**Example** + +
+|> RENAME last_name AS surname
+
+ +### `AS` + +
+|> AS alias
+
+ +**Description** + +Introduces a table alias for the pipe input table, similar to applying +[`AS alias`][using-aliases] on a table subquery in standard syntax. Any existing +table aliases are removed and the new alias becomes the table alias for all +columns in the row. + +This operator can be useful after operators like +[`SELECT`][select-pipes-operator], [`EXTEND`][extend-pipes-operator], or +[`AGGREGATE`][aggregate-pipes-operator] that add columns but can't give table +aliases to them. + +**Example** + +
+|> SELECT x, y, z
+|> AS table_alias
+|> WHERE table_alias.y = 10
+
+ +### `WHERE` + +
+|> WHERE boolean_expression
+
+ +**Description** + +Same behavior as the [`WHERE` clause][where-clause] in standard syntax: Filters +the results of the `FROM` clause. + +In pipe syntax, `WHERE` also replaces the [`HAVING` clause][having-clause] and +[`QUALIFY` clause][qualify-clause]. For example, after performing aggregation +with [`AGGREGATE`][aggregate-pipes-operator], use `WHERE` instead of `HAVING`. +For [window functions][window-functions] inside a `QUALIFY` clause, use window +functions inside a `WHERE` clause instead. + +**Example** + +
+|> WHERE assignee_user.email = 'username@email.com'
+
+ +### `LIMIT...OFFSET` + +
+|> LIMIT count [OFFSET skip_rows]
+
+ +**Description** + +Same behavior as the [`LIMIT` and `OFFSET` clause][limit-offset-clause] in +standard syntax: Limits the number of rows to return in a query, and optionally +skips over rows. In pipe syntax, the `OFFSET` clause is used only with the +`LIMIT` clause. + +**Example** + +
+|> LIMIT 10 OFFSET 2
+
+ +### `AGGREGATE...GROUP BY` + +
+-- Full-table aggregation
+|> AGGREGATE aggregate_expression [[AS] alias] [, ...]
+
+
+-- Aggregation with grouping
+|> AGGREGATE [aggregate_expression [[AS] alias] [, ...]]
+   GROUP BY groupable_items [[AS] alias] [, ...]
+
+ +**Description** + +Performs aggregation on data across grouped rows or an entire table. The +`AGGREGATE` operator is similar to a query in standard syntax that contains a +[`GROUP BY` clause][group-by-clause] or a `SELECT` list with +[aggregate functions][aggregate-functions] or both. + +Without the `GROUP BY` clause, the `AGGREGATE` operator performs full-table +aggregation and always produces exactly one output row. + +With the `GROUP BY` clause, the `AGGREGATE` operator performs aggregation with +grouping, producing one row for each set of distinct values for the grouping +expressions. + +The `AGGREGATE` list corresponds to the aggregated expressions in a `SELECT` +list in standard syntax. Each expression in the `AGGREGATE` list must include +aggregation. Aggregate expressions can also include scalar expressions (for +example, `sqrt(SUM(x*x))`). Aliases are allowed. Window functions aren't +allowed, but [`EXTEND`][extend-pipes-operator] can be used before `AGGREGATE` to +compute window functions. + +The `GROUP BY` list corresponds to the `GROUP BY` clause in standard syntax. +Unlike in standard syntax, aliases can be assigned on `GROUP BY` items. Standard +modifiers like `GROUPING SETS`, `ROLLUP`, and `CUBE` are supported. + +The output columns from `AGGREGATE` include all grouping columns first, followed +by all aggregate columns, using the aliases assigned in either list as the +column names. + +Unlike in standard syntax, grouping expressions aren't repeated across `SELECT` +and `GROUP BY`. In pipe syntax, the grouping expressions are listed only once, +in `GROUP BY`, and are automatically included as output columns. + +Because output columns are fully specified by this operator, `SELECT` isn't usually +needed after `AGGREGATE` to specify the columns to return. + +**Examples** + +
+-- Full-table aggregation
+|> AGGREGATE COUNT(*) AS row_count, SUM(num_users) AS total_users
+
+
+-- Aggregation with grouping
+|> AGGREGATE COUNT(*) AS row_count, SUM(num_users) AS total_users,
+   GROUP BY org_site, date
+
+ +The following examples compare aggregation in standard syntax and in pipe +syntax: + +
+-- Aggregation in standard syntax
+SELECT id, EXTRACT(MONTH FROM date) AS month, SUM(value) AS total
+FROM table
+GROUP BY id, month
+
+ +
+-- Aggregation in pipe syntax
+FROM table
+|> AGGREGATE SUM(value) AS total
+   GROUP BY id, EXTRACT(MONTH FROM date) AS month
+
+ +#### Shorthand syntax for `AGGREGATE` with `ORDER BY` + +Pipe syntax supports the following additional shorthand syntax for the +[`ORDER BY`][order-by-pipes-operator] pipe operator as part of `AGGREGATE` +without repeating the column list: + +
+|> AGGREGATE [
+     aggregate_expression [{ASC | DESC} {NULLS FIRST | NULLS LAST}] [[AS] alias]
+     [, ...]]
+   GROUP [AND ORDER] BY
+     groupable_item [{ASC | DESC} {NULLS FIRST| NULLS LAST}] [[AS] alias]
+     [, ...]
+
+ +The `GROUP AND ORDER BY` clause is equivalent to an `ORDER BY` on all groupable +items. By default, the groupable items are sorted in ascending order with null +values first. Ordering suffixes like `ASC`, `DESC`, `NULLS FIRST`, and +`NULLS LAST` can be used for other orders. + +Without `GROUP AND ORDER BY`, the `ASC` or `DESC` suffixes can be added on +individual columns in the `GROUP BY` or `AGGREGATE` lists or both. The +`NULLS FIRST` or `NULLS LAST` suffixes can be used to further modify sorting. + +If any of these ordering suffixes are present, the syntax behavior is equivalent +to adding an `ORDER BY` that includes all of the suffixed columns, with the +grouping columns first, matching the left-to-right output column order. + +**Examples** + +
+-- Order by all grouping columns.
+|> AGGREGATE COUNT(*)
+   GROUP AND ORDER BY first_name, last_name DESC
+
+ +The ordering in the previous example is equivalent to using +`|> ORDER BY first_name, last_name DESC`. + +
+-- Order by specified grouping and aggregate columns.
+|> AGGREGATE COUNT(*) DESC
+   GROUP BY first_name, last_name ASC
+
+ +The ordering in the previous example is equivalent to using +`|> ORDER BY last_name ASC, COUNT(*) DESC`. + +### `ORDER BY` + +
+|> ORDER BY expression [sort_options] [, ...]
+
+ +**Description** + +Same behavior as the [`ORDER BY` clause][order-by-clause] in standard syntax: +Sorts results by a list of expressions. Suffixes like `ASC`, `DESC`, and +`NULLS LAST` are supported for customizing the ordering for each expression. + +For queries with [`AGGREGATE...GROUP BY`][aggregate-pipes-operator] and +`ORDER BY` together, pipe syntax also supports a `GROUP AND ORDER BY` clause and +other [shorthand ordering suffixes][order-by-with-aggregate] for aggregation. + +**Example** + +
+|> ORDER BY last_name DESC
+
+ +### `JOIN` + +
+|> [join_type] JOIN from_item [[AS] alias] [on_clause | using_clause]
+
+ +**Description** + +Same behavior as the [`JOIN` operation][join-operation] in standard syntax: +Merges two items so that they can be queried as one source. The join operation +treats the pipe input table as the left side of the join and the `JOIN` argument +as the right side of the join. Standard join inputs are supported, including +tables, subqueries, `UNNEST`, and table-valued function (TVF) calls. Standard +join modifiers like `LEFT`, `INNER`, and `CROSS` are allowed before `JOIN`. + +An alias can be assigned to the input item on the right side of the join, but +not to the pipe input table on the left side of the join. If an alias on the +pipe input table is needed, perhaps to disambiguate columns in an +[`ON` expression][on-clause], then an alias can be added using +[`AS`][as-pipes-operator] before the `JOIN`. + +**Example** + +
+|> JOIN ticketing_system_table AS components
+     ON bug_table.component_id = CAST(components.component_id AS int64)
+
+ +### `CALL` + +
+|> CALL table_function (argument [, ...]) [[AS] alias]
+
+ +**Description** + +Calls a [table-valued function][tvf] (TVF), similar to [table function +calls][table-function-calls] in standard syntax. TVFs in standard syntax can be +called in the `FROM` clause or in a `JOIN` operation. These are both allowed in +pipe syntax as well. + +In pipe syntax, TVFs that take a table argument can also be called with the +`CALL` operator. The first table argument comes from the pipe input table and +must be omitted in the arguments. An optional table alias can be added for the +output table. + +Multiple TVFs can be called sequentially without using nested subqueries. + +**Examples** + +
+|> CALL AddSuffix('*')
+|> CALL AddSuffix2(arg1, arg2, arg3)
+
+ +The following examples compare a TVF call in standard syntax and in pipe syntax: + +
+-- Call a TVF in standard syntax.
+FROM tvf( (SELECT * FROM table), arg1, arg2 )
+
+ +
+-- Call the same TVF in pipe syntax.
+SELECT * FROM table
+|> CALL tvf(arg1, arg2)
+
+ +### `WINDOW` + +
+|> WINDOW window_expression [[AS] alias] [, ...]
+
+ +**Description** + +Adds a column with the result of computing the function over some window of +existing rows, similar to calling [window functions][window-functions] in a +`SELECT` list in standard syntax. Existing rows and columns are unchanged. The +window expression must include a window function with an +[`OVER` clause][over-clause]. + +The [`EXTEND`][extend-pipes-operator] pipe operator also supports window +expressions. Using `EXTEND` with window functions is usually preferred instead +of `WINDOW`. + +**Example** + +
+|> WINDOW SUM(val) OVER (ORDER BY k)
+
+ +### `TABLESAMPLE` + +
+|> TABLESAMPLE sample_method (sample_size {PERCENT | ROWS}) [, ...]
+
+ +**Description** + +Same behavior as the [`TABLESAMPLE` operator][tablesample-operator] in standard +syntax: Selects a random sample of rows from the input table. + +**Example** + +
+|> TABLESAMPLE BERNOULLI (0.1 PERCENT)
+
+ +### `PIVOT` + +
+|> PIVOT (aggregate_expression FOR input_column IN (pivot_column [, ...])) [[AS] alias]
+
+ +**Description** + +Same behavior as the [`PIVOT` operator][pivot-operator] in standard syntax: +Rotates rows into columns. + +**Example** + +
+|> SELECT year, username, num_users
+|> PIVOT (SUM(num_users) FOR username IN ('Jeff', 'Jeffrey', 'Jeffery'))
+
+ +### `UNPIVOT` + +
+|> UNPIVOT (values_column FOR name_column IN (column_to_unpivot [, ...])) [[AS] alias]
+
+ +**Description** + +Same behavior as the [`UNPIVOT` operator][unpivot-operator] in standard syntax: +Rotates columns into rows. + +**Example** + +
+|> UNPIVOT (count FOR user_location IN (London, Bangalore, Madrid))
+|> ORDER BY year, cnt
+
+ +### `ASSERT` + +
+|> ASSERT expression [, payload_expression [, ...]]
+
+ +**Description** + +Evaluates an expression over all rows of an input table to verify that the +expression is true or raise an assertion error if it's false. + +The expression must evaluate to a boolean value. When the expression evaluates +to `TRUE`, the input table passes through the `ASSERT` operator unchanged. When +the expression evaluates to `FALSE` or `NULL`, the query fails with an +`Assertion failed` error. + +One or more optional payload expressions can be provided. If the assertion +fails, the payload expression values are computed, converted to strings, and +included in the error message, separated by spaces. + +If no payload is provided, the error message includes the SQL text of the +assertion expression. + +The `ASSERT` operator has no equivalent operation in standard syntax. + The [`ASSERT` statement][assert-statement] is +a related feature that verifies that a single expression +is true. + +**Example** + +
+FROM table
+|> ASSERT count != 0, "Count is zero for user", userId
+|> SELECT total / count AS average
+
+ + + +[query-syntax]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md + +[from-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#from_clause + +[select-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#select_list + +[select-star]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#select_ + +[select-replace]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#select_replace + +[select-except]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#select_except + +[set-operators]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#set_operators + +[drop-statement]: https://github.com/google/zetasql/blob/master/docs/data-definition-language.md#drop + +[where-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#where_clause + +[aggregate-pipes-operator]: #aggregategroup_by + +[extend-pipes-operator]: #extend + +[select-pipes-operator]: #select + +[set-pipes-operator]: #set + +[drop-pipes-operator]: #drop + +[as-pipes-operator]: #as + +[order-by-pipes-operator]: #order_by + +[order-by-with-aggregate]: #shorthand_syntax_for_aggregate_with_order_by + +[limit-offset-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#limit_and_offset_clause + +[having-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#having_clause + +[qualify-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#qualify_clause + +[aggregate-functions]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md + +[group-by-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#group_by_clause + +[order-by-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#order_by_clause + +[join-operation]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#join_types + +[on-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#on_clause + +[window-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#window_clause + +[window-functions]: https://github.com/google/zetasql/blob/master/docs/window-function-calls.md + +[over-clause]: https://github.com/google/zetasql/blob/master/docs/window-function-calls.md#def_over_clause + +[window-pipes-operator]: #window + +[table-function-calls]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#table-function-calls + +[tvf]: https://github.com/google/zetasql/blob/master/docs/table-functions.md#tvfs + +[tablesample-operator]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#tablesample_operator + +[using-aliases]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#using_aliases + +[pivot-operator]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#pivot_operator + +[unpivot-operator]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#unpivot_operator + +[assert-statement]: https://github.com/google/zetasql/blob/master/docs/debugging-statements.md#assert + + + diff --git a/docs/procedural-language.md b/docs/procedural-language.md index 74fa2395b..eb7ef9073 100644 --- a/docs/procedural-language.md +++ b/docs/procedural-language.md @@ -1140,12 +1140,7 @@ multi-statement query with the error message supplied. **Description** Calls a [procedure][procedures] with an argument list. -`procedure_argument` may be a variable or an expression. For `OUT` or `INOUT` -arguments, a variable passed as an argument must have the proper -ZetaSQL [type](data-types). - -The same variable may not appear multiple times as an `OUT` or `INOUT` -argument in the procedure's argument list. +`procedure_argument` may be a variable or an expression. The maximum depth of procedure calls is 50 frames. diff --git a/docs/protocol-buffers.md b/docs/protocol-buffers.md index bc952a747..39587d6a3 100644 --- a/docs/protocol-buffers.md +++ b/docs/protocol-buffers.md @@ -783,7 +783,7 @@ various accessed fields: message_field_y.field_b -" " (empty string) +"" (empty string) Field isn't set but parent message is set, so default value (empty string) is produced. diff --git a/docs/query-syntax.md b/docs/query-syntax.md index 377b3c629..5a45d5813 100644 --- a/docs/query-syntax.md +++ b/docs/query-syntax.md @@ -17,15 +17,7 @@ syntax notation rules: + Curly braces with vertical bars `{ a | b | c }`: Logical `OR`. Select one option. + Ellipsis `...`: Preceding item can repeat. -+ Red square brackets - [ ]: Required and - part of the syntax. -+ Red curly braces - { }: Required and - part of the syntax. -+ Red vertical bar - |: Required and - part of the syntax. ++ Double quotes `"`: Syntax wrapped in double quotes (`""`) is required. ## SQL syntax @@ -390,7 +382,7 @@ FROM from_clause[, ...] from_clause: from_item - [ unpivot_operator ] + [ { pivot_operator | unpivot_operator } ] [ tablesample_operator ] from_item: @@ -411,6 +403,11 @@ The `FROM` clause indicates the table or tables from which to retrieve rows, and specifies how to join those rows together to produce a single stream of rows for processing in the rest of the query. +#### `pivot_operator` + + +See [PIVOT operator][pivot-operator]. + #### `unpivot_operator` @@ -786,6 +783,369 @@ SELECT results FROM Coordinates, Coordinates.position.y[SAFE_OFFSET(1)] AS resul + `NULL` and empty arrays produce zero rows. + An array containing `NULL` values produces rows containing `NULL` values. +## `PIVOT` operator + + +
+FROM from_item[, ...] pivot_operator
+
+pivot_operator:
+  PIVOT(
+    aggregate_function_call [as_alias][, ...]
+    FOR input_column
+    IN ( pivot_column [as_alias][, ...] )
+  ) [AS alias]
+
+as_alias:
+  [AS] alias
+
+ +The `PIVOT` operator rotates rows into columns, using aggregation. +`PIVOT` is part of the `FROM` clause. + ++ `PIVOT` can be used to modify any table expression. ++ A `WITH OFFSET` clause immediately preceding the `PIVOT` operator is not + allowed. + +Conceptual example: + +```sql +-- Before PIVOT is used to rotate sales and quarter into Q1, Q2, Q3, Q4 columns: +/*---------+-------+---------+------* + | product | sales | quarter | year | + +---------+-------+---------+------| + | Kale | 51 | Q1 | 2020 | + | Kale | 23 | Q2 | 2020 | + | Kale | 45 | Q3 | 2020 | + | Kale | 3 | Q4 | 2020 | + | Kale | 70 | Q1 | 2021 | + | Kale | 85 | Q2 | 2021 | + | Apple | 77 | Q1 | 2020 | + | Apple | 0 | Q2 | 2020 | + | Apple | 1 | Q1 | 2021 | + *---------+-------+---------+------*/ + +-- After PIVOT is used to rotate sales and quarter into Q1, Q2, Q3, Q4 columns: +/*---------+------+----+------+------+------* + | product | year | Q1 | Q2 | Q3 | Q4 | + +---------+------+----+------+------+------+ + | Apple | 2020 | 77 | 0 | NULL | NULL | + | Apple | 2021 | 1 | NULL | NULL | NULL | + | Kale | 2020 | 51 | 23 | 45 | 3 | + | Kale | 2021 | 70 | 85 | NULL | NULL | + *---------+------+----+------+------+------*/ +``` + +**Definitions** + +Top-level definitions: + ++ `from_item`: The subquery on which to perform a pivot operation. + The `from_item` must [follow these rules](#rules_for_pivot_from_item). ++ `pivot_operator`: The pivot operation to perform on a `from_item`. ++ `alias`: An alias to use for an item in the query. + +`pivot_operator` definitions: + ++ `aggregate_function_call`: An aggregate function call that aggregates all + input rows such that `input_column` matches a particular value in + `pivot_column`. Each aggregation corresponding to a different `pivot_column` + value produces a different column in the output. + [Follow these rules](#rules_for_pivot_agg_function) when creating an + aggregate function call. ++ `input_column`: Takes a column and retrieves the row values for the + column, [following these rules](#rules_input_column). ++ `pivot_column`: A pivot column to create for each aggregate function + call. If an alias is not provided, a default alias is created. A pivot column + value type must match the value type in `input_column` so that the values can + be compared. It is possible to have a value in `pivot_column` that doesn't + match a value in `input_column`. Must be a constant and + [follow these rules](#rules_pivot_column). + +**Rules** + + +Rules for a `from_item` passed to `PIVOT`: + ++ The `from_item` is restricted to subqueries only. ++ The `from_item` may not produce a value table. ++ The `from_item` may not be a subquery using `SELECT AS STRUCT`. + + +Rules for `aggregate_function_call`: + ++ Must be an aggregate function. For example, `SUM`. ++ You may reference columns in a table passed to `PIVOT`, as + well as correlated columns, but may not access columns defined by the `PIVOT` + clause itself. ++ A table passed to `PIVOT` may be accessed through its alias if one is + provided. + + +Rules for `input_column`: + ++ May access columns from the input table, as well as correlated columns, + not columns defined by the `PIVOT` clause, itself. ++ Evaluated against each row in the input table; aggregate and window function + calls are prohibited. ++ Non-determinism is okay. ++ The type must be groupable. ++ The input table may be accessed through its alias if one is provided. + + +Rules for `pivot_column`: + ++ A `pivot_column` must be a constant. ++ Named constants, such as variables, are not supported. ++ Query parameters are not supported. ++ If a name is desired for a named constant or query parameter, + specify it explicitly with an alias. ++ Corner cases exist where a distinct `pivot_column`s can end up with the same + default column names. For example, an input column might contain both a + `NULL` value and the string literal `"NULL"`. When this happens, multiple + pivot columns are created with the same name. To avoid this situation, + use aliases for pivot column names. ++ If a `pivot_column` doesn't specify an alias, a column name is constructed as + follows: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FromToExample
NULLNULL + Input: NULL
+ Output: "NULL"
+
+ +INT32
INT64
UINT32
UINT64
NUMERIC
BIGNUMERIC
+
+ The number in string format with the following rules: +
    +
  • + Positive numbers are preceded with _. +
  • +
  • + Negative numbers are preceded with minus_. +
  • +
  • + A decimal point is replaced with _point_. +
  • +
+
+ Input: 1
+ Output: _1
+
+ Input: -1
+ Output: minus_1
+
+ Input: 1.0
+ Output: _1_point_0
+
BOOLTRUE or FALSE. + Input: TRUE
+ Output: TRUE
+
+ Input: FALSE
+ Output: FALSE
+
STRINGThe string value. + Input: "PlayerName"
+ Output: PlayerName
+
DATEThe date in _YYYY_MM_DD format. + Input: DATE '2013-11-25'
+ Output: _2013_11_25
+
ENUMThe name of the enumeration constant. + Input: COLOR.RED
+ Output: RED
+
STRUCT + A string formed by computing the pivot_column name for each + field and joining the results together with an underscore. The following + rules apply: +
    +
  • + If the field is named: + <field_name>_<pivot_column_name_for_field_name>. +
  • +
  • + If the field is unnamed: + <pivot_column_name_for_field_name>. +
  • +
+

+ <pivot_column_name_for_field_name> is determined by + applying the rules in this table, recursively. If no rule is available + for any STRUCT field, the entire pivot column is unnamed. +

+

+ Due to implicit type coercion from the IN list values to + the type of <value-expression>, field names must be + present in input_column to have an effect on the names of + the pivot columns. +

+
+ Input: STRUCT("one", "two")
+ Output: one_two
+
+ Input: STRUCT("one" AS a, "two" AS b)
+ Output: one_a_two_b
+
All other data typesNot supported. You must provide an alias. +
+ +**Examples** + +The following examples reference a table called `Produce` that looks like this: + +```sql +WITH Produce AS ( + SELECT 'Kale' as product, 51 as sales, 'Q1' as quarter, 2020 as year UNION ALL + SELECT 'Kale', 23, 'Q2', 2020 UNION ALL + SELECT 'Kale', 45, 'Q3', 2020 UNION ALL + SELECT 'Kale', 3, 'Q4', 2020 UNION ALL + SELECT 'Kale', 70, 'Q1', 2021 UNION ALL + SELECT 'Kale', 85, 'Q2', 2021 UNION ALL + SELECT 'Apple', 77, 'Q1', 2020 UNION ALL + SELECT 'Apple', 0, 'Q2', 2020 UNION ALL + SELECT 'Apple', 1, 'Q1', 2021) +SELECT * FROM Produce + +/*---------+-------+---------+------* + | product | sales | quarter | year | + +---------+-------+---------+------| + | Kale | 51 | Q1 | 2020 | + | Kale | 23 | Q2 | 2020 | + | Kale | 45 | Q3 | 2020 | + | Kale | 3 | Q4 | 2020 | + | Kale | 70 | Q1 | 2021 | + | Kale | 85 | Q2 | 2021 | + | Apple | 77 | Q1 | 2020 | + | Apple | 0 | Q2 | 2020 | + | Apple | 1 | Q1 | 2021 | + *---------+-------+---------+------*/ +``` + +With the `PIVOT` operator, the rows in the `quarter` column are rotated into +these new columns: `Q1`, `Q2`, `Q3`, `Q4`. The aggregate function `SUM` is +implicitly grouped by all unaggregated columns other than the `pivot_column`: +`product` and `year`. + +```sql +SELECT * FROM + Produce + PIVOT(SUM(sales) FOR quarter IN ('Q1', 'Q2', 'Q3', 'Q4')) + +/*---------+------+----+------+------+------* + | product | year | Q1 | Q2 | Q3 | Q4 | + +---------+------+----+------+------+------+ + | Apple | 2020 | 77 | 0 | NULL | NULL | + | Apple | 2021 | 1 | NULL | NULL | NULL | + | Kale | 2020 | 51 | 23 | 45 | 3 | + | Kale | 2021 | 70 | 85 | NULL | NULL | + *---------+------+----+------+------+------*/ +``` + +If you don't include `year`, then `SUM` is grouped only by `product`. + +```sql +SELECT * FROM + (SELECT product, sales, quarter FROM Produce) + PIVOT(SUM(sales) FOR quarter IN ('Q1', 'Q2', 'Q3', 'Q4')) + +/*---------+-----+-----+------+------* + | product | Q1 | Q2 | Q3 | Q4 | + +---------+-----+-----+------+------+ + | Apple | 78 | 0 | NULL | NULL | + | Kale | 121 | 108 | 45 | 3 | + *---------+-----+-----+------+------*/ +``` + +You can select a subset of values in the `pivot_column`: + +```sql +SELECT * FROM + (SELECT product, sales, quarter FROM Produce) + PIVOT(SUM(sales) FOR quarter IN ('Q1', 'Q2', 'Q3')) + +/*---------+-----+-----+------* + | product | Q1 | Q2 | Q3 | + +---------+-----+-----+------+ + | Apple | 78 | 0 | NULL | + | Kale | 121 | 108 | 45 | + *---------+-----+-----+------*/ +``` + +```sql +SELECT * FROM + (SELECT sales, quarter FROM Produce) + PIVOT(SUM(sales) FOR quarter IN ('Q1', 'Q2', 'Q3')) + +/*-----+-----+----* + | Q1 | Q2 | Q3 | + +-----+-----+----+ + | 199 | 108 | 45 | + *-----+-----+----*/ +``` + +You can include multiple aggregation functions in the `PIVOT`. In this case, you +must specify an alias for each aggregation. These aliases are used to construct +the column names in the resulting table. + +```sql +SELECT * FROM + (SELECT product, sales, quarter FROM Produce) + PIVOT(SUM(sales) AS total_sales, COUNT(*) AS num_records FOR quarter IN ('Q1', 'Q2')) + +/*--------+----------------+----------------+----------------+----------------* + |product | total_sales_Q1 | num_records_Q1 | total_sales_Q2 | num_records_Q2 | + +--------+----------------+----------------+----------------+----------------+ + | Kale | 121 | 2 | 108 | 2 | + | Apple | 78 | 2 | 0 | 1 | + *--------+----------------+----------------+----------------+----------------*/ +``` + ## `UNPIVOT` operator @@ -827,6 +1187,7 @@ The `UNPIVOT` operator rotates columns into rows. `UNPIVOT` is part of the expression. + A `WITH OFFSET` clause immediately preceding the `UNPIVOT` operator is not allowed. ++ `PIVOT` aggregations cannot be reversed with `UNPIVOT`. Conceptual example: @@ -2580,6 +2941,7 @@ GROUP BY group_by_specification group_by_specification: { groupable_items + | ALL | grouping_sets_specification | rollup_specification | cube_specification @@ -2600,6 +2962,8 @@ present in the `SELECT` list, or to eliminate redundancy in the output. + `groupable_items`: Group rows in a table that share common values for certain columns. To learn more, see [Group rows by groupable items][group-by-groupable-item]. ++ `ALL`: Automatically group rows. To learn more, see + [Group rows automatically][group-by-all]. + `grouping_sets_specification`: Group rows with the `GROUP BY GROUPING SETS` clause. To learn more, see [Group rows by `GROUPING SETS`][group-by-grouping-sets]. @@ -2742,6 +3106,137 @@ GROUP BY 2, 3; +--------------+----------+-----------*/ ``` +### Group rows by `ALL` + + +
+GROUP BY ALL
+
+ +**Description** + +The `GROUP BY ALL` clause groups rows by inferring grouping keys from the +`SELECT` items. + +The following `SELECT` items are excluded from the `GROUP BY ALL` clause: + ++ Expressions that include [aggregate functions][aggregate-function-calls]. ++ Expressions that include [window functions][window-function-calls]. ++ Expressions that do not reference a name from the `FROM` clause. + This includes: + + Constants + + Query parameters + + Correlated column references + + Expressions that only reference `GROUP BY` keys inferred from + other `SELECT` items. + +After exclusions are applied, an error is produced if any remaining `SELECT` +item includes a volatile function or has a non-groupable type. + +If the set of inferred grouping keys is empty after exclusions are applied, all +input rows are considered a single group for aggregation. This +behavior is equivalent to writing `GROUP BY ()`. + +**Examples** + +In the following example, the query groups rows by `first_name` and +`last_name`. `total_points` is excluded because it represents an +aggregate function. + +```sql +WITH PlayerStats AS ( + SELECT 'Adams' as LastName, 'Noam' as FirstName, 3 as PointsScored UNION ALL + SELECT 'Buchanan', 'Jie', 0 UNION ALL + SELECT 'Coolidge', 'Kiran', 1 UNION ALL + SELECT 'Adams', 'Noam', 4 UNION ALL + SELECT 'Buchanan', 'Jie', 13) +SELECT + SUM(PointsScored) AS total_points, + FirstName AS first_name, + LastName AS last_name +FROM PlayerStats +GROUP BY ALL; + +/*--------------+------------+-----------+ + | total_points | first_name | last_name | + +--------------+------------+-----------+ + | 7 | Noam | Adams | + | 13 | Jie | Buchanan | + | 1 | Kiran | Coolidge | + +--------------+------------+-----------*/ +``` + +If the select list contains an analytic function, the query groups rows by +`first_name` and `last_name`. `total_people` is excluded because it +contains a window function. + +```sql +WITH PlayerStats AS ( + SELECT 'Adams' as LastName, 'Noam' as FirstName, 3 as PointsScored UNION ALL + SELECT 'Buchanan', 'Jie', 0 UNION ALL + SELECT 'Coolidge', 'Kiran', 1 UNION ALL + SELECT 'Adams', 'Noam', 4 UNION ALL + SELECT 'Buchanan', 'Jie', 13) +SELECT + COUNT(*) OVER () AS total_people, + FirstName AS first_name, + LastName AS last_name +FROM PlayerStats +GROUP BY ALL; + +/*--------------+------------+-----------+ + | total_people | first_name | last_name | + +--------------+------------+-----------+ + | 3 | Noam | Adams | + | 3 | Jie | Buchanan | + | 3 | Kiran | Coolidge | + +--------------+------------+-----------*/ +``` + +If multiple `SELECT` items reference the same `FROM` item, and any of them is +a path expression prefix of another, only the prefix path is used for grouping. +In the following example, `coordinates` is excluded because `x_coordinate` and +`y_coordinate` have already referenced `Values.x` and `Values.y` in the +`FROM` clause, and they are prefixes of the path expression used in +`x_coordinate`: + +```sql +WITH Values AS ( + SELECT 1 AS x, 2 AS y + UNION ALL SELECT 1 AS x, 4 AS y + UNION ALL SELECT 2 AS x, 5 AS y +) +SELECT + Values.x AS x_coordinate, + Values.y AS y_coordinate, + [Values.x, Values.y] AS coordinates +FROM Values +GROUP BY ALL + +/*--------------+--------------+-------------+ + | x_coordinate | y_coordinate | coordinates | + +--------------+--------------+-------------+ + | 1 | 4 | [1, 4] | + | 1 | 2 | [1, 2] | + | 2 | 5 | [2, 5] | + +--------------+--------------+-------------*/ +``` + +In the following example, the inferred set of grouping keys is empty. The query +returns one row even when the input contains zero rows. + +```sql +SELECT COUNT(*) AS num_rows +FROM UNNEST([]) +GROUP BY ALL + +/*----------+ + | num_rows | + +----------+ + | 0 | + +----------*/ +``` + ### Group rows by `GROUPING SETS` @@ -4888,6 +5383,93 @@ SELECT * FROM B -- Error ``` +## `AGGREGATION_THRESHOLD` clause + + +
+WITH AGGREGATION_THRESHOLD OPTIONS (
+  threshold = threshold_amount,
+  privacy_unit_column = column_name
+)
+
+ +**Description** + +Use the `AGGREGATION_THRESHOLD` clause to enforce an +aggregation threshold. This clause counts the number of distinct privacy units +(represented by the privacy unit column) for each group, and only outputs the +groups where the distinct privacy unit count satisfies the +aggregation threshold. + +**Definitions:** + ++ `threshold`: The minimum number of distinct + privacy units (privacy unit column values) that need to contribute to each row + in the query results. If a potential row doesn't satisfy this threshold, + that row is omitted from the query results. `threshold_amount` must be + a positive `INT64` value. ++ `privacy_unit_column`: The column that represents the + privacy unit column. Replace `column_name` with the + path expression for the column. The first identifier in the path can start + with either a table name or a column name that's visible in the + `FROM` clause. + +**Details** + + + +The following functions can be used on any column in a query with the +`AGGREGATION_THRESHOLD` clause, including the commonly used +`COUNT`, `SUM`, and `AVG` functions: + ++ `APPROX_COUNT_DISTINCT` ++ `AVG` ++ `COUNT` ++ `COUNTIF` ++ `LOGICAL_OR` ++ `LOGICAL_AND` ++ `SUM` ++ `COVAR_SAMP` ++ `STDDEV_POP` ++ `STDDEV_SAMP` ++ `VAR_POP` ++ `VAR_SAMP` + +**Example** + +In the following example, an aggregation threshold is enforced +on a query. Notice that some privacy units are dropped because +there aren't enough distinct instances. + +```sql +WITH ExamTable AS ( + SELECT "Hansen" AS last_name, "P91" AS test_id, 510 AS test_score UNION ALL + SELECT "Wang", "U25", 500 UNION ALL + SELECT "Wang", "C83", 520 UNION ALL + SELECT "Wang", "U25", 460 UNION ALL + SELECT "Hansen", "C83", 420 UNION ALL + SELECT "Hansen", "C83", 560 UNION ALL + SELECT "Devi", "U25", 580 UNION ALL + SELECT "Devi", "P91", 480 UNION ALL + SELECT "Ivanov", "U25", 490 UNION ALL + SELECT "Ivanov", "P91", 540 UNION ALL + SELECT "Silva", "U25", 550) +SELECT WITH AGGREGATION_THRESHOLD + OPTIONS(threshold=3, privacy_unit_column=last_name) + test_id, + COUNT(DISTINCT last_name) AS student_count, + AVG(test_score) AS avg_test_score +FROM ExamTable +GROUP BY test_id; + +/*---------+---------------+----------------* + | test_id | student_count | avg_test_score | + +---------+---------------+----------------+ + | P91 | 3 | 510.0 | + | U25 | 4 | 516.0 | + *---------+---------------+----------------*/ +``` + ## Differential privacy clause @@ -6121,6 +6703,10 @@ Results: [group-by-col-ordinals]: #group_by_col_ordinals +[group-by-all]: #group_by_all + +[aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md + [group-by-rollup]: #group_by_rollup [group-by-cube]: #group_by_cube @@ -6133,6 +6719,8 @@ Results: [grouping-function]: https://github.com/google/zetasql/blob/master/docs/aggregate_functions.md#grouping +[pivot-operator]: #pivot_operator + [unpivot-operator]: #unpivot_operator [tablesample-operator]: #tablesample_operator @@ -6219,6 +6807,10 @@ Results: [dp-functions]: https://github.com/google/zetasql/blob/master/docs/aggregate-dp-functions.md +[analysis-rules]: https://github.com/google/zetasql/blob/master/docs/analysis-rules.md + +[privacy-view]: https://github.com/google/zetasql/blob/master/docs/analysis-rules.md#privacy_view + [coalesce]: https://github.com/google/zetasql/blob/master/docs/conditional_expressions.md#coalesce diff --git a/docs/range-functions.md b/docs/range-functions.md new file mode 100644 index 000000000..7618114d0 --- /dev/null +++ b/docs/range-functions.md @@ -0,0 +1,841 @@ + + + + +# Range functions + +ZetaSQL supports the following range functions. + +### Function list + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSummary
GENERATE_RANGE_ARRAY + +Splits a range into an array of subranges.
RANGE + + + Constructs a range of DATE, DATETIME, + or TIMESTAMP values. +
RANGE_CONTAINS + + + Signature 1: Checks if one range is in another range. +

+ Signature 2: Checks if a value is in a range. +
RANGE_END + +Gets the upper bound of a range.
RANGE_INTERSECT + +Gets a segment of two ranges that intersect.
RANGE_OVERLAPS + +Checks if two ranges overlap.
RANGE_SESSIONIZE + +Produces a table of sessionized ranges.
RANGE_START + +Gets the lower bound of a range.
+ +### `GENERATE_RANGE_ARRAY` + +```sql +GENERATE_RANGE_ARRAY(range_to_split, step_interval) +``` + +```sql +GENERATE_RANGE_ARRAY(range_to_split, step_interval, include_last_partial_range) +``` + +**Description** + +Splits a range into an array of subranges. + +**Definitions** + ++ `range_to_split`: The `RANGE` value to split. ++ `step_interval`: The `INTERVAL` value, which determines the maximum size of + each subrange in the resulting array. An + [interval single date and time part][interval-single] + is supported, but an interval range of date and time parts is not. + + + If `range_to_split` is `RANGE`, these interval + date parts are supported: `YEAR` to `DAY`. + + + If `range_to_split` is `RANGE`, these interval + date and time parts are supported: `YEAR` to `SECOND`. + + + If `range_to_split` is `RANGE`, these interval + date and time parts are supported: `DAY` to `SECOND`. ++ `include_last_partial_range`: A `BOOL` value, which determines whether or + not to include the last subrange if it's a partial subrange. + If this argument is not specified, the default value is `TRUE`. + + + `TRUE` (default): The last subrange is included, even if it's + smaller than `step_interval`. + + + `FALSE`: Exclude the last subrange if it's smaller than + `step_interval`. + +**Details** + +Returns `NULL` if any input is` NULL`. + +**Return type** + +`ARRAY>` + +**Examples** + +In the following example, a date range between `2020-01-01` and `2020-01-06` +is split into an array of subranges that are one day long. There are +no partial ranges. + +```sql +SELECT GENERATE_RANGE_ARRAY( + RANGE(DATE '2020-01-01', DATE '2020-01-06'), + INTERVAL 1 DAY) AS results; + +/*----------------------------+ + | results | + +----------------------------+ + | [ | + | [2020-01-01, 2020-01-02), | + | [2020-01-02, 2020-01-03), | + | [2020-01-03, 2020-01-04), | + | [2020-01-04, 2020-01-05), | + | [2020-01-05, 2020-01-06), | + | ] | + +----------------------------*/ +``` + +In the following examples, a date range between `2020-01-01` and `2020-01-06` +is split into an array of subranges that are two days long. The final subrange +is smaller than two days: + +```sql +SELECT GENERATE_RANGE_ARRAY( + RANGE(DATE '2020-01-01', DATE '2020-01-06'), + INTERVAL 2 DAY) AS results; + +/*----------------------------+ + | results | + +----------------------------+ + | [ | + | [2020-01-01, 2020-01-03), | + | [2020-01-03, 2020-01-05), | + | [2020-01-05, 2020-01-06) | + | ] | + +----------------------------*/ +``` + +```sql +SELECT GENERATE_RANGE_ARRAY( + RANGE(DATE '2020-01-01', DATE '2020-01-06'), + INTERVAL 2 DAY, + TRUE) AS results; + +/*----------------------------+ + | results | + +----------------------------+ + | [ | + | [2020-01-01, 2020-01-03), | + | [2020-01-03, 2020-01-05), | + | [2020-01-05, 2020-01-06) | + | ] | + +----------------------------*/ +``` + +In the following example, a date range between `2020-01-01` and `2020-01-06` +is split into an array of subranges that are two days long, but the final +subrange is excluded because it's smaller than two days: + +```sql +SELECT GENERATE_RANGE_ARRAY( + RANGE(DATE '2020-01-01', DATE '2020-01-06'), + INTERVAL 2 DAY, + FALSE) AS results; + +/*----------------------------+ + | results | + +----------------------------+ + | [ | + | [2020-01-01, 2020-01-03), | + | [2020-01-03, 2020-01-05) | + | ] | + +----------------------------*/ +``` + +[interval-single]: https://github.com/google/zetasql/blob/master/docs/data-types.md#single_datetime_part_interval + +### `RANGE` + +```sql +RANGE(lower_bound, upper_bound) +``` + +**Description** + +Constructs a range of [`DATE`][date-type], [`DATETIME`][datetime-type], or +[`TIMESTAMP`][timestamp-type] values. + +**Definitions** + ++ `lower_bound`: The range starts from this value. This can be a + `DATE`, `DATETIME`, or `TIMESTAMP` value. If this value is `NULL`, the range + doesn't include a lower bound. ++ `upper_bound`: The range ends before this value. This can be a + `DATE`, `DATETIME`, or `TIMESTAMP` value. If this value is `NULL`, the range + doesn't include an upper bound. + +**Details** + +`lower_bound` and `upper_bound` must be of the same data type. + +Produces an error if `lower_bound` is greater than or equal to `upper_bound`. +To return `NULL` instead, add the `SAFE.` prefix to the function name. + +**Return type** + +`RANGE`, where `T` is the same data type as the input. + +**Examples** + +The following query constructs a date range: + +```sql +SELECT RANGE(DATE '2022-12-01', DATE '2022-12-31') AS results; + +/*--------------------------+ + | results | + +--------------------------+ + | [2022-12-01, 2022-12-31) | + +--------------------------*/ +``` + +The following query constructs a datetime range: + +```sql +SELECT RANGE(DATETIME '2022-10-01 14:53:27', + DATETIME '2022-10-01 16:00:00') AS results; + +/*---------------------------------------------+ + | results | + +---------------------------------------------+ + | [2022-10-01 14:53:27, 2022-10-01T16:00:00) | + +---------------------------------------------*/ +``` + +The following query constructs a timestamp range: + +```sql +SELECT RANGE(TIMESTAMP '2022-10-01 14:53:27 America/Los_Angeles', + TIMESTAMP '2022-10-01 16:00:00 America/Los_Angeles') AS results; + +-- Results depend upon where this query was executed. +/*-----------------------------------------------------------------+ + | results | + +-----------------------------------------------------------------+ + | [2022-10-01 21:53:27.000000+00, 2022-10-01 23:00:00.000000+00) | + +-----------------------------------------------------------------*/ +``` + +The following query constructs a date range with no lower bound: + +```sql +SELECT RANGE(NULL, DATE '2022-12-31') AS results; + +/*-------------------------+ + | results | + +-------------------------+ + | [UNBOUNDED, 2022-12-31) | + +-------------------------*/ +``` + +The following query constructs a date range with no upper bound: + +```sql +SELECT RANGE(DATE '2022-10-01', NULL) AS results; + +/*--------------------------+ + | results | + +--------------------------+ + | [2022-10-01, UNBOUNDED) | + +--------------------------*/ +``` + +[timestamp-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#timestamp_type + +[date-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#date_type + +[datetime-type]: https://github.com/google/zetasql/blob/master/docs/data-types.md#datetime_type + +### `RANGE_CONTAINS` + ++ [Signature 1][range_contains-sig1]: Checks if every value in one range is + in another range. ++ [Signature 2][range_contains-sig2]: Checks if a value is in a range. + +#### Signature 1 + +```sql +RANGE_CONTAINS(outer_range, inner_range) +``` + +**Description** + +Checks if the inner range is in the outer range. + +**Definitions** + ++ `outer_range`: The `RANGE` value to search within. ++ `inner_range`: The `RANGE` value to search for in `outer_range`. + +**Details** + +Returns `TRUE` if `inner_range` exists in `outer_range`. +Otherwise, returns `FALSE`. + +`T` must be of the same type for all inputs. + +**Return type** + +`BOOL` + +**Examples** + +In the following query, the inner range is in the outer range: + +```sql +SELECT RANGE_CONTAINS( + RANGE '[2022-01-01, 2023-01-01)', + RANGE '[2022-04-01, 2022-07-01)') AS results; + +/*---------+ + | results | + +---------+ + | TRUE | + +---------*/ +``` + +In the following query, the inner range is not in the outer range: + +```sql +SELECT RANGE_CONTAINS( + RANGE '[2022-01-01, 2023-01-01)', + RANGE '[2023-01-01, 2023-04-01)') AS results; + +/*---------+ + | results | + +---------+ + | FALSE | + +---------*/ +``` + +#### Signature 2 + +```sql +RANGE_CONTAINS(range_to_search, value_to_find) +``` + +**Description** + +Checks if a value is in a range. + +**Definitions** + ++ `range_to_search`: The `RANGE` value to search within. ++ `value_to_find`: The value to search for in `range_to_search`. + +**Details** + +Returns `TRUE` if `value_to_find` exists in `range_to_search`. +Otherwise, returns `FALSE`. + +The data type for `value_to_find` must be the same data type as `T`in +`range_to_search`. + +**Return type** + +`BOOL` + +**Examples** + +In the following query, the value `2022-04-01` is found in the range +`[2022-01-01, 2023-01-01)`: + +```sql +SELECT RANGE_CONTAINS( + RANGE '[2022-01-01, 2023-01-01)', + DATE '2022-04-01') AS results; + +/*---------+ + | results | + +---------+ + | TRUE | + +---------*/ +``` + +In the following query, the value `2023-04-01` is not found in the range +`[2022-01-01, 2023-01-01)`: + +```sql +SELECT RANGE_CONTAINS( + RANGE '[2022-01-01, 2023-01-01)', + DATE '2023-04-01') AS results; + +/*---------+ + | results | + +---------+ + | FALSE | + +---------*/ +``` + +[range_contains-sig1]: #signature_1 + +[range_contains-sig2]: #signature_2 + +### `RANGE_END` + +```sql +RANGE_END(range_to_check) +``` + +**Description** + +Gets the upper bound of a range. + +**Definitions** + ++ `range_to_check`: The `RANGE` value. + +**Details** + +Returns `NULL` if the upper bound in `range_value` is `UNBOUNDED`. + +Returns `NULL` if `range_to_check` is `NULL`. + +**Return type** + +`T` in `range_value` + +**Examples** + +In the following query, the upper bound of the range is retrieved: + +```sql +SELECT RANGE_END(RANGE '[2022-12-01, 2022-12-31)') AS results; + +/*------------+ + | results | + +------------+ + | 2022-12-31 | + +------------*/ +``` + +In the following query, the upper bound of the range is unbounded, so +`NULL` is returned: + +```sql +SELECT RANGE_END(RANGE '[2022-12-01, UNBOUNDED)') AS results; + +/*------------+ + | results | + +------------+ + | NULL | + +------------*/ +``` + +### `RANGE_INTERSECT` + +```sql +RANGE_INTERSECT(range_a, range_b) +``` + +**Description** + +Gets a segment of two ranges that intersect. + +**Definitions** + ++ `range_a`: The first `RANGE` value. ++ `range_b`: The second `RANGE` value. + +**Details** + +Returns `NULL` if any input is` NULL`. + +Produces an error if `range_a` and `range_b` don't overlap. To return +`NULL` instead, add the `SAFE.` prefix to the function name. + +`T` must be of the same type for all inputs. + +**Return type** + +`RANGE` + +**Examples** + +```sql +SELECT RANGE_INTERSECT( + RANGE '[2022-02-01, 2022-09-01)', + RANGE '[2021-06-15, 2022-04-15)') AS results; + +/*--------------------------+ + | results | + +--------------------------+ + | [2022-02-01, 2022-04-15) | + +--------------------------*/ +``` + +```sql +SELECT RANGE_INTERSECT( + RANGE '[2022-02-01, UNBOUNDED)', + RANGE '[2021-06-15, 2022-04-15)') AS results; + +/*--------------------------+ + | results | + +--------------------------+ + | [2022-02-01, 2022-04-15) | + +--------------------------*/ +``` + +```sql +SELECT RANGE_INTERSECT( + RANGE '[2022-02-01, UNBOUNDED)', + RANGE '[2021-06-15, UNBOUNDED)') AS results; + +/*-------------------------+ + | results | + +-------------------------+ + | [2022-02-01, UNBOUNDED) | + +-------------------------*/ +``` + +### `RANGE_OVERLAPS` + +```sql +RANGE_OVERLAPS(range_a, range_b) +``` + +**Description** + +Checks if two ranges overlap. + +**Definitions** + ++ `range_a`: The first `RANGE` value. ++ `range_b`: The second `RANGE` value. + +**Details** + +Returns `TRUE` if a part of `range_a` intersects with `range_b`, otherwise +returns `FALSE`. + +`T` must be of the same type for all inputs. + +To get the part of the range that overlaps, use the +[`RANGE_INTERSECT`][range-intersect] function. + +**Return type** + +`BOOL` + +**Examples** + +In the following query, the first and second ranges overlap between +`2022-02-01` and `2022-04-15`: + +```sql +SELECT RANGE_OVERLAPS( + RANGE '[2022-02-01, 2022-09-01)', + RANGE '[2021-06-15, 2022-04-15)') AS results; + +/*---------+ + | results | + +---------+ + | TRUE | + +---------*/ +``` + +In the following query, the first and second ranges don't overlap: + +```sql +SELECT RANGE_OVERLAPS( + RANGE '[2020-02-01, 2020-09-01)', + RANGE '[2021-06-15, 2022-04-15)') AS results; + +/*---------+ + | results | + +---------+ + | FALSE | + +---------*/ +``` + +In the following query, the first and second ranges overlap between +`2022-02-01` and `UNBOUNDED`: + +```sql +SELECT RANGE_OVERLAPS( + RANGE '[2022-02-01, UNBOUNDED)', + RANGE '[2021-06-15, UNBOUNDED)') AS results; + +/*---------+ + | results | + +---------+ + | TRUE | + +---------*/ +``` + +[range-intersect]: #range_intersect + +### `RANGE_SESSIONIZE` + +```sql +RANGE_SESSIONIZE( + TABLE table_name, + range_column, + partitioning_columns +) +``` + +```sql +RANGE_SESSIONIZE( + TABLE table_name, + range_column, + partitioning_columns, + sessionize_option +) +``` + +**Description** + +Produces a table of sessionized ranges. + +**Definitions** + ++ `table_name`: A table expression that represents the name of the table to + construct. This can represent any relation with `range_column`. ++ `range_column`: A `STRING` literal that indicates which `RANGE` column + in a table contains the data to sessionize. ++ `partitioning_columns`: An `ARRAY` literal that indicates which + columns should partition the data before the data is sessionized. ++ `sessionize_option`: A `STRING` value that describes how order-adjacent + ranges are sessionized. Your choices are as follows: + + + `MEETS` (default): Ranges that meet or overlap are sessionized. + + + `OVERLAPS`: Only a range that is overlapped by another range is + sessionized. + + If this argument is not provided, `MEETS` is used by default. + +**Details** + +This function produces a table that includes all columns in the +input table and an additional `RANGE` column called +`session_range`, which indicates the start and end of a session. The +start and end of each session is determined by the `sessionize_option` +argument. + +**Return type** + +`TABLE` + +**Examples** + +The examples in this section reference the following table called +`my_sessionized_range_table` in a dataset called `mydataset`: + +```sql +INSERT mydataset.my_sessionized_range_table (emp_id, dept_id, duration) +VALUES(10, 1000, RANGE '[2010-01-10, 2010-03-10)'), + (10, 2000, RANGE '[2010-03-10, 2010-07-15)'), + (10, 2000, RANGE '[2010-06-15, 2010-08-18)'), + (20, 2000, RANGE '[2010-03-10, 2010-07-20)'), + (20, 1000, RANGE '[2020-05-10, 2020-09-20)'); + +SELECT * FROM mydataset.my_sessionized_range_table ORDER BY emp_id; + +/*--------+---------+--------------------------+ + | emp_id | dept_id | duration | + +--------+---------+--------------------------+ + | 10 | 1000 | [2010-01-10, 2010-03-10) | + | 10 | 2000 | [2010-03-10, 2010-07-15) | + | 10 | 2000 | [2010-06-15, 2010-08-18) | + | 20 | 2000 | [2010-03-10, 2010-07-20) | + | 20 | 1000 | [2020-05-10, 2020-09-20) | + +--------+---------+--------------------------*/ +``` + +In the following query, a table of sessionized data is produced for +`my_sessionized_range_table`, and only ranges that meet or overlap are +sessionized: + +```sql +SELECT + emp_id, duration, session_range +FROM + RANGE_SESSIONIZE( + TABLE mydataset.my_sessionized_range_table, + 'duration', + ['emp_id']) +ORDER BY emp_id; + +/*--------+--------------------------+--------------------------+ + | emp_id | duration | session_range | + +--------+--------------------------+--------------------------+ + | 10 | [2010-01-10, 2010-03-10) | [2010-01-10, 2010-08-18) | + | 10 | [2010-03-10, 2010-07-15) | [2010-01-10, 2010-08-18) | + | 10 | [2010-06-15, 2010-08-18) | [2010-01-10, 2010-08-18) | + | 20 | [2010-03-10, 2010-07-20) | [2010-03-10, 2010-07-20) | + | 20 | [2020-05-10, 2020-09-20) | [2020-05-10, 2020-09-20) | + +--------+-----------------------------------------------------*/ +``` + +In the following query, a table of sessionized data is produced for +`my_sessionized_range_table`, and only a range that is overlapped by another +range is sessionized: + +```sql +SELECT + emp_id, duration, session_range +FROM + RANGE_SESSIONIZE( + TABLE mydataset.my_sessionized_range_table, + 'duration', + ['emp_id'], + 'OVERLAPS') +ORDER BY emp_id; + +/*--------+--------------------------+--------------------------+ + | emp_id | duration | session_range | + +--------+--------------------------+--------------------------+ + | 10 | [2010-03-10, 2010-07-15) | [2010-03-10, 2010-08-18) | + | 10 | [2010-06-15, 2010-08-18) | [2010-03-10, 2010-08-18) | + | 10 | [2010-01-10, 2010-03-10) | [2010-01-10, 2010-03-10) | + | 20 | [2020-05-10, 2020-09-20) | [2020-05-10, 2020-09-20) | + | 20 | [2010-03-10, 2010-07-20) | [2010-03-10, 2010-07-20) | + +--------+-----------------------------------------------------*/ +``` + +If you need to normalize sessionized data, you can use a query similar to the +following: + +```sql +SELECT emp_id, session_range AS normalized FROM ( + SELECT emp_id, session_range + FROM RANGE_SESSIONIZE( + TABLE mydataset.my_sessionized_range_table, + 'duration', + ['emp_id'], + 'MEETS') +) +GROUP BY emp_id, normalized; + +/*--------+--------------------------+ + | emp_id | normalized | + +--------+--------------------------+ + | 20 | [2010-03-10, 2010-07-20) | + | 10 | [2010-01-10, 2010-08-18) | + | 20 | [2020-05-10, 2020-09-20) | + +--------+--------------------------*/ +``` + +### `RANGE_START` + +```sql +RANGE_START(range_to_check) +``` + +**Description** + +Gets the lower bound of a range. + +**Definitions** + ++ `range_to_check`: The `RANGE` value. + +**Details** + +Returns `NULL` if the lower bound of `range_value` is `UNBOUNDED`. + +Returns `NULL` if `range_to_check` is `NULL`. + +**Return type** + +`T` in `range_value` + +**Examples** + +In the following query, the lower bound of the range is retrieved: + +```sql +SELECT RANGE_START(RANGE '[2022-12-01, 2022-12-31)') AS results; + +/*------------+ + | results | + +------------+ + | 2022-12-01 | + +------------*/ +``` + +In the following query, the lower bound of the range is unbounded, so +`NULL` is returned: + +```sql +SELECT RANGE_START(RANGE '[UNBOUNDED, 2022-12-31)') AS results; + +/*------------+ + | results | + +------------+ + | NULL | + +------------*/ +``` + diff --git a/docs/recursive-ctes.md b/docs/recursive-ctes.md new file mode 100644 index 000000000..4efbae783 --- /dev/null +++ b/docs/recursive-ctes.md @@ -0,0 +1,413 @@ + + + + +# Work with recursive CTEs + +In ZetaSQL, a `WITH` clause contains one or more common table +expressions (CTEs) with temporary tables that you can reference in a query +expression. CTEs can be [non-recursive][non-recursive-cte], +[recursive][recursive-cte], or both. The [`RECURSIVE`][recursive-keyword] +keyword enables recursion in the `WITH` clause (`WITH RECURSIVE`). + +A recursive CTE can reference itself, a preceding CTE, or a subsequent CTE. A +non-recursive CTE can reference only preceding CTEs and can't reference itself. +Recursive CTEs run continuously until no new results are found, while +non-recursive CTEs run once. For these reasons, recursive CTEs are commonly used +for querying hierarchical data and graph data. + +For example, consider a graph where each row represents a node that can link to +other nodes. To find the transitive closure of all reachable nodes from a +particular start node without knowing the maximum number of hops, you would need +a recursive CTE in the query (`WITH RECURSIVE`). The recursive query would start +with the base case of the start node, and each step would compute the new unseen +nodes that can be reached from all the nodes seen so far up to the previous +step. The query concludes when no new nodes can be found. + +However, recursive CTEs can be computationally expensive, so before you use +them, review this guide and the [`WITH` clause][with-clause] section of the +ZetaSQL reference documentation. + +## Create a recursive CTE + + +To create a recursive CTE in ZetaSQL, use the +[`WITH RECURSIVE` clause][with-clause] as shown in the following example: + +```sql +WITH RECURSIVE + CTE_1 AS ( + (SELECT 1 AS iteration UNION ALL SELECT 1 AS iteration) + UNION ALL + SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 3 + ) +SELECT iteration FROM CTE_1 +ORDER BY 1 ASC +``` + +The preceding example produces the following results: + +```sql +/*-----------* + | iteration | + +-----------+ + | 1 | + | 1 | + | 2 | + | 2 | + | 3 | + | 3 | + *-----------*/ +``` + +To avoid duplicate rows so that only distinct rows become part of the final CTE +result, use `UNION DISTINCT` instead of `UNION ALL`: + +```sql +WITH RECURSIVE + CTE_1 AS ( + (SELECT 1 AS iteration UNION ALL SELECT 1 AS iteration) + UNION DISTINCT + SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 3 + ) +SELECT iteration FROM CTE_1 +ORDER BY 1 ASC +``` + +The preceding example produces the following results: + +```sql +/*-----------* + | iteration | + +-----------+ + | 1 | + | 2 | + | 3 | + *-----------*/ +``` + +A recursive CTE includes a base term, a union operator, and a recursive term. +The base term runs the first iteration of the recursive union operation. The +recursive term runs the remaining iterations and must include one self-reference +to the recursive CTE. Only the recursive term can include a self-reference. + +In the preceding example, the recursive CTE contains the following components: + ++ Recursive CTE name: `CTE_1` ++ Base term: `SELECT 1 AS iteration` ++ Union operator: `UNION ALL` (or + `UNION DISTINCT` for only distinct rows) ++ Recursive term: `SELECT iteration + 1 AS iteration FROM CTE_1 WHERE + iteration < 3` + +To learn more about the recursive CTE syntax, rules, and examples, see [`WITH` +clause][with-clause] in the ZetaSQL reference documentation. + +## Explore reachability in a directed acyclic graph (DAG) + + +You can use a recursive query to explore reachability in a +directed acyclic graph (DAG). The following query finds all nodes that can be +reached from node `5` in a graph called `GraphData`: + +```sql +WITH RECURSIVE + GraphData AS ( + -- 1 5 + -- / \ / \ + -- 2 - 3 6 7 + -- | \ / + -- 4 8 + SELECT 1 AS from_node, 2 AS to_node UNION ALL + SELECT 1, 3 UNION ALL + SELECT 2, 3 UNION ALL + SELECT 3, 4 UNION ALL + SELECT 5, 6 UNION ALL + SELECT 5, 7 UNION ALL + SELECT 6, 8 UNION ALL + SELECT 7, 8 + ), + R AS ( + (SELECT 5 AS node) + UNION ALL + ( + SELECT GraphData.to_node AS node + FROM R + INNER JOIN GraphData + ON (R.node = GraphData.from_node) + ) + ) +SELECT DISTINCT node FROM R ORDER BY node; +``` + +The preceding example produces the following results: + +```sql +/*------* + | node | + +------+ + | 5 | + | 6 | + | 7 | + | 8 | + *------*/ +``` + +## Troubleshoot iteration limit errors + + +Recursive CTEs can result in infinite recursion, which occurs when the recursive +term executes continuously without meeting a termination condition. To terminate +infinite recursions, a limit of iterations for each recursive CTE is +enforced. The iteration limit is determined by your +query engine. Once a recursive CTE reaches +the maximum number of iterations, the CTE execution is aborted with an error. + +This limit exists because the computation of a recursive CTE can be expensive, +and running a CTE with a large number of iterations consumes a lot of system +resources and takes a much longer time to finish. + +Queries that reach the iteration limit are usually missing a proper termination +condition, thus creating an infinite loop, or using recursive CTEs in +inappropriate scenarios. + +If you experience a recursion iteration limit error, review the suggestions in +this section. + +### Check for infinite recursion + + +To prevent infinite recursion, make sure the recursive term is +able to produce an empty result after executing a certain number of iterations. + +One way to check for infinite recursion is to convert your recursive CTE to a +`TEMP TABLE` with a `REPEAT` loop for the first `100` iterations, as follows: + +
+DECLARE current_iteration INT64 DEFAULT 0;
+
+CREATE TEMP TABLE recursive_cte_name AS
+SELECT base_expression, current_iteration AS iteration;
+
+REPEAT
+  SET current_iteration = current_iteration + 1;
+  INSERT INTO recursive_cte_name
+    SELECT recursive_expression, current_iteration
+    FROM recursive_cte_name
+    WHERE termination_condition_expression
+      AND iteration = current_iteration - 1
+      AND current_iteration < 100;
+  UNTIL NOT EXISTS(SELECT * FROM recursive_cte_name WHERE iteration = current_iteration)
+END REPEAT;
+
+ +Replace the following values: + ++ `recursive_cte_name`: The recursive CTE to debug. ++ `base_expression`: The base term of the recursive CTE. ++ `recursive_expression`: The recursive term of the recursive CTE. ++ `termination_condition_expression`: The termination expression of the + recursive CTE. + +For example, consider the following recursive CTE called `TestCTE`: + +```sql +WITH RECURSIVE + TestCTE AS ( + SELECT 1 AS n + UNION ALL + SELECT n + 3 FROM TestCTE WHERE MOD(n, 6) != 0 + ) +``` + +This example uses the following values: + ++ `recursive_cte_name`: `TestCTE` ++ `base_expression`: `SELECT 1` ++ `recursive_expression`: `n + 3` ++ `termination_condition_expression`: `MOD(n, 6) != 0` + +The following code would therefore test the `TestCTE` for infinite recursion: + +
+DECLARE current_iteration INT64 DEFAULT 0;
+
+CREATE TEMP TABLE TestCTE AS
+SELECT 1 AS n, current_iteration AS iteration;
+
+REPEAT
+SET current_iteration = current_iteration + 1;
+
+INSERT INTO TestCTE
+SELECT n + 3, current_iteration
+FROM TestCTE
+WHERE
+  MOD(n, 6) != 0
+  AND iteration = current_iteration - 1
+  AND current_iteration < 10;
+
+UNTIL
+  NOT EXISTS(SELECT * FROM TestCTE WHERE iteration = current_iteration)
+    END REPEAT;
+
+-- Print the number of rows produced by each iteration
+
+SELECT iteration, COUNT(1) AS num_rows
+FROM TestCTE
+GROUP BY iteration
+ORDER BY iteration;
+
+-- Examine the actual result produced for a specific iteration
+
+SELECT * FROM TestCTE WHERE iteration = 2;
+
+ +The preceding example produces the following results that include the +iteration ID and the number of rows that were produced during that iteration: + +```sql +/*-----------+----------* + | iteration | num_rows | + +-----------+----------+ + | 0 | 1 | + | 1 | 1 | + | 2 | 1 | + | 3 | 1 | + | 4 | 1 | + | 5 | 1 | + | 6 | 1 | + | 7 | 1 | + | 8 | 1 | + | 9 | 1 | + | 10 | 1 | + *-----------+----------*/ +``` + +These are the actual results produced during iteration `2`: + +```sql +/*----------+-----------* + | n | iteration | + +----------+-----------+ + | 7 | 2 | + *----------+-----------*/ +``` + +If the number of rows is always greater than zero, which is true in this +example, then the sample likely has an infinite recursion. + +### Verify the appropriate usage of the recursive CTE + + +Verify that you're using the recursive CTE in an appropriate scenario. +Recursive CTEs can be expensive to compute because they're designed to query +hierarchical data and graph data. If you aren't querying these two kinds of +data, consider alternatives, such as using the +[`LOOP` statement][loop-statement] with a non-recursive CTE. + +### Split a recursive CTE into multiple recursive CTEs + + +If you think your recursive CTE needs more than the maximum allowed +iterations, you might be able to break down your recursive CTE into multiple +recursive CTEs. + +You can split a recursive CTE with a query structure similar to the following: + +
+WITH RECURSIVE
+  CTE_1 AS (
+    SELECT base_expression
+    UNION ALL
+    SELECT recursive_expression FROM CTE_1 WHERE iteration < 500
+  ),
+  CTE_2 AS (
+    SELECT * FROM CTE_1 WHERE iteration = 500
+    UNION ALL
+    SELECT recursive_expression FROM CTE_2 WHERE iteration < 500 * 2
+  ),
+  CTE_3 AS (
+    SELECT * FROM CTE_2 WHERE iteration = 500 * 2
+    UNION ALL
+    SELECT recursive_expression FROM CTE_3 WHERE iteration < 500 * 3
+  ),
+  [, ...]
+SELECT * FROM CTE_1
+UNION ALL SELECT * FROM CTE_2 WHERE iteration > 500
+UNION ALL SELECT * FROM CTE_3 WHERE iteration > 500 * 2
+[...]
+
+ +Replace the following values: + +* `base_expression`: The base term expression for the current CTE. +* `recursive_expression`: The recursive term expression for the current CTE. + +For example, the following code splits a CTE into three distinct CTEs: + +```sql +WITH RECURSIVE + CTE_1 AS ( + SELECT 1 AS iteration + UNION ALL + SELECT iteration + 1 AS iteration FROM CTE_1 WHERE iteration < 10 + ), + CTE_2 AS ( + SELECT * FROM CTE_1 WHERE iteration = 10 + UNION ALL + SELECT iteration + 1 AS iteration FROM CTE_2 WHERE iteration < 10 * 2 + ), + CTE_3 AS ( + SELECT * FROM CTE_2 WHERE iteration = 10 * 2 + UNION ALL + SELECT iteration + 1 AS iteration FROM CTE_3 WHERE iteration < 10 * 3 + ) +SELECT iteration FROM CTE_1 +UNION ALL +SELECT iteration FROM CTE_2 WHERE iteration > 10 +UNION ALL +SELECT iteration FROM CTE_3 WHERE iteration > 20 +ORDER BY 1 ASC +``` + +In the preceding example, 500 iterations is replaced with 10 iterations +so that it's faster to see the results of the query. The query produces 30 rows, +but each recursive CTE only iterates 10 times. The output looks like the +following: + +```sql +/*-----------* + | iteration | + +-----------+ + | 2 | + | ... | + | 30 | + *-----------*/ +``` + +You could test the previous query on much larger iterations. + +### Use a loop instead of a recursive CTE + + +To avoid iteration limits, consider using a loop instead of a recursive CTE. +You can create a loop with one of several loop statements, +such as `LOOP`, `REPEAT`, or `WHILE`. For more information, see +[Loops][loops]. + + + +[with-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#with_clause + +[recursive-cte]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#recursive_cte + +[recursive-keyword]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#recursive_keyword + +[non-recursive-cte]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#simple_cte + +[loop-statement]: https://github.com/google/zetasql/blob/master/docs/procedural-language.md#loop + +[loops]: https://github.com/google/zetasql/blob/master/docs/procedural-language.md#loops + + + diff --git a/docs/resolved_ast.md b/docs/resolved_ast.md index b32a3f092..7e1b087c7 100755 --- a/docs/resolved_ast.md +++ b/docs/resolved_ast.md @@ -164,6 +164,7 @@ See that file for comments on specific nodes and fields. ResolvedDifferentialPrivacyAggregateScan ResolvedAnalyticScan ResolvedArrayScan + ResolvedAssertScan ResolvedBarrierScan ResolvedExecuteAsRoleScan ResolvedFilterScan @@ -179,6 +180,7 @@ See that file for comments on specific nodes and fields. ResolvedSampleScan ResolvedSetOperationScan ResolvedSingleRowScan + ResolvedStaticDescribeScan ResolvedTVFScan ResolvedTableScan ResolvedUnpivotScan @@ -189,6 +191,7 @@ See that file for comments on specific nodes and fields. ResolvedAlterObjectStmt ResolvedAlterAllRowAccessPoliciesStmt ResolvedAlterApproxViewStmt + ResolvedAlterConnectionStmt ResolvedAlterDatabaseStmt ResolvedAlterEntityStmt ResolvedAlterExternalSchemaStmt @@ -211,6 +214,7 @@ See that file for comments on specific nodes and fields. ResolvedCreateDatabaseStmt ResolvedCreateRowAccessPolicyStmt ResolvedCreateStatement + ResolvedCreateConnectionStmt ResolvedCreateConstantStmt ResolvedCreateEntityStmt ResolvedCreateFunctionStmt @@ -3128,9 +3132,11 @@ class ResolvedUnnestItem : public ResolvedArgument// This statement: // CREATE [OR REPLACE] [UNIQUE] [SEARCH | VECTOR] INDEX [IF NOT EXISTS] // <index_name_path> ON <table_name_path> -// [STORING (Expression, ...)] // [UNNEST(path_expression) [[AS] alias] [WITH OFFSET [[AS] alias]], ...] -// (path_expression [ASC|DESC], ...) [OPTIONS (name=value, ...)]; +// (path_expression [ASC|DESC], ...) +// [STORING (Expression, ...)] +// [PARTITION BY partition_expression, ...] +// [OPTIONS (name=value, ...)]; // // <table_name_path> is the name of table being indexed. // <table_scan> is a TableScan on the table being indexed. @@ -3146,6 +3152,7 @@ class ResolvedUnnestItem : public ResolvedArgumentResolv int storing_expression_list_size() const; const ResolvedExpr* storing_expression_list(int i) const; + const std::vector<std::unique_ptr<const ResolvedExpr>>& partition_by_list() const; + int partition_by_list_size() const; + const ResolvedExpr* partition_by_list(int i) const; + const std::vector<std::unique_ptr<const ResolvedOption>>& option_list() const; int option_list_size() const; const ResolvedOption* option_list(int i) const; @@ -7659,6 +7670,52 @@ class ResolvedIdentityColumnInfo : public ResolvedAr };

+### ResolvedStaticDescribeScan +
+ +


+// This represents the pipe STATIC_DESCRIBE operator, which is controlled by
+// FEATURE_PIPE_STATIC_DESCRIBE.
+//
+// This scan is a no-op, that just stores the describe_text produced to show
+// the intermediate schema where this operator occurred.
+//
+// This describe_text is displayed in resolved AST DebugStrings (which is
+// used internally in analyzer tests), and is also meant to be displayed
+// through some engine-specific side-channel at query prepare time.
+class ResolvedStaticDescribeScan : public ResolvedScan {
+  static const ResolvedNodeKind TYPE = RESOLVED_STATIC_DESCRIBE_SCAN;
+
+  const ResolvedScan* input_scan() const;
+
+  const std::string& describe_text() const;
+};
+

+ +### ResolvedAssertScan + + +


+// This represents the pipe ASSERT operator, which is controlled by
+// FEATURE_PIPE_ASSERT.
+//
+// `condition` is a boolean expression.
+// `message` is a string expression.
+//
+// `condition` is computed for each row.  If it does not return true,
+// the assertion fails.  Then `message` is evaluated and used as part
+// of the error message, following something like "Assert failed: ".
+class ResolvedAssertScan : public ResolvedScan {
+  static const ResolvedNodeKind TYPE = RESOLVED_ASSERT_SCAN;
+
+  const ResolvedScan* input_scan() const;
+
+  const ResolvedExpr* condition() const;
+
+  const ResolvedExpr* message() const;
+};
+

+ ### ResolvedBarrierScan @@ -7683,3 +7740,37 @@ class ResolvedBarrierScan : public ResolvedScan { };

+### ResolvedCreateConnectionStmt + + +


+// This statement:
+// CREATE [OR REPLACE] [TEMP] CONNECTION
+// [IF NOT EXISTS] <name> [OPTIONS (name=value, ...)]
+//
+// builds a new connection based on the inputs provided via the
+// the OPTIONS field.
+//
+// <name> is the name of the fully qualified connection.
+// <option_list> is the list of options for the connection.
+class ResolvedCreateConnectionStmt : public ResolvedCreateStatement {
+  static const ResolvedNodeKind TYPE = RESOLVED_CREATE_CONNECTION_STMT;
+
+  const std::vector<std::unique_ptr<const ResolvedOption>>& option_list() const;
+  int option_list_size() const;
+  const ResolvedOption* option_list(int i) const;
+};
+

+ +### ResolvedAlterConnectionStmt + + +


+// This statement:
+// ALTER CONNECTION [IF EXISTS] <name_path> SET OPTIONS(...)
+class ResolvedAlterConnectionStmt : public ResolvedAlterObjectStmt {
+  static const ResolvedNodeKind TYPE = RESOLVED_ALTER_CONNECTION_STMT;
+
+};
+

+ diff --git a/docs/sketches.md b/docs/sketches.md new file mode 100644 index 000000000..36792c485 --- /dev/null +++ b/docs/sketches.md @@ -0,0 +1,208 @@ + + + + +# Sketches + +ZetaSQL supports data sketches. +A data sketch is a compact summary of a data aggregation. It captures all the +necessary information to either extract an aggregation result, continue a +data aggregation, or merge it with another sketch, enabling re-aggregation. + +Computing a metric using a sketch is substantially less expensive than computing +an exact value. If your computation is too slow or requires too much temporary +storage, use sketches to reduce query time and resources. + +Additionally, computing [cardinalities][cardinality]{: .external}, such as the +number of distinct users, or [quantiles][quantiles-wiki]{: .external}, such as median visit duration, without +sketches is usually only possible by running jobs over the raw data because +already-aggregated data can't be combined anymore. + +Consider a table with the following data: + + + + + + + + + + + + + + + + + + + + + +
ProductNumber of usersMedian visit duration
Product A500 million10 minutes
Product B20 million2 minutes
+ +Computing the total number of users for both products isn't possible because we +don't know how many users used both products in the table. + +A solution is to store sketches in the table instead. Each sketch is an +approximate and compact representation of a particular input property, such as +cardinality, that you can store, merge (or re-aggregate), and query for +near-exact results. In the previous example, you can estimate the number of +distinct users for Product A and Product B by creating and merging +(re-aggregating) the sketches for each product. You can also estimate the median +visit duration with quantile sketches that you can likewise merge and query. + +Because a sketch has lossy compression of the original data, it introduces a +statistical error that's represented by an error bound or confidence interval +(CI). For most applications, this uncertainty is small. For example, a typical +cardinality-counting sketch has a relative error of about 1% in 95% of all +cases. A sketch trades some accuracy, or _precision_, for faster and less +expensive computations, and less storage. + +In summary, a sketch has these main properties: + ++ Represents an approximate aggregate for a specific metric ++ Is compact ++ Is a serialized form of an in-memory, sublinear data structure ++ Is typically a fixed size and asymptotically smaller than the input ++ Can introduce a statistical error that you determine with a precision + level ++ Can be merged with other sketches to summarize the union of the underlying + data sets + +## Re-aggregation with sketch merging + +Sketches let you store and merge data for efficient re-aggregation. This makes +sketches particularly useful for materialized views of data sets. You can merge +sketches to construct a summary of multiple data streams based on partial +sketches created for each stream. + +For example, if you create a sketch for the estimated number of distinct users +every day, you can get the number of distinct users during the last seven days +by merging daily sketches. Re-aggregating the merged daily sketches helps you +avoid reading the full input of the data set. + +Sketch re-aggregation is also useful in online analytical processing (OLAP). You +can merge sketches to create a roll-up of an +[OLAP cube][olap]{: .external}, where the +sketch summarizes data along one or more specific dimensions of the cube. OLAP +roll-ups aren't possible with true distinct counts. + +## Sketch integration + +You can integrate sketches with other systems. For example, you can build +sketches in external applications, like [Dataflow][dataflow]{: .external} or +[Apache Spark][spark]{: .external}, and consume them in ZetaSQL or vice +versa. + +In addition to ZetaSQL, you can use sketches with the following +coding languages: + ++ C++ ++ Go ++ Java ++ Python + +### Estimate cardinality without deletions + +If you need to estimate [cardinality][cardinality]{: .external} and you don't need +the ability to delete items from the sketch, use an [HLL++ sketch][hll-sketch]. + +For example, to get the number of unique users who actively used a product in a +given month (MAU or 28DAU metrics), use an HLL++ sketch. + +## HLL++ sketches + + +HyperLogLog++ (HLL++) is a sketching algorithm for estimating cardinality. HLL++ +is based on the paper [HyperLogLog in Practice][hll]{: .external}, where the +_++_ denotes the augmentations made to the HyperLogLog algorithm. + +[Cardinality][cardinality]{: .external} is the number of distinct elements in the +input for a sketch. For example, you could use an HLL++ sketch to get the number +of unique users who have opened an application. + +HLL++ estimates very small and very large cardinalities. HLL++ includes a +64-bit hash function, sparse representation to reduce memory requirements for +small cardinality estimates, and empirical bias correction for +small cardinality estimates. + + + +**Precision** + +HLL++ sketches support custom precision. The following table shows the supported +precision values, the maximum storage size, and the confidence interval (CI) of +typical precision levels: + +Precision | Max storage size | 65% CI | 95% CI | 99% CI +------------ | -------------------- | ------ | ------ | ------ +10 | 1 KiB + 28 B | ±3.25% | ±6.50% | ±9.75% +11 | 2 KiB + 28 B | ±2.30% | ±4.60% | ±6.89% +12 | 4 KiB + 28 B | ±1.63% | ±3.25% | ±4.88% +13 | 8 KiB + 28 B | ±1.15% | ±2.30% | ±3.45% +14 | 16 KiB + 30 B | ±0.81% | ±1.63% | ±2.44% +15 (default) | 32 KiB + 30 B | ±0.57% | ±1.15% | ±1.72% +16 | 64 KiB + 30 B | ±0.41% | ±0.81% | ±1.22% +17 | 128 KiB + 30 B | ±0.29% | ±0.57% | ±0.86% +18 | 256 KiB + 30 B | ±0.20% | ±0.41% | ±0.61% +19 | 512 KiB + 30 B | ±0.14% | ±0.29% | ±0.43% +20 | 1024 KiB + 30 B | ±0.10% | ±0.20% | ±0.30% +21 | 2048 KiB + 32 B | ±0.07% | ±0.14% | ±0.22% +22 | 4096 KiB + 32 B | ±0.05% | ±0.10% | ±0.15% +23 | 8192 KiB + 32 B | ±0.04% | ±0.07% | ±0.11% +24 | 16384 KiB + 32 B | ±0.03% | ±0.05% | ±0.08% + +You can define precision for an HLL++ sketch when you initialize it with the +[`HLL_COUNT.INIT`][hll-init] function. + +**Deletion** + +You can't delete values from an HLL++ sketch. + +**Additional details** + +For a list of functions that you can use with HLL++ sketches, see +[HLL++ functions][hll-functions]. + +## Approximate aggregate functions + + +As an alternative to specific sketch-based approximation functions, +ZetaSQL provides predefined approximate aggregate +functions. These approximate aggregate functions support sketches for common +estimations such as distinct count, quantiles, and top count, but they don't +allow custom precision. They also don't expose and store the sketch for +re-aggregation like other types of sketches. The approximate aggregate functions +are designed for running quick sketch-based queries without detailed +configuration. + +For a list of approximate aggregate functions that you can use with +sketch-based approximation, see +[Approximate aggregate functions][approx-aggregate-functions]. + + + +[spark]: https://spark.apache.org + +[dataflow]: https://cloud.google.com/dataflow + +[olap]: https://en.wikipedia.org/wiki/OLAP_cube + +[hll]: https://research.google.com/pubs/archive/40671.pdf + +[hll-sketch]: #sketches_hll + +[hll-functions]: https://github.com/google/zetasql/blob/master/docs/hll_functions.md + +[hll-init]: https://github.com/google/zetasql/blob/master/docs/hll_functions.md#hll_countinit + +[cardinality]: https://en.wikipedia.org/wiki/Cardinality + +[approx-aggregate-functions]: https://github.com/google/zetasql/blob/master/docs/approximate_aggregate_functions.md + +[approx-functions]: #approx_functions + + + diff --git a/docs/statistical_aggregate_functions.md b/docs/statistical_aggregate_functions.md index 224e711c1..1e61cc3dd 100644 --- a/docs/statistical_aggregate_functions.md +++ b/docs/statistical_aggregate_functions.md @@ -455,10 +455,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -626,10 +631,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -746,10 +756,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see @@ -972,10 +987,15 @@ To learn more about the optional aggregate clauses that you can pass into this function, see [Aggregate function calls][aggregate-function-calls]. +This function can be used with the +[`AGGREGATION_THRESHOLD` clause][agg-threshold-clause]. + [aggregate-function-calls]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md +[agg-threshold-clause]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#agg_threshold_clause + To learn more about the `OVER` clause and how to use it, see diff --git a/docs/string_functions.md b/docs/string_functions.md index 22c39cdce..6089ce2f3 100644 --- a/docs/string_functions.md +++ b/docs/string_functions.md @@ -329,6 +329,15 @@ canonical equivalence. + + REGEXP_SUBSTR + + + + Synonym for REGEXP_EXTRACT. + + + REPEAT @@ -583,21 +592,23 @@ regardless of whether the value is a `STRING` or `BYTES` type. **Examples** ```sql -WITH example AS - (SELECT 'абвгд' AS characters, b'абвгд' AS bytes) +SELECT BYTE_LENGTH('абвгд') AS string_example; -SELECT - characters, - BYTE_LENGTH(characters) AS string_example, - bytes, - BYTE_LENGTH(bytes) AS bytes_example -FROM example; +/*----------------* + | string_example | + +----------------+ + | 10 | + *----------------*/ +``` + +```sql +SELECT BYTE_LENGTH(b'абвгд') AS bytes_example; -/*------------+----------------+-------+---------------* - | characters | string_example | bytes | bytes_example | - +------------+----------------+-------+---------------+ - | абвгд | 10 | абвгд | 10 | - *------------+----------------+-------+---------------*/ +/*----------------* + | bytes_example | + +----------------+ + | 10 | + *----------------*/ ``` ### `CHAR_LENGTH` @@ -617,19 +628,13 @@ Gets the number of characters in a `STRING` value. **Examples** ```sql -WITH example AS - (SELECT 'абвгд' AS characters) - -SELECT - characters, - CHAR_LENGTH(characters) AS char_length_example -FROM example; +SELECT CHAR_LENGTH('абвгд') AS char_length; -/*------------+---------------------* - | characters | char_length_example | - +------------+---------------------+ - | абвгд | 5 | - *------------+---------------------*/ +/*-------------* + | char_length | + +-------------+ + | 5 | + *------------ */ ``` ### `CHARACTER_LENGTH` @@ -649,13 +654,9 @@ Synonym for [CHAR_LENGTH][string-link-to-char-length]. **Examples** ```sql -WITH example AS - (SELECT 'абвгд' AS characters) - SELECT - characters, - CHARACTER_LENGTH(characters) AS char_length_example -FROM example; + 'абвгд' AS characters, + CHARACTER_LENGTH('абвгд') AS char_length_example /*------------+---------------------* | characters | char_length_example | @@ -1116,23 +1117,12 @@ This function supports specifying [collation][collation]. **Examples** ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - ENDS_WITH(item, 'e') as example -FROM items; +SELECT ENDS_WITH('apple', 'e') as example /*---------* | example | +---------+ | True | - | False | - | True | *---------*/ ``` @@ -1727,6 +1717,8 @@ JSON ' + + ##### %t and %T behavior @@ -2111,40 +2103,27 @@ If `value` or `delimiters` is `NULL`, the function returns `NULL`. **Examples** ```sql -WITH example AS -( - SELECT 'Hello World-everyone!' AS value UNION ALL - SELECT 'tHe dog BARKS loudly+friendly' AS value UNION ALL - SELECT 'apples&oranges;&pears' AS value UNION ALL - SELECT 'καθίσματα ταινιών' AS value -) -SELECT value, INITCAP(value) AS initcap_value FROM example +SELECT + 'Hello World-everyone!' AS value, + INITCAP('Hello World-everyone!') AS initcap_value /*-------------------------------+-------------------------------* | value | initcap_value | +-------------------------------+-------------------------------+ | Hello World-everyone! | Hello World-Everyone! | - | tHe dog BARKS loudly+friendly | The Dog Barks Loudly+Friendly | - | apples&oranges;&pears | Apples&Oranges;&Pears | - | καθίσματα ταινιών | Καθίσματα Ταινιών | *-------------------------------+-------------------------------*/ +``` -WITH example AS -( - SELECT 'hello WORLD!' AS value, '' AS delimiters UNION ALL - SELECT 'καθίσματα ταιντιώ@ν' AS value, 'τ@' AS delimiters UNION ALL - SELECT 'Apples1oranges2pears' AS value, '12' AS delimiters UNION ALL - SELECT 'tHisEisEaESentence' AS value, 'E' AS delimiters -) -SELECT value, delimiters, INITCAP(value, delimiters) AS initcap_value FROM example; +```sql +SELECT + 'Apples1oranges2pears' as value, + '12' AS delimiters, + INITCAP('Apples1oranges2pears' , '12') AS initcap_value /*----------------------+------------+----------------------* | value | delimiters | initcap_value | +----------------------+------------+----------------------+ - | hello WORLD! | | Hello world! | - | καθίσματα ταιντιώ@ν | τ@ | ΚαθίσματΑ τΑιντΙώ@Ν | | Apples1oranges2pears | 12 | Apples1Oranges2Pears | - | tHisEisEaESentence | E | ThisEIsEAESentence | *----------------------+------------+----------------------*/ ``` @@ -2198,41 +2177,109 @@ Returns an error if: **Examples** ```sql -WITH example AS -(SELECT 'banana' as value, 'an' as subvalue, 1 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, 1 as position, 2 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, 1 as position, 3 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, 3 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, -1 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'an' as subvalue, -3 as position, 1 as -occurrence UNION ALL -SELECT 'banana' as value, 'ann' as subvalue, 1 as position, 1 as -occurrence UNION ALL -SELECT 'helloooo' as value, 'oo' as subvalue, 1 as position, 1 as -occurrence UNION ALL -SELECT 'helloooo' as value, 'oo' as subvalue, 1 as position, 2 as -occurrence -) -SELECT value, subvalue, position, occurrence, INSTR(value, -subvalue, position, occurrence) AS instr -FROM example; +SELECT + 'banana' AS value, 'an' AS subvalue, 1 AS position, 1 AS occurrence, + INSTR('banana', 'an', 1, 1) AS instr; /*--------------+--------------+----------+------------+-------* | value | subvalue | position | occurrence | instr | +--------------+--------------+----------+------------+-------+ | banana | an | 1 | 1 | 2 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, 1 AS position, 2 AS occurrence, + INSTR('banana', 'an', 1, 2) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | 1 | 2 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, 1 AS position, 3 AS occurrence, + INSTR('banana', 'an', 1, 3) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | 1 | 3 | 0 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, 3 AS position, 1 AS occurrence, + INSTR('banana', 'an', 3, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | 3 | 1 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, -1 AS position, 1 AS occurrence, + INSTR('banana', 'an', -1, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | -1 | 1 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'an' AS subvalue, -3 AS position, 1 AS occurrence, + INSTR('banana', 'an', -3, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | an | -3 | 1 | 4 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'banana' AS value, 'ann' AS subvalue, 1 AS position, 1 AS occurrence, + INSTR('banana', 'ann', 1, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | banana | ann | 1 | 1 | 0 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'helloooo' AS value, 'oo' AS subvalue, 1 AS position, 1 AS occurrence, + INSTR('helloooo', 'oo', 1, 1) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | helloooo | oo | 1 | 1 | 5 | + *--------------+--------------+----------+------------+-------*/ +``` + +```sql +SELECT + 'helloooo' AS value, 'oo' AS subvalue, 1 AS position, 2 AS occurrence, + INSTR('helloooo', 'oo', 1, 2) AS instr; + +/*--------------+--------------+----------+------------+-------* + | value | subvalue | position | occurrence | instr | + +--------------+--------------+----------+------------+-------+ | helloooo | oo | 1 | 2 | 6 | *--------------+--------------+----------+------------+-------*/ ``` @@ -2264,43 +2311,23 @@ will be returned. **Examples** ```sql -WITH examples AS -(SELECT 'apple' as example -UNION ALL -SELECT 'banana' as example -UNION ALL -SELECT 'абвгд' as example -) -SELECT example, LEFT(example, 3) AS left_example -FROM examples; +SELECT LEFT('banana', 3) AS results -/*---------+--------------* - | example | left_example | - +---------+--------------+ - | apple | app | - | banana | ban | - | абвгд | абв | - *---------+--------------*/ +/*---------* + | results | + +--------+ + | ban | + *---------*/ ``` ```sql -WITH examples AS -(SELECT b'apple' as example -UNION ALL -SELECT b'banana' as example -UNION ALL -SELECT b'\xab\xcd\xef\xaa\xbb' as example -) -SELECT example, LEFT(example, 3) AS left_example -FROM examples; +SELECT LEFT(b'\xab\xcd\xef\xaa\xbb', 3) AS results -/*----------------------+--------------* - | example | left_example | - +----------------------+--------------+ - | apple | app | - | banana | ban | - | \xab\xcd\xef\xaa\xbb | \xab\xcd\xef | - *----------------------+--------------*/ +/*--------------* + | results | + +--------------+ + | \xab\xcd\xef | + *--------------*/ ``` ### `LENGTH` @@ -2322,21 +2349,15 @@ argument. **Examples** ```sql - -WITH example AS - (SELECT 'абвгд' AS characters) - SELECT - characters, - LENGTH(characters) AS string_example, - LENGTH(CAST(characters AS BYTES)) AS bytes_example -FROM example; + LENGTH('абвгд') AS string_example, + LENGTH(CAST('абвгд' AS BYTES)) AS bytes_example; -/*------------+----------------+---------------* - | characters | string_example | bytes_example | - +------------+----------------+---------------+ - | абвгд | 5 | 10 | - *------------+----------------+---------------*/ +/*----------------+---------------* + | string_example | bytes_example | + +----------------+---------------+ + | 5 | 10 | + *----------------+---------------*/ ``` ### `LOWER` @@ -2363,28 +2384,15 @@ greater than 127 left intact. **Examples** ```sql - -WITH items AS - (SELECT - 'FOO' as item - UNION ALL - SELECT - 'BAR' as item - UNION ALL - SELECT - 'BAZ' as item) - SELECT - LOWER(item) AS example + LOWER('FOO BAR BAZ') AS example FROM items; -/*---------* - | example | - +---------+ - | foo | - | bar | - | baz | - *---------*/ +/*-------------* + | example | + +-------------+ + | foo bar baz | + *-------------*/ ``` [string-link-to-unicode-character-definitions]: http://unicode.org/ucd/ @@ -2426,72 +2434,53 @@ This function returns an error if: **Examples** ```sql -SELECT t, len, FORMAT('%T', LPAD(t, len)) AS LPAD FROM UNNEST([ - STRUCT('abc' AS t, 5 AS len), - ('abc', 2), - ('例子', 4) -]); +SELECT FORMAT('%T', LPAD('c', 5)) AS results -/*------+-----+----------* - | t | len | LPAD | - |------|-----|----------| - | abc | 5 | " abc" | - | abc | 2 | "ab" | - | 例子 | 4 | " 例子" | - *------+-----+----------*/ +/*---------* + | results | + +---------+ + | " c" | + *---------*/ ``` ```sql -SELECT t, len, pattern, FORMAT('%T', LPAD(t, len, pattern)) AS LPAD FROM UNNEST([ - STRUCT('abc' AS t, 8 AS len, 'def' AS pattern), - ('abc', 5, '-'), - ('例子', 5, '中文') -]); +SELECT LPAD('b', 5, 'a') AS results -/*------+-----+---------+--------------* - | t | len | pattern | LPAD | - |------|-----|---------|--------------| - | abc | 8 | def | "defdeabc" | - | abc | 5 | - | "--abc" | - | 例子 | 5 | 中文 | "中文中例子" | - *------+-----+---------+--------------*/ +/*---------* + | results | + +---------+ + | aaaab | + *---------*/ ``` ```sql -SELECT FORMAT('%T', t) AS t, len, FORMAT('%T', LPAD(t, len)) AS LPAD FROM UNNEST([ - STRUCT(b'abc' AS t, 5 AS len), - (b'abc', 2), - (b'\xab\xcd\xef', 4) -]); +SELECT LPAD('abc', 10, 'ghd') AS results -/*-----------------+-----+------------------* - | t | len | LPAD | - |-----------------|-----|------------------| - | b"abc" | 5 | b" abc" | - | b"abc" | 2 | b"ab" | - | b"\xab\xcd\xef" | 4 | b" \xab\xcd\xef" | - *-----------------+-----+------------------*/ +/*------------* + | results | + +------------+ + | ghdghdgabc | + *------------*/ ``` ```sql -SELECT - FORMAT('%T', t) AS t, - len, - FORMAT('%T', pattern) AS pattern, - FORMAT('%T', LPAD(t, len, pattern)) AS LPAD -FROM UNNEST([ - STRUCT(b'abc' AS t, 8 AS len, b'def' AS pattern), - (b'abc', 5, b'-'), - (b'\xab\xcd\xef', 5, b'\x00') -]); - -/*-----------------+-----+---------+-------------------------* - | t | len | pattern | LPAD | - |-----------------|-----|---------|-------------------------| - | b"abc" | 8 | b"def" | b"defdeabc" | - | b"abc" | 5 | b"-" | b"--abc" | - | b"\xab\xcd\xef" | 5 | b"\x00" | b"\x00\x00\xab\xcd\xef" | - *-----------------+-----+---------+-------------------------*/ +SELECT LPAD('abc', 2, 'd') AS results + +/*---------* + | results | + +---------+ + | ab | + *---------*/ +``` + +```sql +SELECT FORMAT('%T', LPAD(b'abc', 10, b'ghd')) AS results + +/*---------------* + | results | + +---------------+ + | b"ghdghdgabc" | + *---------------*/ ``` ### `LTRIM` @@ -2511,68 +2500,32 @@ Identical to [TRIM][string-link-to-trim], but only removes leading characters. **Examples** ```sql -WITH items AS - (SELECT ' apple ' as item - UNION ALL - SELECT ' banana ' as item - UNION ALL - SELECT ' orange ' as item) - -SELECT - CONCAT('#', LTRIM(item), '#') as example -FROM items; +SELECT CONCAT('#', LTRIM(' apple '), '#') AS example /*-------------* | example | +-------------+ - | #apple # | - | #banana # | - | #orange # | + | #apple # | *-------------*/ ``` ```sql -WITH items AS - (SELECT '***apple***' as item - UNION ALL - SELECT '***banana***' as item - UNION ALL - SELECT '***orange***' as item) - -SELECT - LTRIM(item, '*') as example -FROM items; +SELECT LTRIM('***apple***', '*') AS example /*-----------* | example | +-----------+ | apple*** | - | banana*** | - | orange*** | *-----------*/ ``` ```sql -WITH items AS - (SELECT 'xxxapplexxx' as item - UNION ALL - SELECT 'yyybananayyy' as item - UNION ALL - SELECT 'zzzorangezzz' as item - UNION ALL - SELECT 'xyzpearxyz' as item) - -SELECT - LTRIM(item, 'xyz') as example -FROM items; +SELECT LTRIM('xxxapplexxx', 'xyz') AS example /*-----------* | example | +-----------+ | applexxx | - | bananayyy | - | orangezzz | - | pearxyz | *-----------*/ ``` @@ -2609,40 +2562,60 @@ points. **Examples** +The following example normalizes different language characters: + ```sql -SELECT a, b, a = b as normalized -FROM (SELECT NORMALIZE('\u00ea') as a, NORMALIZE('\u0065\u0302') as b); +SELECT + NORMALIZE('\u00ea') as a, + NORMALIZE('\u0065\u0302') as b, + NORMALIZE('\u00ea') = NORMALIZE('\u0065\u0302') as normalized; /*---+---+------------* | a | b | normalized | +---+---+------------+ - | ê | ê | true | + | ê | ê | TRUE | *---+---+------------*/ ``` -The following example normalizes different space characters. +The following examples normalize different space characters: ```sql -WITH EquivalentNames AS ( - SELECT name - FROM UNNEST([ - 'Jane\u2004Doe', - 'John\u2004Smith', - 'Jane\u2005Doe', - 'Jane\u2006Doe', - 'John Smith']) AS name -) -SELECT - NORMALIZE(name, NFKC) AS normalized_name, - COUNT(*) AS name_count -FROM EquivalentNames -GROUP BY 1; +SELECT NORMALIZE('Raha\u2004Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ +``` + +```sql +SELECT NORMALIZE('Raha\u2005Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ +``` -/*-----------------+------------* - | normalized_name | name_count | - +-----------------+------------+ - | John Smith | 2 | - | Jane Doe | 3 | - *-----------------+------------*/ +```sql +SELECT NORMALIZE('Raha\u2006Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ +``` + +```sql +SELECT NORMALIZE('Raha Mahan', NFKC) AS normalized_name + +/*-----------------* + | normalized_name | + +-----------------+ + | Raha Mahan | + *-----------------*/ ``` [string-link-to-normalization-wikipedia]: https://en.wikipedia.org/wiki/Unicode_equivalence#Normalization @@ -2685,34 +2658,45 @@ considered, use `NORMALIZE_AND_CASEFOLD`, otherwise use ```sql SELECT - a, b, - NORMALIZE(a) = NORMALIZE(b) as normalized, - NORMALIZE_AND_CASEFOLD(a) = NORMALIZE_AND_CASEFOLD(b) as normalized_with_case_folding -FROM (SELECT 'The red barn' AS a, 'The Red Barn' AS b); + NORMALIZE('The red barn') = NORMALIZE('The Red Barn') AS normalized, + NORMALIZE_AND_CASEFOLD('The red barn') + = NORMALIZE_AND_CASEFOLD('The Red Barn') AS normalized_with_case_folding; -/*--------------+--------------+------------+------------------------------* - | a | b | normalized | normalized_with_case_folding | - +--------------+--------------+------------+------------------------------+ - | The red barn | The Red Barn | false | true | - *--------------+--------------+------------+------------------------------*/ +/*------------+------------------------------* + | normalized | normalized_with_case_folding | + +------------+------------------------------+ + | FALSE | TRUE | + *------------+------------------------------*/ ``` ```sql -WITH Strings AS ( - SELECT '\u2168' AS a, 'IX' AS b UNION ALL - SELECT '\u0041\u030A', '\u00C5' -) -SELECT a, b, - NORMALIZE_AND_CASEFOLD(a, NFD)=NORMALIZE_AND_CASEFOLD(b, NFD) AS nfd, - NORMALIZE_AND_CASEFOLD(a, NFC)=NORMALIZE_AND_CASEFOLD(b, NFC) AS nfc, - NORMALIZE_AND_CASEFOLD(a, NFKD)=NORMALIZE_AND_CASEFOLD(b, NFKD) AS nkfd, - NORMALIZE_AND_CASEFOLD(a, NFKC)=NORMALIZE_AND_CASEFOLD(b, NFKC) AS nkfc -FROM Strings; +SELECT + '\u2168' AS a, + 'IX' AS b, + NORMALIZE_AND_CASEFOLD('\u2168', NFD)=NORMALIZE_AND_CASEFOLD('IX', NFD) AS nfd, + NORMALIZE_AND_CASEFOLD('\u2168', NFC)=NORMALIZE_AND_CASEFOLD('IX', NFC) AS nfc, + NORMALIZE_AND_CASEFOLD('\u2168', NFKD)=NORMALIZE_AND_CASEFOLD('IX', NFKD) AS nkfd, + NORMALIZE_AND_CASEFOLD('\u2168', NFKC)=NORMALIZE_AND_CASEFOLD('IX', NFKC) AS nkfc; /*---+----+-------+-------+------+------* | a | b | nfd | nfc | nkfd | nkfc | +---+----+-------+-------+------+------+ | Ⅸ | IX | false | false | true | true | + *---+----+-------+-------+------+------*/ +``` + +```sql +SELECT + '\u0041\u030A' AS a, + '\u00C5' AS b, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFD)=NORMALIZE_AND_CASEFOLD('\u00C5', NFD) AS nfd, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFC)=NORMALIZE_AND_CASEFOLD('\u00C5', NFC) AS nfc, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFKD)=NORMALIZE_AND_CASEFOLD('\u00C5', NFKD) AS nkfd, + NORMALIZE_AND_CASEFOLD('\u0041\u030A', NFKC)=NORMALIZE_AND_CASEFOLD('\u00C5', NFKC) AS nkfc; + +/*---+----+-------+-------+------+------* + | a | b | nfd | nfc | nkfd | nkfc | + +---+----+-------+-------+------+------+ | Å | Å | true | true | true | true | *---+----+-------+-------+------+------*/ ``` @@ -2760,72 +2744,137 @@ regular expression syntax. **Examples** +The following queries check to see if an email is valid: + ```sql SELECT - email, - REGEXP_CONTAINS(email, r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid -FROM - (SELECT - ['foo@example.com', 'bar@example.org', 'www.example.net'] - AS addresses), - UNNEST(addresses) AS email; + 'foo@example.com' AS email, + REGEXP_CONTAINS('foo@example.com', r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid /*-----------------+----------* | email | is_valid | +-----------------+----------+ - | foo@example.com | true | - | bar@example.org | true | - | www.example.net | false | + | foo@example.com | TRUE | *-----------------+----------*/ + ``` --- Performs a full match, using ^ and $. Due to regular expression operator --- precedence, it is good practice to use parentheses around everything between ^ --- and $. + ```sql SELECT - email, - REGEXP_CONTAINS(email, r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') - AS valid_email_address, - REGEXP_CONTAINS(email, r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') - AS without_parentheses -FROM - (SELECT - ['a@foo.com', 'a@foo.computer', 'b@bar.org', '!b@bar.org', 'c@buz.net'] - AS addresses), - UNNEST(addresses) AS email; + 'www.example.net' AS email, + REGEXP_CONTAINS('www.example.net', r'@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+') AS is_valid + +/*-----------------+----------* + | email | is_valid | + +-----------------+----------+ + | www.example.net | FALSE | + *-----------------+----------*/ + ``` + +The following queries check to see if an email is valid. They +perform a full match, using `^` and `$`. Due to regular expression operator +precedence, it is good practice to use parentheses around everything between `^` +and `$`. + +```sql +SELECT + 'a@foo.com' AS email, + REGEXP_CONTAINS('a@foo.com', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('a@foo.com', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; /*----------------+---------------------+---------------------* | email | valid_email_address | without_parentheses | +----------------+---------------------+---------------------+ | a@foo.com | true | true | - | a@foo.computer | false | true | - | b@bar.org | true | true | - | !b@bar.org | false | true | - | c@buz.net | false | false | *----------------+---------------------+---------------------*/ ``` -[string-link-to-re2]: https://github.com/google/re2/wiki/Syntax +```sql +SELECT + 'a@foo.computer' AS email, + REGEXP_CONTAINS('a@foo.computer', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('a@foo.computer', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; -### `REGEXP_EXTRACT` +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ + | a@foo.computer | false | true | + *----------------+---------------------+---------------------*/ +``` ```sql -REGEXP_EXTRACT(value, regexp) +SELECT + 'b@bar.org' AS email, + REGEXP_CONTAINS('b@bar.org', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('b@bar.org', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; + +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ + | b@bar.org | true | true | + *----------------+---------------------+---------------------*/ ``` -**Description** +```sql +SELECT + '!b@bar.org' AS email, + REGEXP_CONTAINS('!b@bar.org', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('!b@bar.org', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; -Returns the first substring in `value` that matches the -[re2 regular expression][string-link-to-re2], -`regexp`. Returns `NULL` if there is no match. +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ + | !b@bar.org | false | true | + *----------------+---------------------+---------------------*/ +``` -If the regular expression contains a capturing group (`(...)`), and there is a -match for that capturing group, that match is returned. If there -are multiple matches for a capturing group, the first match is returned. +```sql +SELECT + 'c@buz.net' AS email, + REGEXP_CONTAINS('c@buz.net', r'^([\w.+-]+@foo\.com|[\w.+-]+@bar\.org)$') AS valid_email_address, + REGEXP_CONTAINS('c@buz.net', r'^[\w.+-]+@foo\.com|[\w.+-]+@bar\.org$') AS without_parentheses; + +/*----------------+---------------------+---------------------* + | email | valid_email_address | without_parentheses | + +----------------+---------------------+---------------------+ + | c@buz.net | false | false | + *----------------+---------------------+---------------------*/ +``` + +[string-link-to-re2]: https://github.com/google/re2/wiki/Syntax + +### `REGEXP_EXTRACT` + +```sql +REGEXP_EXTRACT(value, regexp[, position[, occurrence]]) +``` + +**Description** + +Returns the substring in `value` that matches the +[re2 regular expression][string-link-to-re2], `regexp`. +Returns `NULL` if there is no match. + +If the regular expression contains a capturing group (`(...)`), and there is a +match for that capturing group, that match is returned. If there +are multiple matches for a capturing group, the first match is returned. + +If `position` is specified, the search starts at this +position in `value`, otherwise it starts at the beginning of `value`. The +`position` must be a positive integer and cannot be 0. If `position` is greater +than the length of `value`, `NULL` is returned. + +If `occurrence` is specified, the search returns a specific occurrence of the +`regexp` in `value`, otherwise returns the first match. If `occurrence` is +greater than the number of matches found, `NULL` is returned. For +`occurrence` > 1, the function searches for additional occurrences beginning +with the character following the previous occurrence. Returns an error if: + The regular expression is invalid + The regular expression has more than one capturing group ++ The `position` is not a positive integer ++ The `occurrence` is not a positive integer **Return type** @@ -2834,67 +2883,72 @@ Returns an error if: **Examples** ```sql -WITH email_addresses AS - (SELECT 'foo@example.com' as email - UNION ALL - SELECT 'bar@example.org' as email - UNION ALL - SELECT 'baz@example.net' as email) - -SELECT - REGEXP_EXTRACT(email, r'^[a-zA-Z0-9_.+-]+') - AS user_name -FROM email_addresses; +SELECT REGEXP_EXTRACT('foo@example.com', r'^[a-zA-Z0-9_.+-]+') AS user_name /*-----------* | user_name | +-----------+ | foo | - | bar | - | baz | *-----------*/ ``` ```sql -WITH email_addresses AS - (SELECT 'foo@example.com' as email - UNION ALL - SELECT 'bar@example.org' as email - UNION ALL - SELECT 'baz@example.net' as email) - -SELECT - REGEXP_EXTRACT(email, r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.([a-zA-Z0-9-.]+$)') - AS top_level_domain -FROM email_addresses; +SELECT REGEXP_EXTRACT('foo@example.com', r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.([a-zA-Z0-9-.]+$)') /*------------------* | top_level_domain | +------------------+ | com | - | org | - | net | *------------------*/ ``` ```sql -WITH - characters AS ( - SELECT 'ab' AS value, '.b' AS regex UNION ALL - SELECT 'ab' AS value, '(.)b' AS regex UNION ALL - SELECT 'xyztb' AS value, '(.)+b' AS regex UNION ALL - SELECT 'ab' AS value, '(z)?b' AS regex - ) -SELECT value, regex, REGEXP_EXTRACT(value, regex) AS result FROM characters; +SELECT + REGEXP_EXTRACT('ab', '.b') AS result_a, + REGEXP_EXTRACT('ab', '(.)b') AS result_b, + REGEXP_EXTRACT('xyztb', '(.)+b') AS result_c, + REGEXP_EXTRACT('ab', '(z)?b') AS result_d + +/*-------------------------------------------* + | result_a | result_b | result_c | result_d | + +-------------------------------------------+ + | ab | a | t | NULL | + *-------------------------------------------*/ +``` -/*-------+---------+----------* - | value | regex | result | - +-------+---------+----------+ - | ab | .b | ab | - | ab | (.)b | a | - | xyztb | (.)+b | t | - | ab | (z)?b | NULL | - *-------+---------+----------*/ +```sql +WITH example AS +(SELECT 'Hello Helloo and Hellooo' AS value, 'H?ello+' AS regex, 1 as position, +1 AS occurrence UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 1, 2 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 1, 3 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 1, 4 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 2, 1 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 3, 1 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 3, 2 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 3, 3 UNION ALL +SELECT 'Hello Helloo and Hellooo', 'H?ello+', 20, 1 UNION ALL +SELECT 'cats&dogs&rabbits' ,'\\w+&', 1, 2 UNION ALL +SELECT 'cats&dogs&rabbits', '\\w+&', 2, 3 +) +SELECT value, regex, position, occurrence, REGEXP_EXTRACT(value, regex, +position, occurrence) AS regexp_value FROM example; + +/*--------------------------+---------+----------+------------+--------------* + | value | regex | position | occurrence | regexp_value | + +--------------------------+---------+----------+------------+--------------+ + | Hello Helloo and Hellooo | H?ello+ | 1 | 1 | Hello | + | Hello Helloo and Hellooo | H?ello+ | 1 | 2 | Helloo | + | Hello Helloo and Hellooo | H?ello+ | 1 | 3 | Hellooo | + | Hello Helloo and Hellooo | H?ello+ | 1 | 4 | NULL | + | Hello Helloo and Hellooo | H?ello+ | 2 | 1 | ello | + | Hello Helloo and Hellooo | H?ello+ | 3 | 1 | Helloo | + | Hello Helloo and Hellooo | H?ello+ | 3 | 2 | Hellooo | + | Hello Helloo and Hellooo | H?ello+ | 3 | 3 | NULL | + | Hello Helloo and Hellooo | H?ello+ | 20 | 1 | NULL | + | cats&dogs&rabbits | \w+& | 1 | 2 | dogs& | + | cats&dogs&rabbits | \w+& | 2 | 3 | NULL | + *--------------------------+---------+----------+------------+--------------*/ ``` [string-link-to-re2]: https://github.com/google/re2/wiki/Syntax @@ -2930,18 +2984,13 @@ Returns an error if: **Examples** ```sql -WITH code_markdown AS - (SELECT 'Try `function(x)` or `function(y)`' as code) +SELECT REGEXP_EXTRACT_ALL('Try `func(x)` or `func(y)`', '`(.+?)`') AS example -SELECT - REGEXP_EXTRACT_ALL(code, '`(.+?)`') AS example -FROM code_markdown; - -/*----------------------------* - | example | - +----------------------------+ - | [function(x), function(y)] | - *----------------------------*/ +/*--------------------* + | example | + +--------------------+ + | [func(x), func(y)] | + *--------------------*/ ``` [string-link-to-re2]: https://github.com/google/re2/wiki/Syntax @@ -3003,81 +3052,56 @@ Returns an error if: **Examples** ```sql -WITH example AS ( - SELECT 'ab@cd-ef' AS source_value, '@[^-]*' AS regexp UNION ALL - SELECT 'ab@d-ef', '@[^-]*' UNION ALL - SELECT 'abc@cd-ef', '@[^-]*' UNION ALL - SELECT 'abc-ef', '@[^-]*') -SELECT source_value, regexp, REGEXP_INSTR(source_value, regexp) AS instr -FROM example; +SELECT + REGEXP_INSTR('ab@cd-ef', '@[^-]*') AS instr_a, + REGEXP_INSTR('ab@d-ef', '@[^-]*') AS instr_b, + REGEXP_INSTR('abc@cd-ef', '@[^-]*') AS instr_c, + REGEXP_INSTR('abc-ef', '@[^-]*') AS instr_d, -/*--------------+--------+-------* - | source_value | regexp | instr | - +--------------+--------+-------+ - | ab@cd-ef | @[^-]* | 3 | - | ab@d-ef | @[^-]* | 3 | - | abc@cd-ef | @[^-]* | 4 | - | abc-ef | @[^-]* | 0 | - *--------------+--------+-------*/ +/*---------------------------------------* + | instr_a | instr_b | instr_c | instr_d | + +---------------------------------------+ + | 3 | 3 | 4 | 0 | + *---------------------------------------*/ ``` ```sql -WITH example AS ( - SELECT 'a@cd-ef b@cd-ef' AS source_value, '@[^-]*' AS regexp, 1 AS position UNION ALL - SELECT 'a@cd-ef b@cd-ef', '@[^-]*', 2 UNION ALL - SELECT 'a@cd-ef b@cd-ef', '@[^-]*', 3 UNION ALL - SELECT 'a@cd-ef b@cd-ef', '@[^-]*', 4) SELECT - source_value, regexp, position, - REGEXP_INSTR(source_value, regexp, position) AS instr -FROM example; - -/*-----------------+--------+----------+-------* - | source_value | regexp | position | instr | - +-----------------+--------+----------+-------+ - | a@cd-ef b@cd-ef | @[^-]* | 1 | 2 | - | a@cd-ef b@cd-ef | @[^-]* | 2 | 2 | - | a@cd-ef b@cd-ef | @[^-]* | 3 | 10 | - | a@cd-ef b@cd-ef | @[^-]* | 4 | 10 | - *-----------------+--------+----------+-------*/ -``` - -```sql -WITH example AS ( - SELECT 'a@cd-ef b@cd-ef c@cd-ef' AS source_value, - '@[^-]*' AS regexp, 1 AS position, 1 AS occurrence UNION ALL - SELECT 'a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 2 UNION ALL - SELECT 'a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 3) + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 1) AS instr_a, + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 2) AS instr_b, + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 3) AS instr_c, + REGEXP_INSTR('a@cd-ef b@cd-ef', '@[^-]*', 4) AS instr_d, + +/*---------------------------------------* + | instr_a | instr_b | instr_c | instr_d | + +---------------------------------------+ + | 2 | 2 | 10 | 10 | + *---------------------------------------*/ +``` + +```sql SELECT - source_value, regexp, position, occurrence, - REGEXP_INSTR(source_value, regexp, position, occurrence) AS instr -FROM example; + REGEXP_INSTR('a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 1) AS instr_a, + REGEXP_INSTR('a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 2) AS instr_b, + REGEXP_INSTR('a@cd-ef b@cd-ef c@cd-ef', '@[^-]*', 1, 3) AS instr_c -/*-------------------------+--------+----------+------------+-------* - | source_value | regexp | position | occurrence | instr | - +-------------------------+--------+----------+------------+-------+ - | a@cd-ef b@cd-ef c@cd-ef | @[^-]* | 1 | 1 | 2 | - | a@cd-ef b@cd-ef c@cd-ef | @[^-]* | 1 | 2 | 10 | - | a@cd-ef b@cd-ef c@cd-ef | @[^-]* | 1 | 3 | 18 | - *-------------------------+--------+----------+------------+-------*/ +/*-----------------------------* + | instr_a | instr_b | instr_c | + +-----------------------------+ + | 2 | 10 | 18 | + *-----------------------------*/ ``` ```sql -WITH example AS ( - SELECT 'a@cd-ef' AS source_value, '@[^-]*' AS regexp, - 1 AS position, 1 AS occurrence, 0 AS o_position UNION ALL - SELECT 'a@cd-ef', '@[^-]*', 1, 1, 1) SELECT - source_value, regexp, position, occurrence, o_position, - REGEXP_INSTR(source_value, regexp, position, occurrence, o_position) AS instr -FROM example; + REGEXP_INSTR('a@cd-ef', '@[^-]*', 1, 1, 0) AS instr_a, + REGEXP_INSTR('a@cd-ef', '@[^-]*', 1, 1, 1) AS instr_b -/*--------------+--------+----------+------------+------------+-------* - | source_value | regexp | position | occurrence | o_position | instr | - +--------------+--------+----------+------------+------------+-------+ - | a@cd-ef | @[^-]* | 1 | 1 | 0 | 2 | - | a@cd-ef | @[^-]* | 1 | 1 | 1 | 5 | - *--------------+--------+----------+------------+------------+-------*/ +/*-------------------* + | instr_a | instr_b | + +-------------------+ + | 2 | 5 | + *-------------------*/ ``` ### `REGEXP_MATCH` (Deprecated) @@ -3173,21 +3197,12 @@ regular expression syntax. **Examples** ```sql -WITH markdown AS - (SELECT '# Heading' as heading - UNION ALL - SELECT '# Another heading' as heading) - -SELECT - REGEXP_REPLACE(heading, r'^# ([a-zA-Z0-9\s]+$)', '

\\1

') - AS html -FROM markdown; +SELECT REGEXP_REPLACE('# Heading', r'^# ([a-zA-Z0-9\s]+$)', '

\\1

') AS html /*--------------------------* | html | +--------------------------+ |

Heading

| - |

Another heading

| *--------------------------*/ ``` @@ -3195,6 +3210,39 @@ FROM markdown; [string-link-to-lexical-literals]: https://github.com/google/zetasql/blob/master/docs/lexical.md#string_and_bytes_literals +### `REGEXP_SUBSTR` + +```sql +REGEXP_SUBSTR(value, regexp[, position[, occurrence]]) +``` + +**Description** + +Synonym for [REGEXP_EXTRACT][string-link-to-regex]. + +**Return type** + +`STRING` or `BYTES` + +**Examples** + +```sql +WITH example AS +(SELECT 'Hello World Helloo' AS value, 'H?ello+' AS regex, 1 AS position, 1 AS +occurrence +) +SELECT value, regex, position, occurrence, REGEXP_SUBSTR(value, regex, +position, occurrence) AS regexp_value FROM example; + +/*--------------------+---------+----------+------------+--------------* + | value | regex | position | occurrence | regexp_value | + +--------------------+---------+----------+------------+--------------+ + | Hello World Helloo | H?ello+ | 1 | 1 | Hello | + *--------------------+---------+----------+------------+--------------*/ +``` + +[string-link-to-regex]: #regexp_extract + ### `REPEAT` ```sql @@ -3217,21 +3265,33 @@ This function returns an error if the `repetitions` value is negative. **Examples** ```sql -SELECT t, n, REPEAT(t, n) AS REPEAT FROM UNNEST([ - STRUCT('abc' AS t, 3 AS n), - ('例子', 2), - ('abc', null), - (null, 3) -]); +SELECT REPEAT('abc', 3) AS results -/*------+------+-----------* - | t | n | REPEAT | - |------|------|-----------| - | abc | 3 | abcabcabc | - | 例子 | 2 | 例子例子 | - | abc | NULL | NULL | - | NULL | 3 | NULL | - *------+------+-----------*/ +/*-----------* + | results | + |-----------| + | abcabcabc | + *-----------*/ +``` + +```sql +SELECT REPEAT('abc', NULL) AS results + +/*---------* + | results | + |---------| + | NULL | + *---------*/ +``` + +```sql +SELECT REPEAT(NULL, 3) AS results + +/*---------* + | results | + |---------| + | NULL | + *---------*/ ``` ### `REPLACE` @@ -3293,23 +3353,23 @@ Returns the reverse of the input `STRING` or `BYTES`. **Examples** ```sql -WITH example AS ( - SELECT 'foo' AS sample_string, b'bar' AS sample_bytes UNION ALL - SELECT 'абвгд' AS sample_string, b'123' AS sample_bytes -) -SELECT - sample_string, - REVERSE(sample_string) AS reverse_string, - sample_bytes, - REVERSE(sample_bytes) AS reverse_bytes -FROM example; +SELECT REVERSE('abc') AS results + +/*---------* + | results | + +---------+ + | cba | + *---------*/ +``` -/*---------------+----------------+--------------+---------------* - | sample_string | reverse_string | sample_bytes | reverse_bytes | - +---------------+----------------+--------------+---------------+ - | foo | oof | bar | rab | - | абвгд | дгвба | 123 | 321 | - *---------------+----------------+--------------+---------------*/ +```sql +SELECT FORMAT('%T', REVERSE(b'1a3')) AS results + +/*---------* + | results | + +---------+ + | b"3a1" | + *---------*/ ``` ### `RIGHT` @@ -3339,42 +3399,22 @@ will be returned. **Examples** ```sql -WITH examples AS -(SELECT 'apple' as example -UNION ALL -SELECT 'banana' as example -UNION ALL -SELECT 'абвгд' as example -) -SELECT example, RIGHT(example, 3) AS right_example -FROM examples; +SELECT 'apple' AS example, RIGHT('apple', 3) AS right_example /*---------+---------------* | example | right_example | +---------+---------------+ | apple | ple | - | banana | ana | - | абвгд | вгд | *---------+---------------*/ ``` ```sql -WITH examples AS -(SELECT b'apple' as example -UNION ALL -SELECT b'banana' as example -UNION ALL -SELECT b'\xab\xcd\xef\xaa\xbb' as example -) -SELECT example, RIGHT(example, 3) AS right_example -FROM examples; +SELECT b'apple' AS example, RIGHT(b'apple', 3) AS right_example /*----------------------+---------------* | example | right_example | +----------------------+---------------+ | apple | ple | - | banana | ana | - | \xab\xcd\xef\xaa\xbb | \xef\xaa\xbb | *----------------------+---------------* ``` @@ -3416,72 +3456,53 @@ This function returns an error if: **Examples** ```sql -SELECT t, len, FORMAT('%T', RPAD(t, len)) AS RPAD FROM UNNEST([ - STRUCT('abc' AS t, 5 AS len), - ('abc', 2), - ('例子', 4) -]); +SELECT FORMAT('%T', RPAD('c', 5)) AS results -/*------+-----+----------* - | t | len | RPAD | - +------+-----+----------+ - | abc | 5 | "abc " | - | abc | 2 | "ab" | - | 例子 | 4 | "例子 " | - *------+-----+----------*/ +/*---------* + | results | + +---------+ + | "c " | + *---------*/ ``` ```sql -SELECT t, len, pattern, FORMAT('%T', RPAD(t, len, pattern)) AS RPAD FROM UNNEST([ - STRUCT('abc' AS t, 8 AS len, 'def' AS pattern), - ('abc', 5, '-'), - ('例子', 5, '中文') -]); +SELECT RPAD('b', 5, 'a') AS results -/*------+-----+---------+--------------* - | t | len | pattern | RPAD | - +------+-----+---------+--------------+ - | abc | 8 | def | "abcdefde" | - | abc | 5 | - | "abc--" | - | 例子 | 5 | 中文 | "例子中文中" | - *------+-----+---------+--------------*/ +/*---------* + | results | + +---------+ + | baaaa | + *---------*/ ``` ```sql -SELECT FORMAT('%T', t) AS t, len, FORMAT('%T', RPAD(t, len)) AS RPAD FROM UNNEST([ - STRUCT(b'abc' AS t, 5 AS len), - (b'abc', 2), - (b'\xab\xcd\xef', 4) -]); +SELECT RPAD('abc', 10, 'ghd') AS results -/*-----------------+-----+------------------* - | t | len | RPAD | - +-----------------+-----+------------------+ - | b"abc" | 5 | b"abc " | - | b"abc" | 2 | b"ab" | - | b"\xab\xcd\xef" | 4 | b"\xab\xcd\xef " | - *-----------------+-----+------------------*/ +/*------------* + | results | + +------------+ + | abcghdghdg | + *------------*/ ``` ```sql -SELECT - FORMAT('%T', t) AS t, - len, - FORMAT('%T', pattern) AS pattern, - FORMAT('%T', RPAD(t, len, pattern)) AS RPAD -FROM UNNEST([ - STRUCT(b'abc' AS t, 8 AS len, b'def' AS pattern), - (b'abc', 5, b'-'), - (b'\xab\xcd\xef', 5, b'\x00') -]); - -/*-----------------+-----+---------+-------------------------* - | t | len | pattern | RPAD | - +-----------------+-----+---------+-------------------------+ - | b"abc" | 8 | b"def" | b"abcdefde" | - | b"abc" | 5 | b"-" | b"abc--" | - | b"\xab\xcd\xef" | 5 | b"\x00" | b"\xab\xcd\xef\x00\x00" | - *-----------------+-----+---------+-------------------------*/ +SELECT RPAD('abc', 2, 'd') AS results + +/*---------* + | results | + +---------+ + | ab | + *---------*/ +``` + +```sql +SELECT FORMAT('%T', RPAD(b'abc', 10, b'ghd')) AS results + +/*---------------* + | results | + +---------------+ + | b"abcghdghdg" | + *---------------*/ ``` ### `RTRIM` @@ -3501,47 +3522,22 @@ Identical to [TRIM][string-link-to-trim], but only removes trailing characters. **Examples** ```sql -WITH items AS - (SELECT '***apple***' as item - UNION ALL - SELECT '***banana***' as item - UNION ALL - SELECT '***orange***' as item) - -SELECT - RTRIM(item, '*') as example -FROM items; +SELECT RTRIM('***apple***', '*') AS example /*-----------* | example | +-----------+ | ***apple | - | ***banana | - | ***orange | *-----------*/ ``` ```sql -WITH items AS - (SELECT 'applexxx' as item - UNION ALL - SELECT 'bananayyy' as item - UNION ALL - SELECT 'orangezzz' as item - UNION ALL - SELECT 'pearxyz' as item) - -SELECT - RTRIM(item, 'xyz') as example -FROM items; +SELECT RTRIM('applexxz', 'xyz') AS example /*---------* | example | +---------+ | apple | - | banana | - | orange | - | pear | *---------*/ ``` @@ -3597,30 +3593,12 @@ non-Latin characters, an empty `STRING` is returned. **Examples** ```sql -WITH example AS ( - SELECT 'Ashcraft' AS value UNION ALL - SELECT 'Raven' AS value UNION ALL - SELECT 'Ribbon' AS value UNION ALL - SELECT 'apple' AS value UNION ALL - SELECT 'Hello world!' AS value UNION ALL - SELECT ' H3##!@llo w00orld!' AS value UNION ALL - SELECT '#1' AS value UNION ALL - SELECT NULL AS value -) -SELECT value, SOUNDEX(value) AS soundex -FROM example; +SELECT 'Ashcraft' AS value, SOUNDEX('Ashcraft') AS soundex /*----------------------+---------* | value | soundex | +----------------------+---------+ | Ashcraft | A261 | - | Raven | R150 | - | Ribbon | R150 | - | apple | a140 | - | Hello world! | H464 | - | H3##!@llo w00orld! | H464 | - | #1 | | - | NULL | NULL | *----------------------+---------*/ ``` @@ -3699,22 +3677,11 @@ This function supports specifying [collation][collation]. **Examples** ```sql -WITH items AS - (SELECT 'foo' as item - UNION ALL - SELECT 'bar' as item - UNION ALL - SELECT 'baz' as item) - -SELECT - STARTS_WITH(item, 'b') as example -FROM items; +SELECT STARTS_WITH('bar', 'b') AS example /*---------* | example | +---------+ - | False | - | True | | True | *---------*/ ``` @@ -3741,30 +3708,12 @@ This function supports specifying [collation][collation]. **Examples** ```sql -WITH email_addresses AS - (SELECT - 'foo@example.com' AS email_address - UNION ALL - SELECT - 'foobar@example.com' AS email_address - UNION ALL - SELECT - 'foobarbaz@example.com' AS email_address - UNION ALL - SELECT - 'quxexample.com' AS email_address) - -SELECT - STRPOS(email_address, '@') AS example -FROM email_addresses; +SELECT STRPOS('foo@example.com', '@') AS example /*---------* | example | +---------+ | 4 | - | 7 | - | 10 | - | 0 | *---------*/ ``` @@ -3807,128 +3756,62 @@ return. **Examples** ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 2) as example -FROM items; +SELECT SUBSTR('apple', 2) AS example /*---------* | example | +---------+ | pple | - | anana | - | range | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 2, 2) as example -FROM items; +SELECT SUBSTR('apple', 2, 2) AS example /*---------* | example | +---------+ | pp | - | an | - | ra | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, -2) as example -FROM items; +SELECT SUBSTR('apple', -2) AS example /*---------* | example | +---------+ | le | - | na | - | ge | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 1, 123) as example -FROM items; +SELECT SUBSTR('apple', 1, 123) AS example /*---------* | example | +---------+ | apple | - | banana | - | orange | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 123) as example -FROM items; +SELECT SUBSTR('apple', 123) AS example /*---------* | example | +---------+ | | - | | - | | *---------*/ ``` ```sql -WITH items AS - (SELECT 'apple' as item - UNION ALL - SELECT 'banana' as item - UNION ALL - SELECT 'orange' as item) - -SELECT - SUBSTR(item, 123, 5) as example -FROM items; +SELECT SUBSTR('apple', 123, 5) AS example /*---------* | example | +---------+ | | - | | - | | *---------*/ ``` @@ -4050,41 +3933,101 @@ To convert from an array of code points to a `STRING` or `BYTES`, see **Examples** -The following example gets the code points for each element in an array of +The following examples get the code points for each element in an array of words. ```sql -SELECT word, TO_CODE_POINTS(word) AS code_points -FROM UNNEST(['foo', 'bar', 'baz', 'giraffe', 'llama']) AS word; +SELECT + 'foo' AS word, + TO_CODE_POINTS('foo') AS code_points /*---------+------------------------------------* | word | code_points | +---------+------------------------------------+ | foo | [102, 111, 111] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'bar' AS word, + TO_CODE_POINTS('bar') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | bar | [98, 97, 114] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'baz' AS word, + TO_CODE_POINTS('baz') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | baz | [98, 97, 122] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'giraffe' AS word, + TO_CODE_POINTS('giraffe') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | giraffe | [103, 105, 114, 97, 102, 102, 101] | + *---------+------------------------------------*/ +``` + +```sql +SELECT + 'llama' AS word, + TO_CODE_POINTS('llama') AS code_points + +/*---------+------------------------------------* + | word | code_points | + +---------+------------------------------------+ | llama | [108, 108, 97, 109, 97] | *---------+------------------------------------*/ ``` -The following example converts integer representations of `BYTES` to their +The following examples convert integer representations of `BYTES` to their corresponding ASCII character values. ```sql -SELECT word, TO_CODE_POINTS(word) AS bytes_value_as_integer -FROM UNNEST([b'\x00\x01\x10\xff', b'\x66\x6f\x6f']) AS word; +SELECT + b'\x66\x6f\x6f' AS bytes_value, + TO_CODE_POINTS(b'\x66\x6f\x6f') AS bytes_value_as_integer /*------------------+------------------------* - | word | bytes_value_as_integer | + | bytes_value | bytes_value_as_integer | +------------------+------------------------+ - | \x00\x01\x10\xff | [0, 1, 16, 255] | | foo | [102, 111, 111] | *------------------+------------------------*/ ``` +```sql +SELECT + b'\x00\x01\x10\xff' AS bytes_value, + TO_CODE_POINTS(b'\x00\x01\x10\xff') AS bytes_value_as_integer + +/*------------------+------------------------* + | bytes_value | bytes_value_as_integer | + +------------------+------------------------+ + | \x00\x01\x10\xff | [0, 1, 16, 255] | + *------------------+------------------------*/ +``` + The following example demonstrates the difference between a `BYTES` result and a -`STRING` result. +`STRING` result. Notice that the character `Ā` is represented as a two-byte +Unicode sequence. As a result, the `BYTES` version of `TO_CODE_POINTS` returns +an array with two elements, while the `STRING` version returns an array with a +single element. ```sql SELECT TO_CODE_POINTS(b'Ā') AS b_result, TO_CODE_POINTS('Ā') AS s_result; @@ -4096,10 +4039,6 @@ SELECT TO_CODE_POINTS(b'Ā') AS b_result, TO_CODE_POINTS('Ā') AS s_result; *------------+----------*/ ``` -Notice that the character, Ā, is represented as a two-byte Unicode sequence. As -a result, the `BYTES` version of `TO_CODE_POINTS` returns an array with two -elements, while the `STRING` version returns an array with a single element. - [string-link-to-code-points-wikipedia]: https://en.wikipedia.org/wiki/Code_point [string-link-to-codepoints-to-string]: #code_points_to_string @@ -4126,18 +4065,14 @@ in the `STRING` as two hexadecimal characters in the range **Example** ```sql -WITH Input AS ( - SELECT b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF' AS byte_str UNION ALL - SELECT b'foobar' -) -SELECT byte_str, TO_HEX(byte_str) AS hex_str -FROM Input; +SELECT + b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF' AS byte_string, + TO_HEX(b'\x00\x01\x02\x03\xAA\xEE\xEF\xFF') AS hex_string /*----------------------------------+------------------* | byte_string | hex_string | +----------------------------------+------------------+ | \x00\x01\x02\x03\xaa\xee\xef\xff | 00010203aaeeefff | - | foobar | 666f6f626172 | *----------------------------------+------------------*/ ``` @@ -4169,22 +4104,13 @@ type, either `STRING` or `BYTES`. **Examples** ```sql -WITH example AS ( - SELECT 'This is a cookie' AS expression, 'sco' AS source_characters, 'zku' AS - target_characters UNION ALL - SELECT 'A coaster' AS expression, 'co' AS source_characters, 'k' as - target_characters -) -SELECT expression, source_characters, target_characters, TRANSLATE(expression, -source_characters, target_characters) AS translate -FROM example; +SELECT TRANSLATE('This is a cookie', 'sco', 'zku') AS translate -/*------------------+-------------------+-------------------+------------------* - | expression | source_characters | target_characters | translate | - +------------------+-------------------+-------------------+------------------+ - | This is a cookie | sco | zku | Thiz iz a kuukie | - | A coaster | co | k | A kaster | - *------------------+-------------------+-------------------+------------------*/ +/*------------------* + | translate | + +------------------+ + | Thiz iz a kuukie | + *------------------*/ ``` ### `TRIM` @@ -4217,74 +4143,38 @@ In the following example, all leading and trailing whitespace characters are removed from `item` because `set_of_characters_to_remove` is not specified. ```sql -WITH items AS - (SELECT ' apple ' as item - UNION ALL - SELECT ' banana ' as item - UNION ALL - SELECT ' orange ' as item) - -SELECT - CONCAT('#', TRIM(item), '#') as example -FROM items; +SELECT CONCAT('#', TRIM( ' apple '), '#') AS example /*----------* | example | +----------+ | #apple# | - | #banana# | - | #orange# | *----------*/ ``` In the following example, all leading and trailing `*` characters are removed -from `item`. +from '***apple***'. ```sql -WITH items AS - (SELECT '***apple***' as item - UNION ALL - SELECT '***banana***' as item - UNION ALL - SELECT '***orange***' as item) - -SELECT - TRIM(item, '*') as example -FROM items; +SELECT TRIM('***apple***', '*') AS example /*---------* | example | +---------+ | apple | - | banana | - | orange | *---------*/ ``` In the following example, all leading and trailing `x`, `y`, and `z` characters -are removed from `item`. +are removed from 'xzxapplexxy'. ```sql -WITH items AS - (SELECT 'xxxapplexxx' as item - UNION ALL - SELECT 'yyybananayyy' as item - UNION ALL - SELECT 'zzzorangezzz' as item - UNION ALL - SELECT 'xyzpearxyz' as item) - -SELECT - TRIM(item, 'xyz') as example -FROM items; +SELECT TRIM('xzxapplexxy', 'xyz') as example /*---------* | example | +---------+ | apple | - | banana | - | orange | - | pear | *---------*/ ``` @@ -4298,7 +4188,7 @@ SELECT TRIM('abaW̊', 'Y̊') AS a, TRIM('W̊aba', 'Y̊') AS b, TRIM('abaŪ̊', 'Y̊') AS c, - TRIM('Ū̊aba', 'Y̊') AS d; + TRIM('Ū̊aba', 'Y̊') AS d /*------+------+------+------* | a | b | c | d | @@ -4311,21 +4201,12 @@ In the following example, all leading and trailing `b'n'`, `b'a'`, `b'\xab'` bytes are removed from `item`. ```sql -WITH items AS -( - SELECT b'apple' as item UNION ALL - SELECT b'banana' as item UNION ALL - SELECT b'\xab\xcd\xef\xaa\xbb' as item -) -SELECT item, TRIM(item, b'na\xab') AS examples -FROM items; +SELECT b'apple', TRIM(b'apple', b'na\xab') AS example /*----------------------+------------------* | item | example | +----------------------+------------------+ | apple | pple | - | banana | b | - | \xab\xcd\xef\xaa\xbb | \xcd\xef\xaa\xbb | *----------------------+------------------*/ ``` @@ -4383,26 +4264,12 @@ greater than 127 left intact. **Examples** ```sql -WITH items AS - (SELECT - 'foo' as item - UNION ALL - SELECT - 'bar' as item - UNION ALL - SELECT - 'baz' as item) - -SELECT - UPPER(item) AS example -FROM items; +SELECT UPPER('foo') AS example /*---------* | example | +---------+ | FOO | - | BAR | - | BAZ | *---------*/ ``` diff --git a/docs/table-functions.md b/docs/table-functions.md new file mode 100644 index 000000000..431476140 --- /dev/null +++ b/docs/table-functions.md @@ -0,0 +1,210 @@ + + + + +# Table-valued functions + + +ZetaSQL supports table-valued functions (TVFs). +A TVF returns an entire output table instead of +a single scalar value, and appears in the `FROM` clause like a table subquery. + +## Create a TVF + +You can create a TVF using the following syntax: + +```sql +CREATE + { TEMPORARY | TEMP } TABLE FUNCTION + function_name ( [ function_parameter [ DEFAULT value_for_argument ] [, ...] ] ) + [ RETURNS TABLE < column_declaration [, ...] > ] + [ { AS query | LANGUAGE language_name AS string_literal } ] + +function_parameter: + parameter_name { data_type | ANY TYPE | ANY TABLE } + +column_declaration: + column_name data_type +``` + ++ `CREATE ... TABLE FUNCTION`: Creates a new + [table-valued function][table-valued-function] function. + A function can have zero or more function parameters. ++ `TEMPORARY` or `TEMP`: Indicates that the function is temporary; that is it + exists for the lifetime of the session. ++ `function_parameter`: A parameter for the function. + + + `parameter_name`: The name of the parameter. + + + `data_type`: A ZetaSQL [data type][data-types]. + + + `ANY TYPE`: The function will accept an argument of any type for this + function parameter. If more than one parameter includes `ANY TYPE`, + a relationship is not enforced between these parameters when the function + is defined. However, if the type of argument passed into the function at + call time is incompatible with the function definition, this will + result in an error. + + `ANY TYPE` is a [_templated function parameter_][templated-parameters]. + + + + `ANY TABLE`. The function will accept an argument of any relation type for + this argument. However, passing the function arguments of types that are + incompatible with the function definition will result in an error at + call time. + + `ANY TABLE` is a [_templated function parameter_][templated-parameters]. + ++ `DEFAULT value_for_argument`: A default argument for a function parameter. + The `value_for_argument` has to be coercible to the function parameter's + data type. If any function parameter has a default value, all later + function parameters must also have default values. ++ `RETURNS TABLE`: Specifies the schema of the table that a table-valued + function returns as a comma-separated list of `column_name` and `TYPE` + pairs. If `RETURNS TABLE` is absent, ZetaSQL infers the + output schema from the `AS query` statement in the function body. ++ `AS query`: If you want to create a SQL TVF, specifies the SQL query to run. ++ `LANGUAGE ... AS`: If you want to create an external TVF, specifies the + language and code to use. + `language_name` represents the name of the language, such + as `js` for JavaScript. `string_literal` represents the code that defines + the function body. + +You can create a public or privately-scoped TVF in a module. To learn more, +see [Modules][modules]. + +## Specify TVF arguments {#tvf_arguments} + +When a TVF with function parameters is called, arguments must be passed in for +all function parameters that do not have default values. An argument can be of +any supported ZetaSQL type or table, but must be coercible to the +related function parameter's type. + +For non-table arguments, you can optionally include default arguments with the +`DEFAULT value_for_argument` clause. The `value_for_argument` expression runs +when `CREATE FUNCTION` is analyzed, and has to be coercible to the argument type +of the argument. If any argument has a default value, all later arguments must +also have default values. Calls to the function may optionally omit values for +arguments that have default values. + +Specify a table argument the same way you specify the fields of a +[STRUCT][data-types-struct]. + +```sql +parameter_name TABLE +``` + +The table argument can specify a [value table][datamodel-value-tables], +in which each row +is a single column of a specific type. To specify a value table as an argument, +include only the `data_type`, leaving out the `column_name`: + +```sql +parameter_name TABLE +``` + +In many cases, the `data_type` of the single column in the value table is a +protocol buffer; for example: + +```sql +CREATE TEMP TABLE FUNCTION AggregatedMovieLogs( + TicketPurchases TABLE) +``` + +The function body can refer directly to fields within the proto. + +You have the option to specify the input table using the templated type `ANY +TABLE` in place of `TABLE`. This option enables +you to create a polymorphic TVF that accepts any table as input. + +**Example** + +The following example implements a pair of TVFs that define parameterized views +of a range of rows from the Customer table. The first returns all rows for a +range of `CustomerIds`; the second calls the first function and applies an +additional filter based on `CustomerType`. + +```sql +CREATE TEMP TABLE FUNCTION CustomerRange(MinID INT64, MaxID INT64) +AS ( + SELECT * + FROM Customer + WHERE CustomerId >= MinId AND CustomerId <= MaxId +); + +CREATE TEMP TABLE FUNCTION CustomerRangeWithCustomerType( + MinId INT64, + MaxId INT64, + customer_type + ads.boulder.schema.CustomerType + DEFAULT 'CUSTOMER_TYPE_ADVERTISER') +AS ( + SELECT * + FROM CustomerRange(MinId, MaxId) + WHERE type = customer_type +); +``` + +The following function returns all rows from the input table if the first +argument is greater than the second argument; otherwise it returns no rows. + +```sql +CREATE TEMP TABLE FUNCTION MyFunction( + first_value ANY TYPE, + second_value ANY TYPE, + MyInputTable ANY TABLE) + AS + SELECT * + FROM MyInputTable + WHERE first_value > second_value; +``` + +The following function accepts two integers and a table with any set of columns +and returns rows from the table where the predicate evaluates to true. The input +table `SelectedCustomers` must contain a column named `creation_time`, and +`creation_time` must be a numeric type, or the function will return an error. + +```sql +CREATE TEMP TABLE FUNCTION CustomerCreationTimeRange( + min_creation_time INT64, + max_creation_time INT64, + SelectedCustomers ANY TABLE) + AS + SELECT * + FROM SelectedCustomers + WHERE creation_time >= min_creation_time + AND creation_time <= max_creation_time; +``` + +## Call a TVF + +To call a TVF, see [Table function calls][table-function-calls]. + +## Templated function parameters + +A templated function parameter can match more than one argument type at +function call time. If a function signature includes a +templated function parameter, ZetaSQL allows function calls +to pass to the function any argument type as long as the function body is +valid for that argument type. + + + +[table-valued-function]: #tvfs + +[tvf-syntax]: #tvf_structure + +[table-function-calls]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#table_function_calls + +[templated-parameters]: #templated_function_parameters + +[data-types]: https://github.com/google/zetasql/blob/master/docs/data-types.md + +[data-types-struct]: https://github.com/google/zetasql/blob/master/docs/data-types.md#struct_type + +[datamodel-value-tables]: https://github.com/google/zetasql/blob/master/docs/data-model.md#value_tables + +[modules]: https://github.com/google/zetasql/blob/master/docs/modules.md + + + diff --git a/docs/time-series-functions.md b/docs/time-series-functions.md new file mode 100644 index 000000000..7c275c72d --- /dev/null +++ b/docs/time-series-functions.md @@ -0,0 +1,396 @@ + + + + +# Time series functions + +ZetaSQL supports the following time series functions. + +### Function list + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameSummary
DATE_BUCKET + + + Gets the lower bound of the date bucket that contains a date. +
DATETIME_BUCKET + + + Gets the lower bound of the datetime bucket that contains a datetime. +
TIMESTAMP_BUCKET + + + Gets the lower bound of the timestamp bucket that contains a timestamp. +
+ +### `DATE_BUCKET` + +```sql +DATE_BUCKET(date_in_bucket, bucket_width) +``` + +```sql +DATE_BUCKET(date_in_bucket, bucket_width, bucket_origin_date) +``` + +**Description** + +Gets the lower bound of the date bucket that contains a date. + +**Definitions** + ++ `date_in_bucket`: A `DATE` value that you can use to look up a date bucket. ++ `bucket_width`: An `INTERVAL` value that represents the width of + a date bucket. A [single interval][interval-single] with + [date parts][interval-parts] is supported. ++ `bucket_origin_date`: A `DATE` value that represents a point in time. All + buckets expand left and right from this point. If this argument is not set, + `1950-01-01` is used by default. + +**Return type** + +`DATE` + +**Examples** + +In the following example, the origin is omitted and the default origin, +`1950-01-01` is used. All buckets expand in both directions from the origin, +and the size of each bucket is two days. The lower bound of the bucket in +which `my_date` belongs is returned. + +```sql +WITH some_dates AS ( + SELECT DATE '1949-12-29' AS my_date UNION ALL + SELECT DATE '1949-12-30' UNION ALL + SELECT DATE '1949-12-31' UNION ALL + SELECT DATE '1950-01-01' UNION ALL + SELECT DATE '1950-01-02' UNION ALL + SELECT DATE '1950-01-03' +) +SELECT DATE_BUCKET(my_date, INTERVAL 2 DAY) AS bucket_lower_bound +FROM some_dates; + +/*--------------------+ + | bucket_lower_bound | + +--------------------+ + | 1949-12-28 | + | 1949-12-30 | + | 1949-12-30 | + | 1950-12-01 | + | 1950-12-01 | + | 1950-12-03 | + +--------------------*/ + +-- Some date buckets that originate from 1950-01-01: +-- + Bucket: ... +-- + Bucket: [1949-12-28, 1949-12-30) +-- + Bucket: [1949-12-30, 1950-01-01) +-- + Origin: [1950-01-01] +-- + Bucket: [1950-01-01, 1950-01-03) +-- + Bucket: [1950-01-03, 1950-01-05) +-- + Bucket: ... +``` + +In the following example, the origin has been changed to `2000-12-24`, +and all buckets expand in both directions from this point. The size of each +bucket is seven days. The lower bound of the bucket in which `my_date` belongs +is returned: + +```sql +WITH some_dates AS ( + SELECT DATE '2000-12-20' AS my_date UNION ALL + SELECT DATE '2000-12-21' UNION ALL + SELECT DATE '2000-12-22' UNION ALL + SELECT DATE '2000-12-23' UNION ALL + SELECT DATE '2000-12-24' UNION ALL + SELECT DATE '2000-12-25' +) +SELECT DATE_BUCKET( + my_date, + INTERVAL 7 DAY, + DATE '2000-12-24') AS bucket_lower_bound +FROM some_dates; + +/*--------------------+ + | bucket_lower_bound | + +--------------------+ + | 2000-12-17 | + | 2000-12-17 | + | 2000-12-17 | + | 2000-12-17 | + | 2000-12-24 | + | 2000-12-24 | + +--------------------*/ + +-- Some date buckets that originate from 2000-12-24: +-- + Bucket: ... +-- + Bucket: [2000-12-10, 2000-12-17) +-- + Bucket: [2000-12-17, 2000-12-24) +-- + Origin: [2000-12-24] +-- + Bucket: [2000-12-24, 2000-12-31) +-- + Bucket: [2000-12-31, 2000-01-07) +-- + Bucket: ... +``` + +[interval-single]: https://github.com/google/zetasql/blob/master/docs/data-types.md#single_datetime_part_interval + +[interval-range]: https://github.com/google/zetasql/blob/master/docs/data-types.md#range_datetime_part_interval + +[interval-parts]: https://github.com/google/zetasql/blob/master/docs/data-types.md#interval_datetime_parts + +### `DATETIME_BUCKET` + +```sql +DATETIME_BUCKET(datetime_in_bucket, bucket_width) +``` + +```sql +DATETIME_BUCKET(datetime_in_bucket, bucket_width, bucket_origin_datetime) +``` + +**Description** + +Gets the lower bound of the datetime bucket that contains a datetime. + +**Definitions** + ++ `datetime_in_bucket`: A `DATETIME` value that you can use to look up a + datetime bucket. ++ `bucket_width`: An `INTERVAL` value that represents the width of + a datetime bucket. A [single interval][interval-single] with + [date and time parts][interval-parts] is supported. ++ `bucket_origin_datetime`: A `DATETIME` value that represents a point in + time. All buckets expand left and right from this point. If this argument + is not set, `1950-01-01 00:00:00` is used by default. + +**Return type** + +`DATETIME` + +**Examples** + +In the following example, the origin is omitted and the default origin, +`1950-01-01 00:00:00` is used. All buckets expand in both directions from the +origin, and the size of each bucket is 12 hours. The lower bound of the bucket +in which `my_datetime` belongs is returned: + +```sql +WITH some_datetimes AS ( + SELECT DATETIME '1949-12-30 13:00:00' AS my_datetime UNION ALL + SELECT DATETIME '1949-12-31 00:00:00' UNION ALL + SELECT DATETIME '1949-12-31 13:00:00' UNION ALL + SELECT DATETIME '1950-01-01 00:00:00' UNION ALL + SELECT DATETIME '1950-01-01 13:00:00' UNION ALL + SELECT DATETIME '1950-01-02 00:00:00' +) +SELECT DATETIME_BUCKET(my_datetime, INTERVAL 12 HOUR) AS bucket_lower_bound +FROM some_datetimes; + +/*---------------------+ + | bucket_lower_bound | + +---------------------+ + | 1949-12-30 12:00:00 | + | 1949-12-31 00:00:00 | + | 1949-12-31 12:00:00 | + | 1950-01-01 00:00:00 | + | 1950-01-01 12:00:00 | + | 1950-01-02 00:00:00 | + +---------------------*/ + +-- Some datetime buckets that originate from 1950-01-01 00:00:00: +-- + Bucket: ... +-- + Bucket: [1949-12-30 00:00:00, 1949-12-30 12:00:00) +-- + Bucket: [1949-12-30 12:00:00, 1950-01-01 00:00:00) +-- + Origin: [1950-01-01 00:00:00] +-- + Bucket: [1950-01-01 00:00:00, 1950-01-01 12:00:00) +-- + Bucket: [1950-01-01 12:00:00, 1950-02-00 00:00:00) +-- + Bucket: ... +``` + +In the following example, the origin has been changed to `2000-12-24 12:00:00`, +and all buckets expand in both directions from this point. The size of each +bucket is seven days. The lower bound of the bucket in which `my_datetime` +belongs is returned: + +```sql +WITH some_datetimes AS ( + SELECT DATETIME '2000-12-20 00:00:00' AS my_datetime UNION ALL + SELECT DATETIME '2000-12-21 00:00:00' UNION ALL + SELECT DATETIME '2000-12-22 00:00:00' UNION ALL + SELECT DATETIME '2000-12-23 00:00:00' UNION ALL + SELECT DATETIME '2000-12-24 00:00:00' UNION ALL + SELECT DATETIME '2000-12-25 00:00:00' +) +SELECT DATETIME_BUCKET( + my_datetime, + INTERVAL 7 DAY, + DATETIME '2000-12-22 12:00:00') AS bucket_lower_bound +FROM some_datetimes; + +/*--------------------+ + | bucket_lower_bound | + +--------------------+ + | 2000-12-15 12:00:00 | + | 2000-12-15 12:00:00 | + | 2000-12-15 12:00:00 | + | 2000-12-22 12:00:00 | + | 2000-12-22 12:00:00 | + | 2000-12-22 12:00:00 | + +--------------------*/ + +-- Some datetime buckets that originate from 2000-12-22 12:00:00: +-- + Bucket: ... +-- + Bucket: [2000-12-08 12:00:00, 2000-12-15 12:00:00) +-- + Bucket: [2000-12-15 12:00:00, 2000-12-22 12:00:00) +-- + Origin: [2000-12-22 12:00:00] +-- + Bucket: [2000-12-22 12:00:00, 2000-12-29 12:00:00) +-- + Bucket: [2000-12-29 12:00:00, 2000-01-05 12:00:00) +-- + Bucket: ... +``` + +[interval-single]: https://github.com/google/zetasql/blob/master/docs/data-types.md#single_datetime_part_interval + +[interval-parts]: https://github.com/google/zetasql/blob/master/docs/data-types.md#interval_datetime_parts + +### `TIMESTAMP_BUCKET` + +```sql +TIMESTAMP_BUCKET(timestamp_in_bucket, bucket_width) +``` + +```sql +TIMESTAMP_BUCKET(timestamp_in_bucket, bucket_width, bucket_origin_timestamp) +``` + +**Description** + +Gets the lower bound of the timestamp bucket that contains a timestamp. + +**Definitions** + ++ `timestamp_in_bucket`: A `TIMESTAMP` value that you can use to look up a + timestamp bucket. ++ `bucket_width`: An `INTERVAL` value that represents the width of + a timestamp bucket. A [single interval][interval-single] with + [date and time parts][interval-parts] is supported. ++ `bucket_origin_timestamp`: A `TIMESTAMP` value that represents a point in + time. All buckets expand left and right from this point. If this argument + is not set, `1950-01-01 00:00:00` is used by default. + +**Return type** + +`TIMESTAMP` + +**Examples** + +In the following example, the origin is omitted and the default origin, +`1950-01-01 00:00:00` is used. All buckets expand in both directions from the +origin, and the size of each bucket is 12 hours. The lower bound of the bucket +in which `my_timestamp` belongs is returned: + +```sql +WITH some_timestamps AS ( + SELECT TIMESTAMP '1949-12-30 13:00:00.00' AS my_timestamp UNION ALL + SELECT TIMESTAMP '1949-12-31 00:00:00.00' UNION ALL + SELECT TIMESTAMP '1949-12-31 13:00:00.00' UNION ALL + SELECT TIMESTAMP '1950-01-01 00:00:00.00' UNION ALL + SELECT TIMESTAMP '1950-01-01 13:00:00.00' UNION ALL + SELECT TIMESTAMP '1950-01-02 00:00:00.00' +) +SELECT TIMESTAMP_BUCKET(my_timestamp, INTERVAL 12 HOUR) AS bucket_lower_bound +FROM some_timestamps; + +-- Display of results may differ, depending upon the environment and +-- time zone where this query was executed. +/*---------------------------------------------+ + | bucket_lower_bound | + +---------------------------------------------+ + | 2000-12-30 12:00:00.000 America/Los_Angeles | + | 2000-12-31 00:00:00.000 America/Los_Angeles | + | 2000-12-31 12:00:00.000 America/Los_Angeles | + | 2000-01-01 00:00:00.000 America/Los_Angeles | + | 2000-01-01 12:00:00.000 America/Los_Angeles | + | 2000-01-01 00:00:00.000 America/Los_Angeles | + +---------------------------------------------*/ + +-- Some timestamp buckets that originate from 1950-01-01 00:00:00: +-- + Bucket: ... +-- + Bucket: [1949-12-30 00:00:00.00 UTC, 1949-12-30 12:00:00.00 UTC) +-- + Bucket: [1949-12-30 12:00:00.00 UTC, 1950-01-01 00:00:00.00 UTC) +-- + Origin: [1950-01-01 00:00:00.00 UTC] +-- + Bucket: [1950-01-01 00:00:00.00 UTC, 1950-01-01 12:00:00.00 UTC) +-- + Bucket: [1950-01-01 12:00:00.00 UTC, 1950-02-00 00:00:00.00 UTC) +-- + Bucket: ... +``` + +In the following example, the origin has been changed to `2000-12-24 12:00:00`, +and all buckets expand in both directions from this point. The size of each +bucket is seven days. The lower bound of the bucket in which `my_timestamp` +belongs is returned: + +```sql +WITH some_timestamps AS ( + SELECT TIMESTAMP '2000-12-20 00:00:00.00' AS my_timestamp UNION ALL + SELECT TIMESTAMP '2000-12-21 00:00:00.00' UNION ALL + SELECT TIMESTAMP '2000-12-22 00:00:00.00' UNION ALL + SELECT TIMESTAMP '2000-12-23 00:00:00.00' UNION ALL + SELECT TIMESTAMP '2000-12-24 00:00:00.00' UNION ALL + SELECT TIMESTAMP '2000-12-25 00:00:00.00' +) +SELECT TIMESTAMP_BUCKET( + my_timestamp, + INTERVAL 7 DAY, + TIMESTAMP '2000-12-22 12:00:00.00') AS bucket_lower_bound +FROM some_timestamps; + +-- Display of results may differ, depending upon the environment and +-- time zone where this query was executed. +/*---------------------------------------------+ + | bucket_lower_bound | + +---------------------------------------------+ + | 2000-12-15 12:00:00.000 America/Los_Angeles | + | 2000-12-15 12:00:00.000 America/Los_Angeles | + | 2000-12-15 12:00:00.000 America/Los_Angeles | + | 2000-12-22 12:00:00.000 America/Los_Angeles | + | 2000-12-22 12:00:00.000 America/Los_Angeles | + | 2000-12-22 12:00:00.000 America/Los_Angeles | + +---------------------------------------------*/ + +-- Some timestamp buckets that originate from 2000-12-22 12:00:00: +-- + Bucket: ... +-- + Bucket: [2000-12-08 12:00:00.00 UTC, 2000-12-15 12:00:00.00 UTC) +-- + Bucket: [2000-12-15 12:00:00.00 UTC, 2000-12-22 12:00:00.00 UTC) +-- + Origin: [2000-12-22 12:00:00.00 UTC] +-- + Bucket: [2000-12-22 12:00:00.00 UTC, 2000-12-29 12:00:00.00 UTC) +-- + Bucket: [2000-12-29 12:00:00.00 UTC, 2000-01-05 12:00:00.00 UTC) +-- + Bucket: ... +``` + +[interval-single]: https://github.com/google/zetasql/blob/master/docs/data-types.md#single_datetime_part_interval + +[interval-parts]: https://github.com/google/zetasql/blob/master/docs/data-types.md#interval_datetime_parts + diff --git a/docs/time_functions.md b/docs/time_functions.md index 4277107e3..c63f48811 100644 --- a/docs/time_functions.md +++ b/docs/time_functions.md @@ -141,24 +141,6 @@ SELECT CURRENT_TIME() as now; *----------------------------*/ ``` -When a column named `current_time` is present, the column name and the function -call without parentheses are ambiguous. To ensure the function call, add -parentheses; to ensure the column name, qualify it with its -[range variable][time-functions-link-to-range-variables]. For example, the -following query will select the function in the `now` column and the table -column in the `current_time` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_time`) -SELECT current_time() as now, t.current_time FROM t; - -/*-----------------+--------------* - | now | current_time | - +-----------------+--------------+ - | 15:31:38.776361 | column value | - *-----------------+--------------*/ -``` - [time-functions-link-to-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables [time-link-to-timezone-definitions]: https://github.com/google/zetasql/blob/master/docs/timestamp_functions.md#timezone_definitions @@ -416,8 +398,9 @@ Gets the number of unit boundaries between two `TIME` values (`end_time` - + `start_time`: The starting `TIME` value. + `end_time`: The ending `TIME` value. -+ `granularity`: The time part that represents the granularity. - This can be: ++ `granularity`: The time part that represents the granularity. If + you passed in `TIME` values for the first arguments, `granularity` can + be: + `NANOSECOND` @@ -435,6 +418,10 @@ Produces an error if the computation overflows, such as if the difference in nanoseconds between the two `TIME` values overflows. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `TIME_DIFF(TIMESTAMP, TIMESTAMP, PART)` +behaves like `TIMESTAMP_DIFF(TIMESTAMP, TIMESTAMP, PART)`. + **Return Data Type** `INT64` @@ -499,21 +486,32 @@ SELECT ### `TIME_TRUNC` ```sql -TIME_TRUNC(time_expression, time_part) +TIME_TRUNC(time_expression, granularity) ``` **Description** -Truncates a `TIME` value to the granularity of `time_part`. The `TIME` value -is always rounded to the beginning of `time_part`, which can be one of the -following: +Truncates a `TIME` value at a particular time granularity. The `TIME` value +is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `time_expression`: The `TIME` value to truncate. ++ `granularity`: The time part that represents the granularity. If + you passed in a `TIME` value for the first argument, `granularity` can + be: + + + `NANOSECOND`: If used, nothing is truncated from the value. + + + `MICROSECOND`: The nearest lesser than or equal microsecond. + + + `MILLISECOND`: The nearest lesser than or equal millisecond. + + + `SECOND`: The nearest lesser than or equal second. + + + `MINUTE`: The nearest lesser than or equal minute. -+ `NANOSECOND`: If used, nothing is truncated from the value. -+ `MICROSECOND`: The nearest lessor or equal microsecond. -+ `MILLISECOND`: The nearest lessor or equal millisecond. -+ `SECOND`: The nearest lessor or equal second. -+ `MINUTE`: The nearest lessor or equal minute. -+ `HOUR`: The nearest lessor or equal hour. + + `HOUR`: The nearest lesser than or equal hour. **Return Data Type** diff --git a/docs/timestamp_functions.md b/docs/timestamp_functions.md index be23f0779..85fbfefc1 100644 --- a/docs/timestamp_functions.md +++ b/docs/timestamp_functions.md @@ -255,24 +255,6 @@ SELECT CURRENT_TIMESTAMP() AS now; *---------------------------------------------*/ ``` -When a column named `current_timestamp` is present, the column name and the -function call without parentheses are ambiguous. To ensure the function call, -add parentheses; to ensure the column name, qualify it with its -[range variable][timestamp-functions-link-to-range-variables]. For example, the -following query selects the function in the `now` column and the table -column in the `current_timestamp` column. - -```sql -WITH t AS (SELECT 'column value' AS `current_timestamp`) -SELECT current_timestamp() AS now, t.current_timestamp FROM t; - -/*---------------------------------------------+-------------------* - | now | current_timestamp | - +---------------------------------------------+-------------------+ - | 2020-06-02 17:00:53.110 America/Los_Angeles | column value | - *---------------------------------------------+-------------------*/ -``` - [timestamp-functions-link-to-range-variables]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#range_variables ### `EXTRACT` @@ -337,11 +319,15 @@ In the following example, `EXTRACT` returns a value corresponding to the `DAY` time part. ```sql -WITH Input AS (SELECT TIMESTAMP("2008-12-25 05:30:00+00") AS timestamp_value) SELECT - EXTRACT(DAY FROM timestamp_value AT TIME ZONE "UTC") AS the_day_utc, - EXTRACT(DAY FROM timestamp_value AT TIME ZONE "America/Los_Angeles") AS the_day_california -FROM Input + EXTRACT( + DAY + FROM TIMESTAMP('2008-12-25 05:30:00+00') AT TIME ZONE 'UTC') + AS the_day_utc, + EXTRACT( + DAY + FROM TIMESTAMP('2008-12-25 05:30:00+00') AT TIME ZONE 'America/Los_Angeles') + AS the_day_california /*-------------+--------------------* | the_day_utc | the_day_california | @@ -350,38 +336,108 @@ FROM Input *-------------+--------------------*/ ``` -In the following example, `EXTRACT` returns values corresponding to different +In the following examples, `EXTRACT` returns values corresponding to different time parts from a column of type `TIMESTAMP`. ```sql -WITH Timestamps AS ( - SELECT TIMESTAMP("2005-01-03 12:34:56+00") AS timestamp_value UNION ALL - SELECT TIMESTAMP("2007-12-31 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2009-01-01 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2009-12-31 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2017-01-02 12:00:00+00") UNION ALL - SELECT TIMESTAMP("2017-05-26 12:00:00+00") -) SELECT - timestamp_value, - EXTRACT(ISOYEAR FROM timestamp_value) AS isoyear, - EXTRACT(ISOWEEK FROM timestamp_value) AS isoweek, - EXTRACT(YEAR FROM timestamp_value) AS year, - EXTRACT(WEEK FROM timestamp_value) AS week -FROM Timestamps -ORDER BY timestamp_value; + EXTRACT(ISOYEAR FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2005-01-03 12:34:56+00")) AS week + +-- Display of results may differ, depending upon the environment and +-- time zone where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2005 | 1 | 2005 | 1 | + *---------+---------+------+------*/ +``` --- Display of results may differ, depending upon the environment and time zone where this query was executed. -/*---------------------------------------------+---------+---------+------+------* - | timestamp_value | isoyear | isoweek | year | week | - +---------------------------------------------+---------+---------+------+------+ - | 2005-01-03 04:34:56.000 America/Los_Angeles | 2005 | 1 | 2005 | 1 | - | 2007-12-31 04:00:00.000 America/Los_Angeles | 2008 | 1 | 2007 | 52 | - | 2009-01-01 04:00:00.000 America/Los_Angeles | 2009 | 1 | 2009 | 0 | - | 2009-12-31 04:00:00.000 America/Los_Angeles | 2009 | 53 | 2009 | 52 | - | 2017-01-02 04:00:00.000 America/Los_Angeles | 2017 | 1 | 2017 | 1 | - | 2017-05-26 05:00:00.000 America/Los_Angeles | 2017 | 21 | 2017 | 21 | - *---------------------------------------------+---------+---------+------+------*/ +```sql +SELECT + TIMESTAMP("2007-12-31 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2007-12-31 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2008 | 1 | 2007 | 52 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2009-01-01 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2009-01-01 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2009 | 1 | 2009 | 0 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2009-12-31 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2009-12-31 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2009 | 53 | 2009 | 52 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2017-01-02 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2017-01-02 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2017 | 1 | 2017 | 1 | + *---------+---------+------+------*/ +``` + +```sql +SELECT + TIMESTAMP("2017-05-26 12:00:00+00") AS timestamp_value, + EXTRACT(ISOYEAR FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS isoyear, + EXTRACT(ISOWEEK FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS isoweek, + EXTRACT(YEAR FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS year, + EXTRACT(WEEK FROM TIMESTAMP("2017-05-26 12:00:00+00")) AS week + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*---------+---------+------+------* + | isoyear | isoweek | year | week | + +---------+---------+------+------+ + | 2017 | 21 | 2017 | 21 | + *---------+---------+------+------*/ ``` In the following example, `timestamp_expression` falls on a Monday. `EXTRACT` @@ -389,19 +445,17 @@ calculates the first column using weeks that begin on Sunday, and it calculates the second column using weeks that begin on Monday. ```sql -WITH table AS (SELECT TIMESTAMP("2017-11-06 00:00:00+00") AS timestamp_value) SELECT - timestamp_value, - EXTRACT(WEEK(SUNDAY) FROM timestamp_value) AS week_sunday, - EXTRACT(WEEK(MONDAY) FROM timestamp_value) AS week_monday -FROM table; - --- Display of results may differ, depending upon the environment and time zone where this query was executed. -/*---------------------------------------------+-------------+---------------* - | timestamp_value | week_sunday | week_monday | - +---------------------------------------------+-------------+---------------+ - | 2017-11-05 16:00:00.000 America/Los_Angeles | 45 | 44 | - *---------------------------------------------+-------------+---------------*/ + EXTRACT(WEEK(SUNDAY) FROM TIMESTAMP("2017-11-06 00:00:00+00")) AS week_sunday, + EXTRACT(WEEK(MONDAY) FROM TIMESTAMP("2017-11-06 00:00:00+00")) AS week_monday + +-- Display of results may differ, depending upon the environment and time zone +-- where this query was executed. +/*-------------+---------------* + | week_sunday | week_monday | + +-------------+---------------+ + | 45 | 44 | + *-------------+---------------*/ ``` [ISO-8601]: https://en.wikipedia.org/wiki/ISO_8601 @@ -725,8 +779,9 @@ Gets the number of unit boundaries between two `TIMESTAMP` values + `start_timestamp`: The starting `TIMESTAMP` value. + `end_timestamp`: The ending `TIMESTAMP` value. -+ `granularity`: The timestamp part that represents the granularity. - This can be: ++ `granularity`: The timestamp part that represents the granularity. If + you passed in `TIMESTAMP` values for the first arguments, `granularity` can + be: + `NANOSECOND` @@ -745,6 +800,10 @@ Produces an error if the computation overflows, such as if the difference in nanoseconds between the two `TIMESTAMP` values overflows. +Note: The behavior of the this function follows the type of arguments passed in. +For example, `TIMESTAMP_DIFF(DATE, DATE, PART)` +behaves like `DATE_DIFF(DATE, DATE, PART)`. + **Return Data Type** `INT64` @@ -1018,44 +1077,63 @@ SELECT ### `TIMESTAMP_TRUNC` ```sql -TIMESTAMP_TRUNC(timestamp_expression, date_time_part[, time_zone]) +TIMESTAMP_TRUNC(timestamp_expression, granularity[, time_zone]) ``` **Description** -Truncates a timestamp to the granularity of `date_time_part`. -The timestamp is always rounded to the beginning of `date_time_part`, -which can be one of the following: - -+ `NANOSECOND`: If used, nothing is truncated from the value. -+ `MICROSECOND`: The nearest lessor or equal microsecond. -+ `MILLISECOND`: The nearest lessor or equal millisecond. -+ `SECOND`: The nearest lessor or equal second. -+ `MINUTE`: The nearest lessor or equal minute. -+ `HOUR`: The nearest lessor or equal hour. -+ `DAY`: The day in the Gregorian calendar year that contains the - `TIMESTAMP` value. -+ `WEEK`: The first day of the week in the week that contains the - `TIMESTAMP` value. Weeks begin on Sundays. `WEEK` is equivalent to - `WEEK(SUNDAY)`. -+ `WEEK(WEEKDAY)`: The first day of the week in the week that contains the - `TIMESTAMP` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the - following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, - or `SATURDAY`. -+ `ISOWEEK`: The first day of the [ISO 8601 week][ISO-8601-week] in the - ISO week that contains the `TIMESTAMP` value. The ISO week begins on - Monday. The first ISO week of each ISO year contains the first Thursday of the - corresponding Gregorian calendar year. -+ `MONTH`: The first day of the month in the month that contains the - `TIMESTAMP` value. -+ `QUARTER`: The first day of the quarter in the quarter that contains the - `TIMESTAMP` value. -+ `YEAR`: The first day of the year in the year that contains the - `TIMESTAMP` value. -+ `ISOYEAR`: The first day of the [ISO 8601][ISO-8601] week-numbering year - in the ISO year that contains the `TIMESTAMP` value. The ISO year is the - Monday of the first week whose Thursday belongs to the corresponding - Gregorian calendar year. +Truncates a `TIMESTAMP` value at a particular time granularity. The `TIMESTAMP` +value is always rounded to the beginning of `granularity`. + +**Definitions** + ++ `timestamp_expression`: The `TIMESTAMP` value to truncate. ++ `granularity`: The datetime part that represents the granularity. If + you passed in a `TIMESTAMP` value for the first argument, `granularity` can + be: + + + `NANOSECOND`: If used, nothing is truncated from the value. + + + `MICROSECOND`: The nearest lesser than or equal microsecond. + + + `MILLISECOND`: The nearest lesser than or equal millisecond. + + + `SECOND`: The nearest lesser than or equal second. + + + `MINUTE`: The nearest lesser than or equal minute. + + + `HOUR`: The nearest lesser than or equal hour. + + + `DAY`: The day in the Gregorian calendar year that contains the + `TIMESTAMP` value. + + + `WEEK`: The first day in the week that contains the + `TIMESTAMP` value. Weeks begin on Sundays. `WEEK` is equivalent to + `WEEK(SUNDAY)`. + + + `WEEK(WEEKDAY)`: The first day in the week that contains the + `TIMESTAMP` value. Weeks begin on `WEEKDAY`. `WEEKDAY` must be one of the + following: `SUNDAY`, `MONDAY`, `TUESDAY`, `WEDNESDAY`, `THURSDAY`, `FRIDAY`, + or `SATURDAY`. + + + `ISOWEEK`: The first day in the [ISO 8601 week][ISO-8601-week] that contains + the `TIMESTAMP` value. The ISO week begins on + Monday. The first ISO week of each ISO year contains the first Thursday of the + corresponding Gregorian calendar year. + + + `MONTH`: The first day in the month that contains the + `TIMESTAMP` value. + + + `QUARTER`: The first day in the quarter that contains the + `TIMESTAMP` value. + + + `YEAR`: The first day in the year that contains the + `TIMESTAMP` value. + + + `ISOYEAR`: The first day in the [ISO 8601][ISO-8601] week-numbering year + that contains the `TIMESTAMP` value. The ISO year is the + Monday of the first week where Thursday belongs to the corresponding + Gregorian calendar year. @@ -1065,23 +1143,20 @@ which can be one of the following: -`TIMESTAMP_TRUNC` function supports an optional `time_zone` parameter. This -parameter applies to the following `date_time_part`: ++ `time_zone`: Use this parameter if you want to use a time zone other than + the default time zone, which is implementation defined, as part of the + truncate operation. This can be: -+ `MINUTE` -+ `HOUR` -+ `DAY` -+ `WEEK` -+ `WEEK()` -+ `ISOWEEK` -+ `MONTH` -+ `QUARTER` -+ `YEAR` -+ `ISOYEAR` - -Use this parameter if you want to use a time zone other than the -default time zone, which is implementation defined, as part of the -truncate operation. + + `MINUTE` + + `HOUR` + + `DAY` + + `WEEK` + + `WEEK()` + + `ISOWEEK` + + `MONTH` + + `QUARTER` + + `YEAR` + + `ISOYEAR` When truncating a timestamp to `MINUTE` or`HOUR` parts, `TIMESTAMP_TRUNC` determines the civil time of the diff --git a/docs/user-defined-aggregates.md b/docs/user-defined-aggregates.md new file mode 100644 index 000000000..5ee4b4fe8 --- /dev/null +++ b/docs/user-defined-aggregates.md @@ -0,0 +1,164 @@ + + + + +# User-defined aggregate functions + + +ZetaSQL supports user-defined aggregate functions (UDAs). + +An SQL UDA is a user-defined function that +performs a calculation on a group of rows at a time and returns the result of +that calculation as a single value. + +The arguments represent a column of the input group such that each row in the +group has a value for that column. + +## Create a SQL UDA + +You can create a UDA using the following syntax: + +```sql +CREATE [ { TEMPORARY | TEMP } ] AGGREGATE FUNCTION + function_name ( [ function_parameter [, ...] ] ) + [ RETURNS data_type ] + AS ( function_body ) + +function_parameter: + parameter_name + { data_type | ANY TYPE } + [ DEFAULT default_value ] + [ NOT AGGREGATE ] +``` + +This syntax consists of the following components: + ++ `CREATE ... FUNCTION`: Creates a new function. A function + can have zero or more function parameters. + + + `TEMPORARY` or `TEMP`: Indicates that the function is temporary; that is, + it exists for the lifetime of the session. A temporary function can have + the same name as a built-in function. If this happens, the + temporary function hides the built-in function for the duration of the + temporary function's lifetime. ++ `function_name`: The name of the function. ++ `function_parameter`: A parameter for the function. + + + `parameter_name`: The name of the function parameter. + + + `data_type`: A ZetaSQL [data type][data-types]. + + + + `ANY TYPE`: The function will accept an argument of any type for this + function parameter. If more than one parameter includes `ANY TYPE`, + a relationship is not enforced between these parameters when the function + is defined. However, if the type of argument passed into the function at + call time is incompatible with the function definition, this will + result in an error. + + `ANY TYPE` is a [_templated function parameter_][templated-parameters]. + + + + + `DEFAULT default_value`: If an argument is not provided for a function + parameter, `default_value` is used. `default_value` must be a literal + or `NULL` value. All function parameters following this one + must also have default values. + + + + + `NOT AGGREGATE`: Specifies that a function parameter is not an + aggregate. A non-aggregate function parameter can appear anywhere in the + function definition. You can learn more about UDAF parameters + [here][aggregate-udf-parameters]. + ++ `RETURNS data_type`: Optional clause that specifies the data type + that the function returns. ZetaSQL infers the result type + of the function from the SQL function body when the `RETURN` clause is + omitted. ++ `function_body`: The SQL expression that defines the function body. + +You can create a public or privately-scoped +UDA in a module. To learn more, +see [Modules][modules]. + +[quoted-literals]: https://github.com/google/zetasql/blob/master/docs/lexical.md#quoted_literals + +[modules]: https://github.com/google/zetasql/blob/master/docs/modules.md + +## Call a SQL UDA + +You can call a SQL UDA in the same way that you call a built-in +aggregate function. For details, see [Function calls][function-calls]. + +## UDA function parameters + + +A user-defined aggregate (UDA) function can include aggregate or non-aggregate +function parameters. Like other [aggregate functions][aggregate-fns-link], +UDA functions normally aggregate function parameters across all rows in a +[group][group-by-link]. However, you can specify a function parameter as +non-aggregate with the `NOT AGGREGATE` keyword. + +A non-aggregate function parameter is a scalar function parameter with a +constant value for all rows in a group. Valid non-aggregate function parameters +include literals, constants, query parameters, and any references to the +function parameters of a user-defined function. Inside the UDA definition, +aggregate function parameters can only appear as function arguments to aggregate +function calls. References to non-aggregate function parameters can appear +anywhere in the UDA definition. + +## SQL UDA examples + +The following example shows a SQL UDA that includes a non-aggregate +function parameter. Inside the function definition, the aggregate `SUM` method +takes the aggregate function parameter `dividend`, while the non-aggregate +division operator ( `/` ) takes the non-aggregate function parameter `divisor`. + +```sql +CREATE TEMP AGGREGATE FUNCTION ScaledSum( + dividend DOUBLE, + divisor DOUBLE NOT AGGREGATE) +RETURNS DOUBLE +AS ( + SUM(dividend) / divisor +); + +SELECT ScaledSum(col1, 2) AS scaled_sum +FROM ( + SELECT 1 AS col1 UNION ALL + SELECT 3 AS col1 UNION ALL + SELECT 5 AS col1 +); + +/*------------* + | scaled_sum | + +------------+ + | 4.5 | + *------------*/ +``` + +## Templated function parameters + +A templated function parameter can match more than one argument type at +function call time. If a function signature includes a +templated function parameter, ZetaSQL allows function calls +to pass to the function any argument type as long as the function body is +valid for that argument type. + + + +[templated-parameters]: #templated_function_parameters + +[data-types]: https://github.com/google/zetasql/blob/master/docs/data-types.md + +[function-calls]: https://github.com/google/zetasql/blob/master/docs/functions-reference.md + +[aggregate-udf-parameters]: #aggregate_udf_parameters + +[group-by-link]: https://github.com/google/zetasql/blob/master/docs/query-syntax.md#group_by_clause + +[aggregate-fns-link]: https://github.com/google/zetasql/blob/master/docs/aggregate-function-calls.md + + + diff --git a/examples/bazel/.bazelversion b/examples/bazel/.bazelversion index 6abaeb2f9..f22d756da 100644 --- a/examples/bazel/.bazelversion +++ b/examples/bazel/.bazelversion @@ -1 +1 @@ -6.2.0 +6.5.0 diff --git a/execute_query.md b/execute_query.md new file mode 100644 index 000000000..c24af3810 --- /dev/null +++ b/execute_query.md @@ -0,0 +1,108 @@ +## Execute Query + +`execute_query` is a tool to parse, analyze and run SQL queries using the +reference implementation. + +You can run a query by supplying it directly on the command-line: + +```sh +execute_query "select 1 + 1;" +``` + +The tool can be run as a local web server that presents a UI where you can enter +a query and execute it interactively. The following command will run a local web +server and show a URL that you can open in your browser. You can also provide an +optional `--port` flag (the default port is 8080). + +```sh +execute_query --web +``` + +[!NOTE] +> Note that this tool is intended primarily as a way to explore the language and +> debug its implementation. It executes queries using the internal reference +> implementation which is intended primarily for testing. It is not performant +> or scalable, and there is no query optimizer. + +### Modes + +The default mode executes the query and shows the result. Other modes +show the intermediate stages like parse trees and analzyer output. +For example, this shows the +parse tree, the resolved AST, and the results of executing the query. + +```sh +execute_query --mode=parse,analyze,execute 'SELECT 1+2, CONCAT("hello", " world");' +``` + +The web UI (run with the `--web` flag) has checkboxes for selecting these modes. + +The modes are: + +* `execute`: Executes the query and shows results in a table. In the + command-line mode, you can also see the results in JSON or textproto format + using the `--output_mode` flag. +* `analyze`: Shows the resolved AST, as documented in [ZetaSQL Resolved + AST](https://github.com/google/zetasql/blob/master/docs/resolved_ast.md) +* `parse`: Shows the parse tree as defined in + [ast_node.h](https://github.com/google/zetasql/blob/master/zetasql/parser/ast_node.h) +* `explain`: Shows the evaluator query plan (for execution in the reference + implementation) +* `unparse`: Shows the result of converting the parse tree back to SQL +* `unanalyze`: Shows the result of converting the resolved AST back to SQL + +### Catalogs + +The tool also includes some built-in catalogs that provide some pre-defined +tables with sample data that you can use for queries. You can specify the +catalog to use with the `--catalog` flag. In the web UI, the +catalog can be selected from a dropdown list. + +The following catalogs are supported: + +* `none`: An empty catalog with no tables. +* `sample`: The sample catalog defined in + [sample_catalog.cc](https://github.com/google/zetasql/blob/master/zetasql/testdata/sample_catalog.cc), + which is used for most [analyzer tests](https://github.com/google/zetasql/tree/master/zetasql/analyzer/testdata). + These tables do not have any data. +* `tpch`: A catalog with the standard + [tables](https://github.com/google/zetasql/tree/master/zetasql/examples/tpch/describe.txt) + from the TPC-H benchmark, with a 1MB dataset. + +For example, the `sample` catalog defines the tables `KeyValue` and +`MultipleColumns`, and the `tpch` catalog defines the tables `Orders`, +`LineItem` and `Customer`, among others. You can see the schema of these tables +by executing `DESCRIBE `. + +In `parse` mode, the catalog isn't used, so any statement can be parsed +regardless of the catalog. + +For `analyze` and `execute` modes, the catalog is used to resolve table schemas. + +When executing queries, some catalogs provided have data attached to the +tables. Queries against those tables will be executable. + +### Executable statements + +Execution is mostly limited to queries. They can be written in standard syntax +using `SELECT` or in pipe syntax using `FROM`. + +Other executable statements: + +* `DESCRIBE ;` shows table schemas or function signatures, looking + up names from the selected catalog. +* `SELECT ;` can be used to evaluate expressions. In the + command-line mode, you can also specify `--sql_mode=expression` to interpret + the input as expressions. + +DDL statements that update the catalog: + +* `CREATE FUNCTION` creates a SQL UDF, or defines signatures for non-SQL UDFs. + These functions support analysis but not execution. +* `CREATE TABLE FUNCTION `creates ia SQL TVF, or defines a signature for non-SQL + a TVF. +* `CREATE TABLE` defines a table. It will have zero rows when queried. + +SQL functions and TVFs will be executable. Non-SQL functions and TVFs don't +have an implementation, so queries using them can be analyzed but can't be +executed. diff --git a/java/com/google/zetasql/BUILD b/java/com/google/zetasql/BUILD index b0aa41c81..e1b9e1bb7 100644 --- a/java/com/google/zetasql/BUILD +++ b/java/com/google/zetasql/BUILD @@ -63,6 +63,7 @@ BUILTIN_PROTO_DEPS = [ "//zetasql/public/functions:normalize_mode_java_proto", "//zetasql/public/functions:range_sessionize_mode_java_proto", "//zetasql/public/functions:rounding_mode_java_proto", + "//zetasql/public/functions:unsupported_fields_java_proto", "//zetasql/public:function_java_proto", "//zetasql/public:type_proto_java_proto", "//zetasql/public:value_java_proto", diff --git a/java/com/google/zetasql/BuiltinDescriptorPool.java b/java/com/google/zetasql/BuiltinDescriptorPool.java index bbf1d5802..a16ff6a40 100644 --- a/java/com/google/zetasql/BuiltinDescriptorPool.java +++ b/java/com/google/zetasql/BuiltinDescriptorPool.java @@ -35,6 +35,7 @@ import com.google.zetasql.functions.ZetaSQLNormalizeMode.NormalizeMode; import com.google.zetasql.functions.ZetaSQLRangeSessionizeMode.RangeSessionizeEnums.RangeSessionizeMode; import com.google.zetasql.functions.ZetaSQLRoundingMode.RoundingMode; +import com.google.zetasql.functions.ZetaSQLUnsupportedFields.UnsupportedFields; import com.google.type.Date; import com.google.type.LatLng; import com.google.type.TimeOfDay; @@ -50,6 +51,7 @@ final class BuiltinDescriptorPool { .importFileDescriptor(DateTimestampPart.getDescriptor().getFile()) .importFileDescriptor(NormalizeMode.getDescriptor().getFile()) .importFileDescriptor(RoundingMode.getDescriptor().getFile()) + .importFileDescriptor(UnsupportedFields.getDescriptor().getFile()) .importFileDescriptor(ArrayFindMode.getDescriptor().getFile()) .importFileDescriptor(Timestamp.getDescriptor().getFile()) .importFileDescriptor(Date.getDescriptor().getFile()) diff --git a/java/com/google/zetasql/TVFSignature.java b/java/com/google/zetasql/TVFSignature.java index 14ba537ac..947b8c34d 100644 --- a/java/com/google/zetasql/TVFSignature.java +++ b/java/com/google/zetasql/TVFSignature.java @@ -496,12 +496,18 @@ private static Category findCategory(@Nullable Value value, Type type) { if (type.getKind() == TypeKind.TYPE_UNKNOWN && (value == null || value.isNull())) { return Category.UNTYPED_NULL; } - if (type.isArray()) { - if (value == null || value.isNull()) { + if (type.isArray() && value != null) { + if (value.isNull()) { return Category.UNTYPED_EMPTY_ARRAY; } return Category.TYPED_LITERAL; } + // When value is null (Java null, NOT value with isNull=true), + // ValueWithType is used to carry the type information. + // This is used in cases like: TVFArgumentType. + if (!type.getKind().equals(TypeKind.TYPE_UNKNOWN) && value == null) { + return Category.TYPED_EXPRESSION; + } return Category.UNTYPED_NULL; } diff --git a/java/com/google/zetasql/resolvedast/DebugStrings.java b/java/com/google/zetasql/resolvedast/DebugStrings.java index fcfee9214..ed56ce115 100644 --- a/java/com/google/zetasql/resolvedast/DebugStrings.java +++ b/java/com/google/zetasql/resolvedast/DebugStrings.java @@ -54,6 +54,7 @@ import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedMakeProtoField; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedOption; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedOutputColumn; +import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedStaticDescribeScan; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedSystemVariable; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWindowFrame; import com.google.zetasql.resolvedast.ResolvedNodes.ResolvedWindowFrameExpr; @@ -468,6 +469,20 @@ static void collectDebugStringFields(ResolvedSystemVariable node, List fields) { + if (!node.getDescribeText().isEmpty()) { + fields.add(new DebugStringField("describe_text", node.getDescribeText())); + } + if (node.getInputScan() != null) { + fields.add(new DebugStringField("input_scan", node.getInputScan())); + } + } + + static String getNameForDebugString(ResolvedStaticDescribeScan node) { + return node.nodeKindString(); + } + static String getNameForDebugString(ResolvedComputedColumn node) { String name = node.getColumn().shortDebugString(); return node.getExpr().getNameForDebugStringWithNameFormat(name); diff --git a/zetasql/analyzer/BUILD b/zetasql/analyzer/BUILD index 590f1be1c..fd0a33e61 100644 --- a/zetasql/analyzer/BUILD +++ b/zetasql/analyzer/BUILD @@ -68,6 +68,7 @@ cc_library( "//zetasql/public:function_cc_proto", "//zetasql/public:id_string", "//zetasql/public:language_options", + "//zetasql/public:options_cc_proto", "//zetasql/public:signature_match_result", "//zetasql/public:strings", "//zetasql/public:type", @@ -200,6 +201,7 @@ cc_library( "//zetasql/base:varsetter", "//zetasql/common:errors", "//zetasql/common:internal_analyzer_options", + "//zetasql/common:internal_analyzer_output_properties", "//zetasql/common:status_payload_utils", "//zetasql/common:string_util", "//zetasql/common:thread_stack", @@ -355,6 +357,8 @@ cc_library( "//zetasql/analyzer/rewriters:map_function_rewriter", "//zetasql/analyzer/rewriters:multiway_unnest_rewriter", "//zetasql/analyzer/rewriters:nulliferror_function_rewriter", + "//zetasql/analyzer/rewriters:order_by_and_limit_in_aggregate_rewriter", + "//zetasql/analyzer/rewriters:pipe_assert_rewriter", "//zetasql/analyzer/rewriters:pivot_rewriter", "//zetasql/analyzer/rewriters:registration", "//zetasql/analyzer/rewriters:sql_function_inliner", @@ -671,7 +675,6 @@ cc_library( "//zetasql/resolved_ast:validator", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:btree", - "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", @@ -742,6 +745,7 @@ cc_library( "//zetasql/base:ret_check", "//zetasql/base:status", "//zetasql/base/testing:status_matchers", + "//zetasql/common:internal_analyzer_output_properties", "//zetasql/common:status_payload_utils", "//zetasql/common:unicode_utils", "//zetasql/parser", diff --git a/zetasql/analyzer/all_rewriters.cc b/zetasql/analyzer/all_rewriters.cc index 34d42f7db..ac3407081 100644 --- a/zetasql/analyzer/all_rewriters.cc +++ b/zetasql/analyzer/all_rewriters.cc @@ -29,6 +29,8 @@ #include "zetasql/analyzer/rewriters/map_function_rewriter.h" #include "zetasql/analyzer/rewriters/multiway_unnest_rewriter.h" #include "zetasql/analyzer/rewriters/nulliferror_function_rewriter.h" +#include "zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.h" +#include "zetasql/analyzer/rewriters/pipe_assert_rewriter.h" #include "zetasql/analyzer/rewriters/pivot_rewriter.h" #include "zetasql/analyzer/rewriters/registration.h" #include "zetasql/analyzer/rewriters/sql_function_inliner.h" @@ -90,6 +92,10 @@ void RegisterBuiltinRewriters() { GetInsertDmlValuesRewriter()); r.Register(ResolvedASTRewrite::REWRITE_MULTIWAY_UNNEST, GetMultiwayUnnestRewriter()); + r.Register(ResolvedASTRewrite::REWRITE_PIPE_ASSERT, + GetPipeAssertRewriter()); + r.Register(ResolvedASTRewrite::REWRITE_ORDER_BY_AND_LIMIT_IN_AGGREGATE, + GetOrderByAndLimitInAggregateRewriter()); // This rewriter should typically be the last in the rewrite sequence // because it cleans up after several other rewriters add ResolvedWithExprs. diff --git a/zetasql/analyzer/analyzer_test.cc b/zetasql/analyzer/analyzer_test.cc index cdc052f05..d28124464 100644 --- a/zetasql/analyzer/analyzer_test.cc +++ b/zetasql/analyzer/analyzer_test.cc @@ -2171,8 +2171,11 @@ TEST(AnalyzerTest, AnalyzeStatementsOfScript) { SampleCatalog catalog; std::string script = "SELECT * FROM KeyValue;\nSELECT garbage;"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK(ParseScript(script, ParserOptions(), ERROR_MESSAGE_ONE_LINE, - /*keep_error_location_payload=*/false, &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(script, ParserOptions(), + {.mode = ERROR_MESSAGE_ONE_LINE, + .attach_error_location_payload = false, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); ASSERT_EQ(parser_output->script()->statement_list().size(), 2); AnalyzerOptions analyzer_options; diff --git a/zetasql/analyzer/analyzer_test_options.h b/zetasql/analyzer/analyzer_test_options.h index 14685622b..c6e414c4c 100644 --- a/zetasql/analyzer/analyzer_test_options.h +++ b/zetasql/analyzer/analyzer_test_options.h @@ -158,9 +158,11 @@ class AnalyzerTestCase; // - Multiple statements may use the same prepare_database to add multiple // objects to the same catalog. However, such statements must be // sequentially adjacent, and only the first may be contain -// a 'use_catalog' option. +// a 'use_database' option. // - The following statements are currently supported: // CREATE [AGGREGATE] FUNCTION +// CREATE TABLE FUNCTION +// CREATE TABLE // // error_message_mode // - "with_payload", "one_line", or "multi_line_with_caret". diff --git a/zetasql/analyzer/expr_matching_helpers.cc b/zetasql/analyzer/expr_matching_helpers.cc index 2acb0de54..6100806bd 100644 --- a/zetasql/analyzer/expr_matching_helpers.cc +++ b/zetasql/analyzer/expr_matching_helpers.cc @@ -440,25 +440,6 @@ static absl::StatusOr GetLongestProtoOrStructNamePathForPrefixMatching( return found; } -// Returns true if the `valid_field_map` contains an entry of the `column`, and -// a prefix of `name_path` exists in the value list of `column`. -static bool FieldInfoMapContainsColumnAndNamePathPrefix( - const ValidFieldInfoMap& valid_field_map, const ResolvedColumn& column, - const ValidNamePath& name_path) { - const ValidNamePathList* valid_name_path_list; - bool has_name_path_list = - valid_field_map.LookupNamePathList(column, &valid_name_path_list); - if (!has_name_path_list) { - return false; - } - - ResolvedColumn unused_target_column; - int unused_prefix_length; - return ValidFieldInfoMap::FindLongestMatchingPathIfAny( - *valid_name_path_list, name_path.name_path(), &unused_target_column, - &unused_prefix_length); -} - absl::StatusOr AllPathsInExprHaveExpectedPrefixes( const ResolvedExpr& expr, const ValidFieldInfoMap& expected_prefixes, IdStringPool* id_string_pool) { @@ -507,8 +488,8 @@ absl::StatusOr AllPathsInExprHaveExpectedPrefixes( continue; } - if (!FieldInfoMapContainsColumnAndNamePathPrefix( - expected_prefixes, column_ref->column(), name_path)) { + if (!expected_prefixes.ContainsColumnAndNamePathPrefix(column_ref->column(), + name_path)) { return false; } } diff --git a/zetasql/analyzer/expr_resolver_helper.cc b/zetasql/analyzer/expr_resolver_helper.cc index 93c3654dd..03c0e4f3a 100644 --- a/zetasql/analyzer/expr_resolver_helper.cc +++ b/zetasql/analyzer/expr_resolver_helper.cc @@ -317,7 +317,8 @@ ExprResolutionInfo::ExprResolutionInfo(ExprResolutionInfo* parent, ARG_UPDATE(top_level_ast_expr), column_alias(!options.column_alias.empty() ? options.column_alias : parent->column_alias) -{ + , + enclosing_aggregate_function(parent->enclosing_aggregate_function) { // This constructor can only be used to switch the name scope to the parent's // aggregate or analytic scope, not to introduce a new scope, // unless allow_new_scopes is set. @@ -346,7 +347,8 @@ ExprResolutionInfo::ExprResolutionInfo(ExprResolutionInfo* parent) use_post_grouping_columns(parent->use_post_grouping_columns), top_level_ast_expr(parent->top_level_ast_expr), column_alias(parent->column_alias) -{} + , + enclosing_aggregate_function(parent->enclosing_aggregate_function) {} ExprResolutionInfo::ExprResolutionInfo( const NameScope* name_scope_in, const NameScope* aggregate_name_scope_in, @@ -445,6 +447,10 @@ std::string ExprResolutionInfo::DebugString() const { absl::StrAppend(&debugstring, "\nclause_name: ", clause_name); absl::StrAppend(&debugstring, "\nuse_post_grouping_columns: ", use_post_grouping_columns); + absl::StrAppend(&debugstring, "\enclosing_aggregate_function:\n", + (enclosing_aggregate_function != nullptr + ? enclosing_aggregate_function->DebugString() + : "NULL")); absl::StrAppend( &debugstring, "\nQueryResolutionInfo:\n", (query_resolution_info != nullptr ? query_resolution_info->DebugString() diff --git a/zetasql/analyzer/expr_resolver_helper.h b/zetasql/analyzer/expr_resolver_helper.h index 3d84d4448..ed933956d 100644 --- a/zetasql/analyzer/expr_resolver_helper.h +++ b/zetasql/analyzer/expr_resolver_helper.h @@ -249,6 +249,8 @@ struct ExprResolutionInfo { // Currently used for initially resolving select list columns, and // resolving LIMIT with an empty NameScope, so never resolves against // post-grouping columns. + // Also used for expressions in pipe operators that could include + // window functions like pipe WHERE and EXTEND. ExprResolutionInfo(const NameScope* name_scope_in, QueryResolutionInfo* query_resolution_info_in, const ASTExpression* top_level_ast_expr_in = nullptr, @@ -335,6 +337,13 @@ struct ExprResolutionInfo { // Context around if we can flatten and if we're currently actively doing so. // See FlattenState for details. FlattenState flatten_state; + + // Used to track the enclosing aggregate function when resolving arguments to + // an aggregate function that uses multi-level aggregation. Only the immediate + // enclosing aggregate function is tracked; `MultiLevelAggregateInfo` is + // responsible for tracking the entire set of enclosing aggregate functions. + // See `MultiLevelAggregateInfo` for more details. + const ASTFunctionCall* enclosing_aggregate_function = nullptr; }; // Create a vector from another container. @@ -364,10 +373,12 @@ class ResolvedTVFArg { type_ = EXPR; } void SetScan(std::unique_ptr scan, - std::shared_ptr name_list) { + std::shared_ptr name_list, + bool is_pipe_input_table) { scan_ = std::move(scan); name_list_ = name_list; type_ = SCAN; + is_pipe_input_table_ = is_pipe_input_table; } void SetModel(std::unique_ptr model) { model_ = std::move(model); @@ -412,6 +423,7 @@ class ResolvedTVFArg { ZETASQL_RET_CHECK(IsScan()); return name_list_; } + bool IsPipeInputTable() const { return is_pipe_input_table_; } absl::StatusOr> MoveExpr() { ZETASQL_RET_CHECK(IsExpr()); @@ -443,12 +455,19 @@ class ResolvedTVFArg { CONNECTION, DESCRIPTOR } type_ = UNDEFINED; + std::unique_ptr expr_; std::unique_ptr scan_; std::unique_ptr model_; std::unique_ptr connection_; std::unique_ptr descriptor_; + + // If type is SCAN, the NameList of the table being scanned. std::shared_ptr name_list_; + + // Indicates whether this is a relation created as a pipe CALL input table. + // Can only be true if type_ is SCAN. + bool is_pipe_input_table_ = false; }; // Computes the default alias to use for an expression. diff --git a/zetasql/analyzer/function_resolver.cc b/zetasql/analyzer/function_resolver.cc index 575e82d54..fe85ace0e 100644 --- a/zetasql/analyzer/function_resolver.cc +++ b/zetasql/analyzer/function_resolver.cc @@ -223,7 +223,7 @@ const std::string& FunctionResolver::BinaryOperatorToFunctionName( absl::StatusOr FunctionResolver::SignatureMatches( const std::vector& arg_ast_nodes, - const std::vector& input_arguments, + absl::Span input_arguments, const FunctionSignature& signature, bool allow_argument_coercion, const NameScope* name_scope, std::unique_ptr* result_signature, @@ -850,7 +850,7 @@ absl::StatusOr FunctionResolver::FindMatchingSignature( const Function* function, const ASTNode* ast_location, const std::vector& arg_locations_in, - const std::vector& named_arguments, + absl::Span named_arguments, const NameScope* name_scope, std::vector* input_arguments, std::vector* arg_overrides, @@ -2118,43 +2118,30 @@ absl::Status FunctionResolver::ResolveGeneralFunctionCall( // see the concatenation operator function in the ResolvedAST, and only // see the canonical CONCAT/ARRAY_CONCAT function call. if (function->Name() == "$concat_op") { - const Function* concat_op_function; - ResolvedFunctionCallBase::ErrorMode concat_op_error_mode; std::vector function_name_path; - std::unique_ptr concat_op_result_signature; arg_reorder_index_mapping.clear(); if (result_signature->result_type().type()->IsArray()) { function_name_path.push_back("array_concat"); - ZETASQL_RETURN_IF_ERROR(resolver_->LookupFunctionFromCatalog( - ast_location, function_name_path, - Resolver::FunctionNotFoundHandleMode::kReturnError, - &concat_op_function, &concat_op_error_mode)); - ZETASQL_ASSIGN_OR_RETURN( - const FunctionSignature* matched_signature, - FindMatchingSignature(concat_op_function, ast_location, - arg_locations_in, named_arguments, - /*name_scope=*/nullptr, &input_argument_types, - /*arg_overrides=*/nullptr, - &arg_reorder_index_mapping, - /*mismatch_errors=*/nullptr)); - concat_op_result_signature.reset(matched_signature); } else { function_name_path.push_back("concat"); - ZETASQL_RETURN_IF_ERROR(resolver_->LookupFunctionFromCatalog( - ast_location, function_name_path, - Resolver::FunctionNotFoundHandleMode::kReturnError, - &concat_op_function, &concat_op_error_mode)); - ZETASQL_ASSIGN_OR_RETURN( - const FunctionSignature* matched_signature, - FindMatchingSignature(concat_op_function, ast_location, - arg_locations_in, named_arguments, - /*name_scope=*/nullptr, &input_argument_types, - /*arg_overrides=*/nullptr, - &arg_reorder_index_mapping, - /*mismatch_errors=*/nullptr)); - concat_op_result_signature.reset(matched_signature); } + const Function* concat_op_function; + ResolvedFunctionCallBase::ErrorMode concat_op_error_mode; + ZETASQL_RETURN_IF_ERROR(resolver_->LookupFunctionFromCatalog( + ast_location, function_name_path, + Resolver::FunctionNotFoundHandleMode::kReturnError, &concat_op_function, + &concat_op_error_mode)); + ZETASQL_ASSIGN_OR_RETURN( + const FunctionSignature* matched_signature, + FindMatchingSignature( + concat_op_function, ast_location, arg_locations_in, named_arguments, + /*name_scope=*/nullptr, &input_argument_types, + /*arg_overrides=*/nullptr, &arg_reorder_index_mapping, + /*mismatch_errors=*/nullptr)); + ZETASQL_RET_CHECK_NE(matched_signature, nullptr); + std::unique_ptr concat_op_result_signature( + matched_signature); *resolved_expr_out = MakeResolvedFunctionCall( concat_op_result_signature->result_type().type(), concat_op_function, *concat_op_result_signature, std::move(arguments), @@ -2405,7 +2392,7 @@ absl::Status FunctionResolver::ResolveTemplatedSQLFunctionCall( function, resolver->ResolveExprWithFunctionArguments( function.GetParseResumeLocation().input(), expression, - &function_arguments, expr_resolution_info.get(), &resolved_sql_body), + function_arguments, expr_resolution_info.get(), &resolved_sql_body), analyzer_options.error_message_options())); if (function.IsAggregate()) { diff --git a/zetasql/analyzer/function_resolver.h b/zetasql/analyzer/function_resolver.h index c2701e354..8c927cc1d 100644 --- a/zetasql/analyzer/function_resolver.h +++ b/zetasql/analyzer/function_resolver.h @@ -227,7 +227,7 @@ class FunctionResolver { // E.g., the ones in FunctionSignatureMatcher::GetConcreteArguments. absl::StatusOr SignatureMatches( const std::vector& arg_ast_nodes, - const std::vector& input_arguments, + absl::Span input_arguments, const FunctionSignature& signature, bool allow_argument_coercion, const NameScope* name_scope, std::unique_ptr* result_signature, @@ -400,7 +400,7 @@ class FunctionResolver { absl::StatusOr FindMatchingSignature( const Function* function, const ASTNode* ast_location, const std::vector& arg_locations, - const std::vector& named_arguments, + absl::Span named_arguments, const NameScope* name_scope, std::vector* input_arguments, std::vector* arg_overrides, diff --git a/zetasql/analyzer/function_signature_matcher.cc b/zetasql/analyzer/function_signature_matcher.cc index bad34f0ae..c54dd3efd 100644 --- a/zetasql/analyzer/function_signature_matcher.cc +++ b/zetasql/analyzer/function_signature_matcher.cc @@ -35,6 +35,7 @@ #include "zetasql/public/id_string.h" #include "zetasql/public/input_argument_type.h" #include "zetasql/public/language_options.h" +#include "zetasql/public/options.pb.h" #include "zetasql/public/proto_util.h" #include "zetasql/public/signature_match_result.h" #include "zetasql/public/strings.h" @@ -80,7 +81,7 @@ class FunctionSignatureMatcher { // any internal errors. // // * are the list of parser ASTNodes for each input_arguments. - // It's used to assist lambda arguments resolving. + // It's used to extract additional details for named arguments and lambdas. // * is called to resolve lambda arguments if any. // It can be set to nullptr if no lambda argument is expected. // * The resolved lambdas, if any, are put into if the @@ -665,22 +666,23 @@ bool SignatureArgumentCountMatches( return false; } - const int num_repeated = signature.NumRepeatedArguments(); - const int num_optional = signature.NumOptionalArguments(); + const int signature_num_repeated = signature.NumRepeatedArguments(); + const int signature_num_optional = signature.NumOptionalArguments(); // Initially qualify the signature based on the number of arguments, taking // into account optional and repeated arguments. Find x and y such that: - // input_arguments_size = sig.num_required + x*sig.num_repeated + y - // where 0 < y <= sig.num_optional. - if (num_repeated > 0) { + // input_arguments_size = signature_num_required + + // x*signature_num_repeated + y + // where 0 < y <= signature_num_optional. + if (signature_num_repeated > 0) { while (input_arguments_size > signature_num_required + - *repetitions * num_repeated + - num_optional) { + *repetitions * signature_num_repeated + + signature_num_optional) { ++(*repetitions); } } - if (num_repeated == 0 && + if (signature_num_repeated == 0 && input_arguments_size > signature.arguments().size()) { const int sig_arguments_size = static_cast(signature.arguments().size()); @@ -691,19 +693,35 @@ bool SignatureArgumentCountMatches( return false; } - const int opts = input_arguments_size - signature_num_required - - *repetitions * num_repeated; - if (opts < 0 || opts > num_optional) { + const int input_remainder_size = input_arguments_size - + signature_num_required - + *repetitions * signature_num_repeated; + if (input_remainder_size < 0) { + // We have calculated too many repetitions. Since the repetitions takes + // optionals into account, this means the number of repeated arguments + // provided isn't a multiple of the number of repeated arguments in the + // signature. + const int num_repeated_actual = + *repetitions * signature_num_repeated + input_remainder_size; + SET_MISMATCH_ERROR(absl::StrCat( + "Wrong number of repeated arguments provided. Expected a multiple of ", + signature_num_repeated, " but got ", num_repeated_actual, + " repeated argument", optional_s(num_repeated_actual))); + return false; + } + + if (input_remainder_size > signature_num_optional) { // We do not have enough optionals to match the arguments size, and // repeating the repeated block again would require too many arguments. - SET_MISMATCH_ERROR( - absl::StrCat(opts, " optional argument", optional_s(opts), - " provided while signature has at most ", num_optional, - " optional argument", optional_s(num_optional))); + SET_MISMATCH_ERROR(absl::StrCat( + input_remainder_size, " optional argument", + optional_s(input_remainder_size), + " provided while signature has at most ", signature_num_optional, + " optional argument", optional_s(signature_num_optional))); return false; } - *optionals = opts; + *optionals = input_remainder_size; return true; } @@ -778,11 +796,9 @@ bool FunctionSignatureMatcher:: zetasql_base::FindOrNull(*templated_argument_map, sig_arg_type.kind()); // FunctionSignature::IsValid() guarantees that a templated argument of // lambda must have been seen before the lambda argument. - ABSL_DCHECK_NE(param_typeset, nullptr); if (param_typeset == nullptr) { - SET_MISMATCH_ERROR_WITH_INDEX(absl::StrFormat( - "failed to infer type for %dth argument (%s) of lambda", - sig_arg_idx + 1, arg_names[sig_arg_idx].ToString())); + SET_MISMATCH_ERROR(absl::StrFormat("Failed to infer type %s", + sig_arg_type.DebugString())); return false; } // Untyped arguments get their types after all templated args are @@ -1514,9 +1530,10 @@ bool FunctionSignatureMatcher::DetermineResolvedTypesForTemplatedArguments( types::Int64ArrayType()); break; case SignatureArgumentKindTypeSet::TYPED_ARGUMENTS: { - const Type* common_supertype = - coercer_.GetCommonSuperType(type_set.typed_arguments()); - if (common_supertype == nullptr) { + const Type* common_supertype = nullptr; + absl::Status status = coercer_.GetCommonSuperType( + type_set.typed_arguments(), &common_supertype); + if (!status.ok() || common_supertype == nullptr) { SET_MISMATCH_ERROR(absl::Substitute( "Unable to find common supertype for templated argument $0\n" " Input types for $0: $1", @@ -1595,6 +1612,7 @@ absl::StatusOr FunctionSignatureMatcher::SignatureMatches( // message. return false; } + ZETASQL_RET_CHECK_GE(input_arguments.size(), arg_ast_nodes.size()); concrete_result_signature->reset(); diff --git a/zetasql/analyzer/function_signature_matcher.h b/zetasql/analyzer/function_signature_matcher.h index 297672307..a2964aea6 100644 --- a/zetasql/analyzer/function_signature_matcher.h +++ b/zetasql/analyzer/function_signature_matcher.h @@ -49,8 +49,8 @@ struct FunctionArgumentOverride { // Contains information about the resolution of a particular argument // in a funtion call for a particular signature. -// This is generally passed as a vector, this generally -// be some normalized list of ~1:1 with actual function call arguments, but +// This is generally passed as a vector. This will generally +// be some normalized list ~1:1 with actual function call arguments, but // may be reordered (in the case of named arguments) or extended in the // case of optional arguments. struct ArgIndexEntry { @@ -89,6 +89,8 @@ using ResolveLambdaCallback = std::function are the list of parser ASTNodes for each input_arguments. // It's used to assist lambda and sequence argument resolving. +// There may be more than when default +// argument values have been added. // is called to resolve lambda arguments if any. It // can be set to nullptr if no lambda argument is expected. // The resolved lambda arguments, if any, are put into if the diff --git a/zetasql/analyzer/lookup_catalog_column_callback_test.cc b/zetasql/analyzer/lookup_catalog_column_callback_test.cc index 054d74a9c..a4c0f1eda 100644 --- a/zetasql/analyzer/lookup_catalog_column_callback_test.cc +++ b/zetasql/analyzer/lookup_catalog_column_callback_test.cc @@ -30,6 +30,9 @@ namespace zetasql { namespace { + +using ::testing::HasSubstr; +using ::zetasql_base::testing::IsOk; using ::zetasql_base::testing::StatusIs; class LookupCatalogColumnCallbackTest : public ::testing::Test { @@ -40,11 +43,16 @@ class LookupCatalogColumnCallbackTest : public ::testing::Test { BuiltinFunctionOptions::AllReleasedFunctions()); } - absl::Status Analyze(absl::string_view sql) { + absl::Status AnalyzeExpression(absl::string_view sql) { return AnalyzeExpressionForAssignmentToType( sql, options_, &catalog_, &type_factory_, types::Int64Type(), &output_); } + absl::Status AnalyzeStatement(absl::string_view sql) { + return zetasql::AnalyzeStatement(sql, options_, &catalog_, &type_factory_, + &output_); + } + std::unique_ptr MakeAnnotation() { std::unique_ptr m = AnnotationMap::Create(types::Int64Type()); @@ -64,9 +72,13 @@ class LookupCatalogColumnCallbackTest : public ::testing::Test { }; TEST_F(LookupCatalogColumnCallbackTest, BaselineErrorWhenNoColumnDefined) { - EXPECT_THAT(Analyze("mycolumn + 1"), + EXPECT_THAT(AnalyzeExpression("mycolumn + 1"), StatusIs(absl::StatusCode::kInvalidArgument, - testing::HasSubstr("Unrecognized name: mycolumn"))); + HasSubstr("Unrecognized name: mycolumn"))); + + EXPECT_THAT(AnalyzeStatement("SELECT mycolumn + 1"), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Unrecognized name: mycolumn"))); } TEST_F(LookupCatalogColumnCallbackTest, @@ -75,9 +87,14 @@ TEST_F(LookupCatalogColumnCallbackTest, [](absl::string_view column) -> absl::StatusOr { return nullptr; }); - EXPECT_THAT(Analyze("mycolumn + 1"), + + EXPECT_THAT(AnalyzeExpression("mycolumn + 1"), StatusIs(absl::StatusCode::kInvalidArgument, - testing::HasSubstr("Unrecognized name: mycolumn"))); + HasSubstr("Unrecognized name: mycolumn"))); + + EXPECT_THAT(AnalyzeStatement("SELECT mycolumn + 1"), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Unrecognized name: mycolumn"))); } TEST_F(LookupCatalogColumnCallbackTest, @@ -86,9 +103,14 @@ TEST_F(LookupCatalogColumnCallbackTest, [](absl::string_view column) -> absl::StatusOr { return absl::NotFoundError("error column-not-found: mycolumn"); }); - EXPECT_THAT(Analyze("mycolumn + 1"), + + EXPECT_THAT(AnalyzeExpression("mycolumn + 1"), + StatusIs(absl::StatusCode::kNotFound, + HasSubstr("error column-not-found: mycolumn"))); + + EXPECT_THAT(AnalyzeStatement("SELECT mycolumn + 1"), StatusIs(absl::StatusCode::kNotFound, - testing::HasSubstr("error column-not-found: mycolumn"))); + HasSubstr("error column-not-found: mycolumn"))); } TEST_F(LookupCatalogColumnCallbackTest, SuccessfulLookupTest) { @@ -97,23 +119,50 @@ TEST_F(LookupCatalogColumnCallbackTest, SuccessfulLookupTest) { EXPECT_THAT(column, testing::StrCaseEq("mycolumn")); return &column_; }); - EXPECT_THAT(Analyze("MycOluMn + 1"), zetasql_base::testing::IsOk()); - const zetasql::ResolvedCatalogColumnRef& result = + EXPECT_THAT(AnalyzeExpression("MycOluMn + 1"), IsOk()); + + const ResolvedCatalogColumnRef& resolved_expression = *output_->resolved_expr() ->GetAs() ->argument_list(0) ->GetAs(); // Case of the column name will match the column returned by the callback. - EXPECT_EQ(result.column()->Name(), "mycolumn"); + EXPECT_EQ(resolved_expression.column()->Name(), "mycolumn"); // Types will match. - EXPECT_TRUE(result.column()->GetType()->Equals(types::Int64Type())) - << result.column()->GetType()->DebugString(); + EXPECT_TRUE( + resolved_expression.column()->GetType()->Equals(types::Int64Type())) + << resolved_expression.column()->GetType()->DebugString(); // Column annotation will be propagated. + EXPECT_TRUE(resolved_expression.column()->GetTypeAnnotationMap()->Equals( + *column_annotation_)) + << resolved_expression.column()->GetTypeAnnotationMap()->DebugString(); + + EXPECT_THAT(AnalyzeStatement("SELECT MycOluMn + 1"), IsOk()); + + const ResolvedCatalogColumnRef& resolved_statement = + *output_->resolved_statement() + ->GetAs() + ->query() + ->GetAs() + ->expr_list(0) + ->GetAs() + ->expr() + ->GetAs() + ->argument_list(0) + ->GetAs(); + + // Case of the column name will match the column returned by the callback. + EXPECT_EQ(resolved_statement.column()->Name(), "mycolumn"); + // Types will match. EXPECT_TRUE( - result.column()->GetTypeAnnotationMap()->Equals(*column_annotation_)) - << result.column()->GetTypeAnnotationMap()->DebugString(); + resolved_statement.column()->GetType()->Equals(types::Int64Type())) + << resolved_statement.column()->GetType()->DebugString(); + // Column annotation will be propagated. + EXPECT_TRUE(resolved_statement.column()->GetTypeAnnotationMap()->Equals( + *column_annotation_)) + << resolved_statement.column()->GetTypeAnnotationMap()->DebugString(); } } // namespace diff --git a/zetasql/analyzer/name_scope.cc b/zetasql/analyzer/name_scope.cc index b9a3176fb..41ccaaead 100644 --- a/zetasql/analyzer/name_scope.cc +++ b/zetasql/analyzer/name_scope.cc @@ -109,6 +109,20 @@ bool ValidFieldInfoMap::LookupNamePathList( return (*valid_name_path_list != nullptr); } +bool ValidFieldInfoMap::ContainsColumnAndNamePathPrefix( + const ResolvedColumn& column, const ValidNamePath& name_path) const { + const ValidNamePathList* valid_name_path_list; + if (!LookupNamePathList(column, &valid_name_path_list)) { + return false; + } + + ResolvedColumn unused_target_column; + int unused_prefix_length; + return FindLongestMatchingPathIfAny( + *valid_name_path_list, name_path.name_path(), &unused_target_column, + &unused_prefix_length); +} + void ValidFieldInfoMap::Clear() { column_to_valid_name_paths_map_.clear(); } @@ -1412,16 +1426,36 @@ absl::Status NameList::MergeFrom(const NameList& other, ZETASQL_RET_CHECK(!options.flatten_to_table); ZETASQL_RET_CHECK(other.is_value_table()); } - ZETASQL_RET_CHECK(!options.columns_to_replace || !options.excluded_field_names); + ZETASQL_RET_CHECK_LE((options.excluded_field_names ? 1 : 0) + + (options.columns_to_replace ? 1 : 0) + + (options.columns_to_rename ? 1 : 0), + 1); + if (options.columns_to_replace != nullptr) { + ZETASQL_RET_CHECK(!options.flatten_to_table); + ZETASQL_RET_CHECK(!options.rename_value_table_to_name); + } const IdStringSetCase* excluded_field_names = options.excluded_field_names; const MergeOptions::ColumnsToReplaceMap* columns_to_replace = options.columns_to_replace; + const MergeOptions::ColumnsToRenameMap* columns_to_rename = + options.columns_to_rename; // Copy the columns vector, with exclusions. // We're not using AddColumn because we're going to copy the NameScope // maps directly below. for (const NamedColumn& named_column : other.columns()) { + IdString new_name = named_column.name(); + bool renamed_column = false; + if (columns_to_rename != nullptr) { + const IdString* found_name = + zetasql_base::FindOrNull(*columns_to_rename, named_column.name()); + if (found_name != nullptr) { + new_name = *found_name; + renamed_column = true; + } + } + const ResolvedColumn* replacement_column = nullptr; if (columns_to_replace != nullptr) { replacement_column = @@ -1429,7 +1463,7 @@ absl::Status NameList::MergeFrom(const NameList& other, } if (replacement_column != nullptr) { - columns_.push_back(NamedColumn(named_column.name(), *replacement_column, + columns_.push_back(NamedColumn(new_name, *replacement_column, /*is_explicit=*/true)); } else if (excluded_field_names == nullptr || !zetasql_base::ContainsKey(*excluded_field_names, named_column.name())) { @@ -1461,7 +1495,6 @@ absl::Status NameList::MergeFrom(const NameList& other, true /* is_valid_to_access */, {} /* valid_field_info_map */}); - IdString new_name = named_column.name(); if (options.rename_value_table_to_name != nullptr) { new_name = *options.rename_value_table_to_name; } @@ -1471,6 +1504,10 @@ absl::Status NameList::MergeFrom(const NameList& other, columns_.emplace_back(new_name, named_column.column(), named_column.is_explicit(), new_excluded_field_names); + } else if (renamed_column) { + columns_.push_back(NamedColumn(new_name, named_column.column(), + /*is_explicit=*/true, + named_column.excluded_field_names())); } else { columns_.push_back(named_column); } @@ -1501,6 +1538,13 @@ absl::Status NameList::MergeFrom(const NameList& other, } } + if (columns_to_rename != nullptr) { + const IdString* found_name = zetasql_base::FindOrNull(*columns_to_rename, name); + if (found_name != nullptr) { + name = *found_name; + } + } + if (target.IsRangeVariable()) { // With `flatten_to_table`, range variables are dropped, except for // value table range variables, which will be converted to columns. diff --git a/zetasql/analyzer/name_scope.h b/zetasql/analyzer/name_scope.h index 97b69e80b..ad339f0f0 100644 --- a/zetasql/analyzer/name_scope.h +++ b/zetasql/analyzer/name_scope.h @@ -134,6 +134,12 @@ class ValidFieldInfoMap { bool LookupNamePathList(const ResolvedColumn& column, const ValidNamePathList** valid_name_path_list) const; + // Returns true if `column_to_valid_name_paths_map_` contains `column`, and + // some prefix of `name_path` exists in the valid name paths for `column`. + // The target column in `name_path` is ignored. + bool ContainsColumnAndNamePathPrefix(const ResolvedColumn& column, + const ValidNamePath& name_path) const; + void Clear(); // Find the entry in `name_path_list` whose name path is the @@ -952,6 +958,9 @@ class NameList { absl::Status AddAmbiguousColumn_Test(IdString name); // Options to customize behavior of NameList::MergeFrom. + // + // `excluded_field_names`, `columns_to_rename` and `columns_to_rename` are + // mutually exclusive. struct MergeOptions { // If non-NULL, names in this list will be excluded. // Range variables with matching names are also excluded. @@ -965,12 +974,24 @@ class NameList { // columns, pseudo-columns, range variables, ambiguous names, etc) // will be removed, and replaced by one new entry pointing at the column. // This also acts like `excluded_field_names` for other occurrences of - // replaced name. `excluded_field_names` cannot be set at the same time. + // replaced name. typedef absl::flat_hash_map ColumnsToReplaceMap; ColumnsToReplaceMap* columns_to_replace = nullptr; + // If non-NULL, names in this map will be renamed to the map entry's value. + // All matching names in the NameList will be renamed. + // A matching name in the scope will be renamed (possibly resulting + // in an ambiguous name if it collides with another name). + // All renames are applied simultaneously, so swaps will work. + // If this is present, `flatten_to_table` and `rename_value_table_to_name` + // are not allowed. + using ColumnsToRenameMap = + absl::flat_hash_map; + ColumnsToRenameMap* columns_to_rename = nullptr; + // If true, the copied names are converted to be just a flat table. // Range variables are dropped, and value tables are converted to // regular columns. diff --git a/zetasql/analyzer/query_resolver_helper.cc b/zetasql/analyzer/query_resolver_helper.cc index 09d00a415..66753c9e6 100644 --- a/zetasql/analyzer/query_resolver_helper.cc +++ b/zetasql/analyzer/query_resolver_helper.cc @@ -199,6 +199,72 @@ class DeferredResolutionFinder : public NonRecursiveParseTreeVisitor { } // namespace +absl::Status MultiLevelAggregateInfo::AddAggregateFunction( + const ASTFunctionCall* aggregate_function, + const ASTFunctionCall* enclosing_aggregate_function) { + ZETASQL_RET_CHECK(aggregate_function != nullptr); + // `enclosing_aggregate_function` can be nullptr if `aggregate_function` is + // a top-level aggregate function. + if (enclosing_aggregate_function != nullptr) { + ZETASQL_RET_CHECK( + directly_nested_aggregate_functions_map_[enclosing_aggregate_function] + .insert(aggregate_function) + .second); + } + // Only add grouping items for aggregate functions with GROUP BY modifiers. + if (aggregate_function->group_by() != nullptr) { + ZETASQL_RETURN_IF_ERROR(AddAllGroupingItems(aggregate_function)); + } + return absl::OkStatus(); +}; + +absl::Status MultiLevelAggregateInfo::AddAllGroupingItems( + const ASTFunctionCall* aggregate_function) { + ZETASQL_RETURN_IF_ERROR( + AddGroupingItemsFromAllEnclosingAggregateFunctions(aggregate_function)); + for (const ASTGroupingItem* grouping_item : + aggregate_function->group_by()->grouping_items()) { + ZETASQL_RET_CHECK(aggregate_function_argument_grouping_keys_map_[aggregate_function] + .insert({grouping_item, nullptr}) + .second); + } + return absl::OkStatus(); +} + +const ASTFunctionCall* MultiLevelAggregateInfo::GetEnclosingAggregateFunction( + const ASTFunctionCall* aggregate_function) const { + for (const auto& pair : directly_nested_aggregate_functions_map_) { + const ASTFunctionCall* enclosing_aggregate_function = pair.first; + const absl::flat_hash_set& + nested_aggregate_functions = pair.second; + if (nested_aggregate_functions.contains(aggregate_function)) { + return enclosing_aggregate_function; + } + } + return nullptr; +} + +absl::Status +MultiLevelAggregateInfo::AddGroupingItemsFromAllEnclosingAggregateFunctions( + const ASTFunctionCall* aggregate_function) { + const ASTFunctionCall* enclosing_aggregate_function = + GetEnclosingAggregateFunction(aggregate_function); + if (enclosing_aggregate_function == nullptr || + !aggregate_function_argument_grouping_keys_map_.contains( + enclosing_aggregate_function)) { + return absl::OkStatus(); + } + + for (const auto& pair : aggregate_function_argument_grouping_keys_map_.at( + enclosing_aggregate_function)) { + const ASTGroupingItem* grouping_item = pair.first; + ZETASQL_RET_CHECK(aggregate_function_argument_grouping_keys_map_[aggregate_function] + .insert({grouping_item, nullptr}) + .second); + } + return absl::OkStatus(); +} + void QueryGroupByAndAggregateInfo::Reset() { has_group_by = false; has_aggregation = false; @@ -592,6 +658,12 @@ absl::Status QueryResolutionInfo::GetAndRemoveSelectListColumnsWithoutAnalytic( ResolvedColumnList QueryResolutionInfo::GetResolvedColumnList() const { ResolvedColumnList resolved_column_list; + resolved_column_list.reserve( + select_column_state_list_->select_column_state_list().size() + + pipe_extra_select_items_.size()); + for (const PipeExtraSelectItem& item : pipe_extra_select_items_) { + resolved_column_list.push_back(item.column); + } for (const std::unique_ptr& select_column_state : select_column_state_list_->select_column_state_list()) { resolved_column_list.push_back(select_column_state->resolved_select_column); @@ -611,9 +683,23 @@ void QueryResolutionInfo::SetHasAggregation(bool value) { group_by_info_.has_aggregation = value; } +// TODO: Rename to Operator. +bool QueryResolutionInfo::IsPipeOp() const { + switch (select_form_) { + case SelectForm::kPipeSelect: + case SelectForm::kPipeExtend: + case SelectForm::kPipeAggregate: + case SelectForm::kPipeWindow: + return true; + default: + return false; + } +} + bool QueryResolutionInfo::SelectFormAllowsSelectStar() const { switch (select_form_) { case SelectForm::kClassic: + case SelectForm::kPipeSelect: return true; default: return false; @@ -623,6 +709,7 @@ bool QueryResolutionInfo::SelectFormAllowsSelectStar() const { bool QueryResolutionInfo::SelectFormAllowsAggregation() const { switch (select_form_) { case SelectForm::kClassic: + case SelectForm::kPipeAggregate: return true; default: return false; @@ -632,6 +719,9 @@ bool QueryResolutionInfo::SelectFormAllowsAggregation() const { bool QueryResolutionInfo::SelectFormAllowsAnalytic() const { switch (select_form_) { case SelectForm::kClassic: + case SelectForm::kPipeWindow: + case SelectForm::kPipeSelect: + case SelectForm::kPipeExtend: return true; default: return false; @@ -644,6 +734,14 @@ const char* QueryResolutionInfo::SelectFormClauseName() const { return "SELECT"; case SelectForm::kNoFrom: return "SELECT without FROM clause"; + case SelectForm::kPipeSelect: + return "pipe SELECT"; + case SelectForm::kPipeExtend: + return "pipe EXTEND"; + case SelectForm::kPipeAggregate: + return "pipe AGGREGATE"; + case SelectForm::kPipeWindow: + return "pipe WINDOW"; } } diff --git a/zetasql/analyzer/query_resolver_helper.h b/zetasql/analyzer/query_resolver_helper.h index 26b9a9197..7f51ef000 100644 --- a/zetasql/analyzer/query_resolver_helper.h +++ b/zetasql/analyzer/query_resolver_helper.h @@ -39,9 +39,13 @@ #include "zetasql/resolved_ast/resolved_ast_enums.pb.h" #include "zetasql/resolved_ast/resolved_column.h" #include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "zetasql/base/ret_check.h" +#include "zetasql/base/status_macros.h" namespace zetasql { @@ -72,6 +76,14 @@ enum class SelectForm { kClassic, // A no-FROM-clause SELECT query. kNoFrom, + // An ASTSelect representing a pipe SELECT operator. + kPipeSelect, + // An ASTSelect representing a pipe EXTEND operator. + kPipeExtend, + // An ASTSelect representing a pipe AGGREGATE operator. + kPipeAggregate, + // An ASTSelect representing a pipe WINDOW operator. + kPipeWindow, }; struct GroupingSetInfo { @@ -123,6 +135,13 @@ struct GroupingSetIds { }; // This stores a column to order by in the final ResolvedOrderByScan. +// It is used for +// * items from ORDER BY (in QueryResolutionInfo::order_by_item_info). +// * items from pipe AGGREGATE ... GROUP BY to order by +// (in QueryResolutionInfo::aggregate_order_by_item_info and +// QueryResolutionInfo::group_by_order_by_item_info). +// Since ordering items come from multiple sources, any needed attributes are +// passed via this object rather than the original ASTOrderBy. struct OrderByItemInfo { // Constructor for ordering by an ordinal (column number). OrderByItemInfo(const ASTNode* ast_location_in, @@ -179,6 +198,9 @@ struct OrderByItemInfo { // AddColumnsForOrderByExprs resolves those expressions to a specific // , which is then referenced in MakeResolvedOrderByScan. // + // For OrderByItemInfos added from the GROUP BY, the + // is filled in directly, without an , + // in ResolveGroupingItemExpression. std::unique_ptr order_expression; ResolvedColumn order_column; @@ -231,6 +253,122 @@ struct GroupByColumnState { } }; +// Stores (and mutates) information needed to resolve multi-level aggregate +// expressions. +// +// A Multi-level aggregate function (i.e. an aggregate function with one or more +// GROUP BY modifiers) is resolved in 2 passes. 2-pass resolution is required to +// determine if column references in scalar expression arguments are valid to +// access. For instance, consider this expression: +// +// SUM(k2 + v2 + ANY_VALUE(k1 + MIN(v1) GROUP BY k1) GROUP BY k2) +// +// 'k1' is valid to access within the ANY_VALUE function since it is a grouping +// key for the 'k1 + MIN(v1)' expression. 'k2' is valid to access within the +// SUM function since it is a grouping key for the 'k2 + v2' expression. +// But 'v2' is not valid to access within the SUM function since it is not a +// grouping key within the 'k2 + v2 + ANY_VALUE(...)' expression. +// +// The 1st pass resolves the arguments to the aggregate function. This pass also +// computes grouping key information and stores it in `MultiLevelAggregateInfo`. +// The 2nd pass uses the computed grouping keys to creates a namescope which +// marks non-grouping key columns as target access errors, and re-resolves the +// arguments against this new namescope. +class MultiLevelAggregateInfo { + public: + MultiLevelAggregateInfo() = default; + MultiLevelAggregateInfo(const MultiLevelAggregateInfo&) = delete; + MultiLevelAggregateInfo& operator=(const MultiLevelAggregateInfo&) = delete; + + // Add an aggregate function to `MultiLevelAggregateInfo`, along with its + // enclosing aggregate function (nullptr if no enclosing aggregate function). + absl::Status AddAggregateFunction( + const ASTFunctionCall* aggregate_function, + const ASTFunctionCall* enclosing_aggregate_function); + + private: + // Populate `aggregate_function_argument_grouping_keys_map_` with grouping + // items in the GROUP BY modifier of the `aggregate_function`, as well as all + // enclosing aggregate functions. + absl::Status AddAllGroupingItems(const ASTFunctionCall* aggregate_function); + + // Return the enclosing aggregate function of the given aggregate function, + // or nullptr if there is no enclosing aggregate function. + const ASTFunctionCall* GetEnclosingAggregateFunction( + const ASTFunctionCall* aggregate_function) const; + + // Populate `aggregate_function_argument_grouping_keys_map_` with grouping + // items from all enclosing aggregate functions. Since resolution order will + // have fully populated all the grouping items for the immediate enclosing + // aggregate function, we can just copy the grouping items from the enclosing + // aggregate function in the map. + absl::Status AddGroupingItemsFromAllEnclosingAggregateFunctions( + const ASTFunctionCall* aggregate_function); + + // Maps an aggregate function in a multi-level aggregate expression to + // aggregate functions nested within it. + // + // For example, given the expression: + // + // SUM(ANY_VALUE(MIN(value1) GROUP BY key3) + MAX(value2) GROUP BY key2) + // + // - SUM would be mapped to ANY_VALUE and MAX aggregate functions + // - ANY_VALUE would be mapped to MIN + // - MIN and MAX would not be present in the map + // TODO: The use `flat_hash_set` results in a non-deterministic + // resolution order when multiple nested aggregate functions are present at + // the same level in multi-level aggregate expr (e.g. SUM(MAX(...)+MIN(...))) + // This breaks multi-level aggregation analyzer tests and should be fixed. + absl::flat_hash_map> + directly_nested_aggregate_functions_map_; + + // Map an aggregate function in a multi-level aggregate expression to + // the grouping items that form the grouping key for **arguments to the** + // the aggregate function (not the grouping key for the aggregate function + // itself). Only maps aggregate functions that have a GROUP BY modifier. + // + // This information is used during the 2nd-pass resolution of an aggregate + // function with a GROUP BY modifier. + // + // For example, given this query: + // + // SELECT + // SUM(ANY_VALUE(MIN(value1) GROUP BY key3) + MAX(value2) GROUP BY key2) + // FROM Table + // GROUP BY key1 + // + // - SUM would be mapped to key1, key2 + // - ANY_VALUE would be mapped to key1, key2, key3 + // - MIN and MAX would not be present in the map. + // TODO: The use `flat_hash_map` results in a non-deterministic + // resolution order when multiple (and/or nested) GROUP BY modifiers are + // present in the multi-level aggregate expr. This breaks multi-level + // aggregation analyzer tests and should be fixed. + absl::flat_hash_map< + const ASTFunctionCall*, + absl::flat_hash_map>> + aggregate_function_argument_grouping_keys_map_; + + // Stores ResolvedComputedColumns for aggregate functions nested within an + // outer aggregate function. These ResolvedComputedColumns are used when + // finishing resolution of the outer aggregate function. + // + // For example, given the expression: + // + // SUM(ANY_VALUE(MIN(value1) GROUP BY key3) GROUP BY key2) + // + // MIN would have it's resolved expression (R1) stored in this map. When + // finishing resolution of ANY_VALUE, R1 will be moved out of this map and + // into the resolved expr for ANY_VALUE (R2), and R2 will be stored in this + // map. When finishing resolution of SUM, R2 will be moved out of this map + // and into the resolved expr for SUM (R3). + absl::flat_hash_map> + resolved_nested_aggregate_functions_map_; +}; + // QueryGroupByAndAggregateInfo is used (and mutated) to store info related // to grouping/distinct and aggregation analysis for a single SELECT query // block. @@ -286,6 +424,11 @@ struct QueryGroupByAndAggregateInfo { std::vector> grouping_output_columns; + // TODO: Consider moving this to `ExprResolutionInfo`. + // Stores (and mutates) information needed to resolve multi-level aggregate + // expressions. + MultiLevelAggregateInfo multi_level_aggregate_info; + // Stores information about STRUCT or PROTO fields that appear in the // GROUP BY. // @@ -328,6 +471,7 @@ struct SelectColumnState { std::unique_ptr resolved_expr_in) : ast_select_column(ast_select_column), ast_expr(ast_select_column->expression()), + ast_grouping_item_order(ast_select_column->grouping_item_order()), alias(alias_in), is_explicit(is_explicit_in), select_list_position(-1), @@ -359,6 +503,9 @@ struct SelectColumnState { // Points at the * if this came from SELECT *. const ASTExpression* ast_expr; + // Stores the ordering suffix applied for this select item. + const ASTGroupingItemOrder* ast_grouping_item_order; + // The alias provided by the user or computed for this column. const IdString alias; @@ -507,6 +654,17 @@ class SelectColumnStateList { column_alias_to_state_list_position_; }; +// This represents extra items to prepend on the output column list and +// NameList, used when resolving pipe operators. For pipe AGGREGATE, it's the +// GROUP BY columns. For pipe WINDOW, it's the passed-through input columns. +struct PipeExtraSelectItem { + PipeExtraSelectItem(IdString alias_in, ResolvedColumn column_in) + : alias(alias_in), column(column_in) {} + + const IdString alias; + const ResolvedColumn column; +}; + // QueryResolutionInfo is used (and mutated) to store info related to // the analysis of a single SELECT query block. It stores information // related to SELECT list entries, grouping and aggregation, and analytic @@ -710,6 +868,23 @@ class QueryResolutionInfo { return &order_by_item_info_; } + bool group_by_has_and_order_by() const { return group_by_has_and_order_by_; } + void set_group_by_has_and_order_by() { group_by_has_and_order_by_ = true; } + + const std::vector& aggregate_order_by_item_info() const { + return aggregate_order_by_item_info_; + } + std::vector* mutable_aggregate_order_by_item_info() { + return &aggregate_order_by_item_info_; + } + + const std::vector& group_by_order_by_item_info() const { + return group_by_order_by_item_info_; + } + std::vector* mutable_group_by_order_by_item_info() { + return &group_by_order_by_item_info_; + } + AnalyticFunctionResolver* analytic_resolver() { return analytic_resolver_.get(); } @@ -718,6 +893,17 @@ class QueryResolutionInfo { return select_column_state_list_.get(); } + MultiLevelAggregateInfo* multi_level_aggregate_info() { + return &group_by_info_.multi_level_aggregate_info; + } + + // This is the list of extra items to prepend on the output column list and + // NameList, used when resolving pipe operators. For pipe AGGREGATE, it's the + // GROUP BY columns. For pipe WINDOW, it's the passed-through input columns. + std::vector* pipe_extra_select_items() { + return &pipe_extra_select_items_; + } + std::vector>* select_list_columns_to_compute() { return &select_list_columns_to_compute_; @@ -809,6 +995,15 @@ class QueryResolutionInfo { const char* SelectFormClauseName() const; + bool IsPipeSelect() const { return select_form() == SelectForm::kPipeSelect; } + bool IsPipeExtend() const { return select_form() == SelectForm::kPipeExtend; } + bool IsPipeWindow() const { return select_form() == SelectForm::kPipeWindow; } + bool IsPipeExtendOrWindow() const { return IsPipeExtend() || IsPipeWindow(); } + bool IsPipeAggregate() const { + return select_form() == SelectForm::kPipeAggregate; + } + bool IsPipeOp() const; // True for any pipe operator. + // Tests for conditions that depend on SelectForm. bool SelectFormAllowsSelectStar() const; bool SelectFormAllowsAggregation() const; @@ -927,6 +1122,8 @@ class QueryResolutionInfo { QueryGroupByAndAggregateInfo group_by_info_; + std::vector pipe_extra_select_items_; + // Indicates if we're resolving a special form of ASTSelect, like // a no-FROM-clause query. SelectForm select_form_ = SelectForm::kClassic; @@ -944,8 +1141,20 @@ class QueryResolutionInfo { bool has_order_by_ = false; // List of items from ORDER BY. + // This has items from the standard ORDER BY clause or pipe ORDER BY. + // Orderings from other pipe clauses are in the vectors below. + // This vector and the vectors below cannot both be non-empty. std::vector order_by_item_info_; + // Indicates whether the AND ORDER BY modifier is present on GROUP BY. + bool group_by_has_and_order_by_ = false; + + // Ordering items added from the AGGREGATE list (in pipe AGGREGATE). + std::vector aggregate_order_by_item_info_; + + // Ordering items added from the GROUP BY (in pipe AGGREGATE). + std::vector group_by_order_by_item_info_; + // DML THEN RETURN information, where it also uses the select list. bool is_resolving_returning_clause_ = false; diff --git a/zetasql/analyzer/resolver.cc b/zetasql/analyzer/resolver.cc index d3b83a618..0b67f177b 100644 --- a/zetasql/analyzer/resolver.cc +++ b/zetasql/analyzer/resolver.cc @@ -319,14 +319,14 @@ absl::Status Resolver::ResolveStandaloneExprAndAssignToType( absl::Status Resolver::ResolveExprWithFunctionArguments( absl::string_view sql, const ASTExpression* ast_expr, - IdStringHashMapCase>* + const IdStringHashMapCase>& function_arguments, ExprResolutionInfo* expr_resolution_info, std::unique_ptr* output) { RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); Reset(sql); auto arg_info = std::make_unique(); - for (auto& [arg_name, resolved_arg] : *function_arguments) { + for (auto& [arg_name, resolved_arg] : function_arguments) { ZETASQL_RETURN_IF_ERROR( arg_info->AddScalarArg(arg_name, resolved_arg->argument_kind(), FunctionArgumentType(resolved_arg->type()))); diff --git a/zetasql/analyzer/resolver.h b/zetasql/analyzer/resolver.h index 4d6aa92ce..a16e4262c 100644 --- a/zetasql/analyzer/resolver.h +++ b/zetasql/analyzer/resolver.h @@ -89,6 +89,7 @@ #include "absl/types/optional.h" #include "absl/types/span.h" #include "absl/types/variant.h" +#include "zetasql/base/ret_check.h" namespace zetasql { @@ -127,6 +128,7 @@ class Resolver { enum class OrderBySimpleMode { kNormal, + kPipes, }; struct GeneratedColumnIndexAndResolvedId { @@ -174,7 +176,7 @@ class Resolver { // directly and deprecate this one. absl::Status ResolveExprWithFunctionArguments( absl::string_view sql, const ASTExpression* ast_expr, - IdStringHashMapCase>* + const IdStringHashMapCase>& function_arguments, ExprResolutionInfo* expr_resolution_info, std::unique_ptr* output); @@ -1138,6 +1140,11 @@ class Resolver { const ASTCreateModelStatement* ast_statement, std::unique_ptr* output); + // Resolve a CREATE CONNECTION statement. + absl::Status ResolveCreateConnectionStatement( + const ASTCreateConnectionStatement* ast_statement, + std::unique_ptr* output); + // Resolve a CREATE DATABASE statement. absl::Status ResolveCreateDatabaseStatement( const ASTCreateDatabaseStatement* ast_statement, @@ -1475,6 +1482,10 @@ class Resolver { const ASTRevokeStatement* ast_statement, std::unique_ptr* output); + absl::Status ResolveAlterConnectionStatement( + const ASTAlterConnectionStatement* ast_statement, + std::unique_ptr* output); + absl::Status ResolveAlterPrivilegeRestrictionStatement( const ASTAlterPrivilegeRestrictionStatement* ast_statement, std::unique_ptr* output); @@ -1660,6 +1671,11 @@ class Resolver { std::shared_ptr* output_name_list, const Type* inferred_type_for_query = nullptr); + absl::Status ResolveFromQuery( + const ASTFromQuery* from_query, const NameScope* scope, + std::unique_ptr* output, + std::shared_ptr* output_name_list); + // Resolves a WITH entry. // is true only when a WITH entry is actually recursive, as // opposed to merely belonging to a WITH clause with the RECURSIVE keyword. @@ -1703,6 +1719,12 @@ class Resolver { std::shared_ptr* output_name_list, const Type* inferred_type_for_query = nullptr); + absl::Status ResolveAliasedQueryExpression( + const ASTAliasedQueryExpression* aliased_query, const NameScope* scope, + std::unique_ptr* output, + std::shared_ptr* output_name_list, + const Type* inferred_type_for_query); + // If the query contains a WITH clause, resolves all WITH entries and returns // them. Otherwise, just returns an empty vector. absl::StatusOr>> @@ -2682,6 +2704,8 @@ class Resolver { // value nullptr. absl::Status ResolveGroupingItemExpression( const ASTExpression* ast_group_by_expr, + const ASTAlias* ast_alias, + const ASTGroupingItemOrder* ast_grouping_item_order, const NameScope* from_clause_scope, bool from_grouping_set, QueryResolutionInfo* query_resolution_info, ResolvedComputedColumnList* column_list = nullptr); @@ -2781,6 +2805,7 @@ class Resolver { // Resolves a standalone ORDER BY outside the context of a SELECT. // A ResolvedOrderByScan will be added to `scan`. + // This is used for ORDER BY after set operations and for pipe ORDER BY. absl::Status ResolveOrderBySimple(const ASTOrderBy* order_by, const NameScope* scope, const char* clause_name, @@ -2850,6 +2875,99 @@ class Resolver { ExprResolutionInfo* expr_resolution_info, std::unique_ptr* resolved_having); + absl::Status ResolvePipeOperatorList( + const ASTQuery* query, const NameScope* outer_scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list, + const Type* inferred_type_for_query); + + absl::Status ResolvePipeWhere( + const ASTPipeWhere* where, const NameScope* scope, + std::unique_ptr* current_scan); + + absl::Status ResolvePipeLimitOffset( + const ASTPipeLimitOffset* limit_offset, const NameScope* scope, + std::unique_ptr* current_scan); + + absl::Status ResolvePipeTablesample( + const ASTPipeTablesample* tablesample, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeSelect( + const ASTPipeSelect* select, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list, + const Type* inferred_type_for_pipe); + + absl::Status ResolvePipeExtend( + const ASTPipeExtend* extend, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeAggregate( + const ASTPipeAggregate* aggregate, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeOrderBy( + const ASTPipeOrderBy* order_by, const NameScope* scope, + std::unique_ptr* current_scan); + + absl::Status ResolvePipeCall( + const ASTPipeCall* call, const NameScope* outer_scope, + const NameScope* scope, std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeWindow( + const ASTPipeWindow* window, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeJoin( + const ASTPipeJoin* join, const NameScope* outer_scope, + const NameScope* scope, std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeAs( + const ASTPipeAs* pipe_as, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeStaticDescribe( + const ASTPipeStaticDescribe* pipe_static_describe, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeAssert( + const ASTPipeAssert* pipe_assert, const NameScope* scope, + std::unique_ptr* current_scan); + + absl::Status ResolvePipeDrop( + const ASTPipeDrop* pipe_drop, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeRename( + const ASTPipeRename* pipe_rename, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeSet( + const ASTPipeSet* pipe_set, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipePivot( + const ASTPipePivot* pipe_pivot, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + + absl::Status ResolvePipeUnpivot( + const ASTPipeUnpivot* pipe_unpivot, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list); + // Add a ProjectScan if necessary to make sure that `scan` produces columns // with the desired types. // - `target_column_list` provides the expected column types. @@ -3061,9 +3179,9 @@ class Resolver { // This returns a new ResolvedTVFScan which contains the name of the function // to call and the scalar and table-valued arguments to pass into the call. // - // Initial arguments can be passed in through `resolved_tvf_args`. These - // will be treated as the leftmost positional arguments, preceding the - // arguments in the `ast_tvf`. + // An initial `pipe_input_arg` of table type can optionally be passed in and + // will be treated as the pipe input for pipe CALL, and will be matched + // against the first table argument in the signature. // // The steps of resolving this function call proceed in the following order: // @@ -3092,7 +3210,7 @@ class Resolver { absl::Status ResolveTVF(const ASTTVF* ast_tvf, const NameScope* external_scope, const NameScope* local_scope, - std::vector* resolved_tvf_args, + ResolvedTVFArg* pipe_input_arg, std::unique_ptr* output, std::shared_ptr* output_name_list); @@ -3242,6 +3360,7 @@ class Resolver { absl::Status ResolveOrderByItems( const std::vector& output_column_list, const OrderByItemInfoVectorList& order_by_info_lists, + bool is_pipe_order_by, std::vector>* resolved_order_by_items); @@ -3252,7 +3371,7 @@ class Resolver { const ASTHint* order_by_hint, const std::vector& output_column_list, const OrderByItemInfoVectorList& order_by_info_lists, - std::unique_ptr* scan); + bool is_pipe_order_by, std::unique_ptr* scan); // Make a ResolvedColumnRef for . Caller owns the returned object. // Has side-effect of calling RecordColumnAccess on , so that @@ -3388,12 +3507,17 @@ class Resolver { absl::Status ResolveConnection( const ASTExpression* path_expr_or_default, - std::unique_ptr* resolved_connection); + std::unique_ptr* resolved_connection, + bool is_default_connection_allowed = false); absl::Status ResolveConnectionPath( const ASTPathExpression* path_expr, std::unique_ptr* resolved_connection); + absl::Status ResolveDefaultConnection( + const ASTDefaultLiteral* default_literal, + std::unique_ptr* resolved_connection); + absl::Status ResolveSequence( const ASTPathExpression* path_expr, std::unique_ptr* resolved_sequence); @@ -3666,14 +3790,13 @@ class Resolver { ExprResolutionInfo* expr_resolution_info, std::unique_ptr* resolved_expr_out); - // Resolve GROUP BY modifiers for aggregate functions. Note that the GROUP + // Resolve a GROUP BY modifier for an aggregate function. Note that the GROUP // BY modifiers are different from the GROUP BY clause; they occur directly // within the body of an aggregate function call (e.g. SUM(AVG(X) GROUP BY Y)) - absl::Status ResolveAggregateFunctionGroupingModifiers( - const ASTFunctionCall* ast_function, + absl::Status ResolveAggregateFunctionGroupByModifier( + const ASTGroupingItem* group_by_modifier, ExprResolutionInfo* expr_resolution_info, - std::vector>* - resolved_grouping_modifiers); + std::unique_ptr* resolved_group_by); // Resolve aggregate function for the first pass. This function also resolves // GROUP_ROWS clause if it is present. @@ -4787,12 +4910,11 @@ class Resolver { // it should always be 0 because this method is using the first signature to // match input arguments; if it doesn't match, this method return a non-OK // status. - // `arg_locations` and `resolved_tvf_args` may be non-empty to pass in - // initial positional arguments. + // if present is the pipe input argument in pipe CALL. absl::StatusOr MatchTVFSignature( const ASTTVF* ast_tvf, const TableValuedFunction* tvf_catalog_entry, const NameScope* external_scope, const NameScope* local_scope, - const FunctionResolver& function_resolver, + const FunctionResolver& function_resolver, ResolvedTVFArg* pipe_input_arg, std::unique_ptr* result_signature, std::vector* arg_locations, std::vector* resolved_tvf_args, @@ -4802,12 +4924,12 @@ class Resolver { // Prepares a list of TVF input arguments and a result signature. This // includes adding necessary casts and coercions, and wrapping the resolved // input arguments with TVFInputArgumentType as appropriate. - // `resolved_tvf_args` may be non-empty to pass in initial positional - // arguments that will precede the arguments from `ast_tvf`. + // if present is the pipe input argument in pipe CALL. absl::Status PrepareTVFInputArguments( absl::string_view tvf_name_string, const ASTTVF* ast_tvf, const TableValuedFunction* tvf_catalog_entry, const NameScope* external_scope, const NameScope* local_scope, + ResolvedTVFArg* pipe_input_arg, std::unique_ptr* result_signature, std::vector* resolved_tvf_args, std::vector* tvf_input_arguments); diff --git a/zetasql/analyzer/resolver_alter_stmt.cc b/zetasql/analyzer/resolver_alter_stmt.cc index 9c16f8239..c930c3b62 100644 --- a/zetasql/analyzer/resolver_alter_stmt.cc +++ b/zetasql/analyzer/resolver_alter_stmt.cc @@ -604,6 +604,24 @@ absl::Status Resolver::ResolveAlterExternalSchemaStatement( return absl::OkStatus(); } +absl::Status Resolver::ResolveAlterConnectionStatement( + const ASTAlterConnectionStatement* ast_statement, + std::unique_ptr* output) { + bool has_only_set_options_action = true; + std::vector> + resolved_alter_actions; + ZETASQL_RET_CHECK(ast_statement->path() != nullptr); + ZETASQL_RETURN_IF_ERROR(ResolveAlterActions(ast_statement, + /**/ "CONNECTION", + output, &has_only_set_options_action, + &resolved_alter_actions)); + + *output = MakeResolvedAlterConnectionStmt( + ast_statement->path()->ToIdentifierVector(), + std::move(resolved_alter_actions), ast_statement->is_if_exists()); + return absl::OkStatus(); +} + absl::Status Resolver::ResolveAlterTableStatement( const ASTAlterTableStatement* ast_statement, std::unique_ptr* output) { diff --git a/zetasql/analyzer/resolver_expr.cc b/zetasql/analyzer/resolver_expr.cc index 6116b4f79..773eb4413 100644 --- a/zetasql/analyzer/resolver_expr.cc +++ b/zetasql/analyzer/resolver_expr.cc @@ -52,6 +52,7 @@ #include "zetasql/analyzer/resolver_common_inl.h" #include "zetasql/common/errors.h" #include "zetasql/common/internal_analyzer_options.h" +#include "zetasql/common/internal_analyzer_output_properties.h" #include "zetasql/parser/ast_node.h" #include "zetasql/parser/ast_node_kind.h" #include "zetasql/parser/parse_tree.h" @@ -106,6 +107,7 @@ #include "zetasql/resolved_ast/resolved_column.h" #include "zetasql/resolved_ast/resolved_node.h" #include "zetasql/resolved_ast/resolved_node_kind.pb.h" +#include "zetasql/resolved_ast/target_syntax.h" #include "zetasql/base/case.h" #include "zetasql/base/string_numbers.h" #include "absl/cleanup/cleanup.h" @@ -1374,6 +1376,8 @@ absl::Status Resolver::ResolveColumnRefExprToPostGroupingColumn( ZETASQL_RET_CHECK_EQ(RESOLVED_COLUMN_REF, (*resolved_column_ref_expr)->node_kind()); ABSL_DCHECK(query_resolution_info != nullptr); ABSL_DCHECK(query_resolution_info->HasGroupByOrAggregation()); + // This function only runs for SELECT *. + ZETASQL_RET_CHECK(!query_resolution_info->IsPipeAggregate()); const ResolvedColumnRef* resolved_column_ref = (*resolved_column_ref_expr)->GetAs(); @@ -1548,6 +1552,8 @@ absl::Status Resolver::ResolvePathExpressionAsExpression( ParseLocationRange path_parse_location = path_expr.GetParseLocationRange(); IdString first_name = path_expr.GetFirstIdString(); + const std::string lowercase_name = + absl::AsciiStrToLower(first_name.ToStringView()); // The length of the longest prefix of that has been resolved // successfully. Often 1 after successful resolution and 0 if resolution // fails, but can be longer for name paths and named constants. 0 while @@ -1571,6 +1577,10 @@ absl::Status Resolver::ResolvePathExpressionAsExpression( if (expr_resolution_info->is_post_distinct()) { problem_string = "not visible after SELECT DISTINCT"; } + if (expr_resolution_info->query_resolution_info != nullptr && + expr_resolution_info->query_resolution_info->IsPipeAggregate()) { + problem_string = "not aggregated"; + } ZETASQL_RETURN_IF_ERROR(expr_resolution_info->name_scope->LookupNamePath( path_expr, expr_resolution_info->clause_name, problem_string, in_strict_mode(), &correlated_columns_sets, &num_names_consumed, @@ -1671,8 +1681,6 @@ absl::Status Resolver::ResolvePathExpressionAsExpression( // expression column (for standalone expression evaluation only). // TODO: Move this to a separate function to handle this // case out of line. - const std::string lowercase_name = - absl::AsciiStrToLower(path_expr.GetFirstIdString().ToString()); const Type* column_type = zetasql_base::FindPtrOrNull( analyzer_options_.expression_columns(), lowercase_name); @@ -1713,20 +1721,20 @@ absl::Status Resolver::ResolvePathExpressionAsExpression( num_names_consumed = 1; } } + } + if (num_names_consumed == 0 && + InternalAnalyzerOptions::GetLookupExpressionCallback(analyzer_options_) != + nullptr) { // Lookup the column using the callback function if set. - if (num_names_consumed == 0 && - InternalAnalyzerOptions::GetLookupExpressionCallback( - analyzer_options_) != nullptr) { - std::unique_ptr expr; - ZETASQL_RETURN_IF_ERROR(InternalAnalyzerOptions::GetLookupExpressionCallback( - analyzer_options_)(lowercase_name, expr)); - if (expr != nullptr) { - MaybeRecordParseLocation(path_expr.GetParseLocationRange(), - const_cast(expr.get())); - resolved_expr = std::move(expr); - num_names_consumed = 1; - } + std::unique_ptr expr; + ZETASQL_RETURN_IF_ERROR(InternalAnalyzerOptions::GetLookupExpressionCallback( + analyzer_options_)(lowercase_name, expr)); + if (expr != nullptr) { + MaybeRecordParseLocation(path_expr.GetParseLocationRange(), + const_cast(expr.get())); + resolved_expr = std::move(expr); + num_names_consumed = 1; } } @@ -4356,6 +4364,8 @@ absl::Status Resolver::ResolveIntervalArgument( << "INTERVAL argument only support single date part field."; } + ZETASQL_RET_CHECK(resolved_arguments_out->back() != nullptr); + // Coerce the interval value argument to INT64 if necessary. if (!resolved_arguments_out->back()->type()->IsInt64()) { std::unique_ptr resolved_interval_value_arg = @@ -4925,33 +4935,38 @@ bool IsGroupingFunction(const Function* function) { function->signatures()[0].context_id() == FN_GROUPING && function->IsZetaSQLBuiltin(); } -} // namespace -absl::Status Resolver::ResolveAggregateFunctionGroupingModifiers( - const ASTFunctionCall* ast_function, - ExprResolutionInfo* expr_resolution_info, - std::vector>* - resolved_grouping_modifiers) { - ZETASQL_RET_CHECK(resolved_grouping_modifiers->empty()); - if (ast_function->group_by() == nullptr) { - return absl::OkStatus(); +absl::Status CheckNodeIsNull(const ASTNode* node, + absl::string_view error_message) { + if (node != nullptr) { + return MakeSqlErrorAt(node) << error_message; } + return absl::OkStatus(); +} - auto sql_error_if_ast_node_present = - [](const ASTNode* node, absl::string_view error_message) -> absl::Status { - if (node != nullptr) { - return MakeSqlErrorAt(node) << error_message; - } - return absl::OkStatus(); - }; - const ASTGroupBy* group_by = ast_function->group_by(); - ZETASQL_RETURN_IF_ERROR(sql_error_if_ast_node_present( - group_by->hint(), - "Hints are not supported inside an aggregate function.")); - ZETASQL_RETURN_IF_ERROR(sql_error_if_ast_node_present( +absl::Status ValidateAggregateFunctionGroupByClause( + const ASTGroupBy* group_by) { + ZETASQL_RET_CHECK_NE(group_by, nullptr); + ZETASQL_RET_CHECK(!group_by->grouping_items().empty()); + ZETASQL_RETURN_IF_ERROR( + CheckNodeIsNull(group_by->hint(), + "Hints are not supported inside an aggregate function.")); + ZETASQL_RETURN_IF_ERROR(CheckNodeIsNull( group_by->all(), "GROUP BY ALL is not supported inside an aggregate function.")); + if (group_by->and_order_by()) { + return MakeSqlErrorAt(group_by) + << "GROUP AND ORDER BY is not supported outside pipe AGGREGATE."; + } + return absl::OkStatus(); +} + +} // namespace +absl::Status Resolver::ResolveAggregateFunctionGroupByModifier( + const ASTGroupingItem* group_by_modifier, + ExprResolutionInfo* expr_resolution_info, + std::unique_ptr* resolved_group_by) { ZETASQL_RET_CHECK_NE(expr_resolution_info->query_resolution_info, nullptr); ExprResolutionInfo multi_level_agg_expr_resolution_info( @@ -4963,51 +4978,52 @@ absl::Status Resolver::ResolveAggregateFunctionGroupingModifiers( .clause_name = "GROUP BY inside aggregate", }); - for (const ASTGroupingItem* grouping_item : group_by->grouping_items()) { - ZETASQL_RET_CHECK_NE(grouping_item, nullptr); - ZETASQL_RETURN_IF_ERROR(sql_error_if_ast_node_present( - grouping_item->rollup(), - "GROUP BY ROLLUP is not supported inside an aggregate function.")); - ZETASQL_RETURN_IF_ERROR(sql_error_if_ast_node_present( - grouping_item->cube(), - "GROUP BY CUBE is not supported inside an aggregate function.")); - ZETASQL_RETURN_IF_ERROR( - sql_error_if_ast_node_present(grouping_item->grouping_set_list(), - "GROUP BY GROUPING SETS is not supported " - "inside an aggregate function.")); - - std::unique_ptr resolved_grouping_expr; - ZETASQL_RETURN_IF_ERROR(ResolveExpr(grouping_item->expression(), - &multi_level_agg_expr_resolution_info, - &resolved_grouping_expr)); - - std::string type_description; - if (!resolved_grouping_expr->type()->SupportsGrouping(language(), - &type_description)) { - return MakeSqlErrorAt(grouping_item) - << "GROUP BY modifier has type " << type_description - << ", which is not groupable."; - } - - // TODO: Refactor ordinal checking logic into a shared method. - if (resolved_grouping_expr->node_kind() == RESOLVED_LITERAL && - !resolved_grouping_expr->GetAs() - ->has_explicit_type()) { - const Value& value = - resolved_grouping_expr->GetAs()->value(); - if (value.type_kind() == TYPE_INT64 && !value.is_null()) { - return MakeSqlErrorAt(grouping_item) - << "GROUP BY modifiers cannot specify ordinals."; - } else { - return MakeSqlErrorAt(grouping_item) - << "GROUP BY modifiers cannot be literal values."; - } + ZETASQL_RET_CHECK_NE(group_by_modifier, nullptr); + ZETASQL_RETURN_IF_ERROR(CheckNodeIsNull( + group_by_modifier->rollup(), + "GROUP BY ROLLUP is not supported inside an aggregate function.")); + ZETASQL_RETURN_IF_ERROR(CheckNodeIsNull( + group_by_modifier->cube(), + "GROUP BY CUBE is not supported inside an aggregate function.")); + ZETASQL_RETURN_IF_ERROR(CheckNodeIsNull(group_by_modifier->grouping_set_list(), + "GROUP BY GROUPING SETS is not supported " + "inside an aggregate function.")); + ZETASQL_RETURN_IF_ERROR(CheckNodeIsNull(group_by_modifier->grouping_item_order(), + "GROUP BY does not support order " + "specification outside pipe AGGREGATE")); + ZETASQL_RETURN_IF_ERROR(CheckNodeIsNull( + group_by_modifier->alias(), + "GROUP BY expressions in an aggregate function cannot have aliases.")); + + std::unique_ptr resolved_grouping_expr; + ZETASQL_RETURN_IF_ERROR(ResolveExpr(group_by_modifier->expression(), + &multi_level_agg_expr_resolution_info, + &resolved_grouping_expr)); + + std::string type_description; + if (!resolved_grouping_expr->type()->SupportsGrouping(language(), + &type_description)) { + return MakeSqlErrorAt(group_by_modifier) + << "GROUP BY modifier has type " << type_description + << ", which is not groupable."; + } + + // TODO: Refactor ordinal checking logic into a shared method. + if (resolved_grouping_expr->node_kind() == RESOLVED_LITERAL && + !resolved_grouping_expr->GetAs()->has_explicit_type()) { + const Value& value = + resolved_grouping_expr->GetAs()->value(); + if (value.type_kind() == TYPE_INT64 && !value.is_null()) { + return MakeSqlErrorAt(group_by_modifier) + << "GROUP BY modifiers cannot specify ordinals."; + } else { + return MakeSqlErrorAt(group_by_modifier) + << "GROUP BY modifiers cannot be literal values."; } - // TODO: Plumb the `resolved_grouping_expr` through the query - // resolution pipeline. - ZETASQL_RET_CHECK_FAIL() << "GROUP BY modifier is not yet supported."; } - return absl::OkStatus(); + // TODO: Plumb the `resolved_grouping_expr` through the query + // resolution pipeline. + ZETASQL_RET_CHECK_FAIL() << "GROUP BY modifier is not yet supported."; } absl::Status Resolver::ResolveAggregateFunctionCallFirstPass( @@ -5019,8 +5035,28 @@ absl::Status Resolver::ResolveAggregateFunctionCallFirstPass( std::unique_ptr* resolved_expr_out) { std::vector> resolved_grouping_modifiers; - ZETASQL_RETURN_IF_ERROR(ResolveAggregateFunctionGroupingModifiers( - ast_function, expr_resolution_info, &resolved_grouping_modifiers)); + if (ast_function->group_by() != nullptr) { + ZETASQL_RETURN_IF_ERROR( + ValidateAggregateFunctionGroupByClause(ast_function->group_by())); + for (const ASTGroupingItem* ast_group_by_modifier : + ast_function->group_by()->grouping_items()) { + std::unique_ptr resolved_group_by; + ZETASQL_RETURN_IF_ERROR(ResolveAggregateFunctionGroupByModifier( + ast_group_by_modifier, expr_resolution_info, &resolved_group_by)); + resolved_grouping_modifiers.push_back(std::move(resolved_group_by)); + } + } + // TODO: `MultiLevelAggregateInfo` should probably not be owned + // by `QueryResolutionInfo`. + if (expr_resolution_info->query_resolution_info != nullptr) { + ZETASQL_RET_CHECK_NE(expr_resolution_info->query_resolution_info, nullptr); + ZETASQL_RETURN_IF_ERROR( + expr_resolution_info->query_resolution_info + ->multi_level_aggregate_info() + ->AddAggregateFunction( + ast_function, + expr_resolution_info->enclosing_aggregate_function)); + } std::unique_ptr local_expr_resolution_info; std::vector> correlated_columns; if (ast_function->with_group_rows() == nullptr) { @@ -7720,7 +7756,7 @@ absl::Status Resolver::FinishResolvingAggregateFunction( } ZETASQL_RETURN_IF_ERROR(ResolveOrderByItems( /*output_column_list=*/{}, {order_by_info}, - &resolved_order_by_items)); + /*is_pipe_order_by=*/false, &resolved_order_by_items)); } const ASTLimitOffset* limit_offset = ast_function_call->limit_offset(); std::unique_ptr limit_expr; @@ -8670,6 +8706,15 @@ absl::Status Resolver::ResolveFunctionCallImpl( } } + // Keep track of the enclosing aggregate function for resolving multi-level + // aggregates. + if (function->IsAggregate()) { + ZETASQL_RET_CHECK(ast_location != nullptr); + ZETASQL_RET_CHECK(ast_location->Is()); + expr_resolution_info->enclosing_aggregate_function = + ast_location->GetAsOrDie(); + } + // Rather than resolved_arguments, resolve arguments into generic arguments? std::vector> resolved_arguments; std::vector ast_arguments; diff --git a/zetasql/analyzer/resolver_query.cc b/zetasql/analyzer/resolver_query.cc index d9362bbf7..40ca7f099 100644 --- a/zetasql/analyzer/resolver_query.cc +++ b/zetasql/analyzer/resolver_query.cc @@ -52,6 +52,8 @@ #include "zetasql/analyzer/resolver_common_inl.h" #include "zetasql/analyzer/set_operation_resolver_base.h" #include "zetasql/common/errors.h" +#include "zetasql/common/internal_analyzer_output_properties.h" +#include "zetasql/public/deprecation_warning.pb.h" #include "zetasql/public/function_signature.h" #include "zetasql/public/functions/array_zip_mode.pb.h" #include "zetasql/public/input_argument_type.h" @@ -111,7 +113,6 @@ #include "zetasql/base/case.h" #include "absl/algorithm/container.h" #include "absl/base/casts.h" -#include "absl/cleanup/cleanup.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "zetasql/base/check.h" @@ -285,6 +286,60 @@ absl::Status CreateUnsupportedGroupingSetsError( grouping_clause_name, clause_name); } +// Check that expected scan nodes were added on top of a previous scan. +// Starting from `outer_scan` and recursively traversing input scans, expect +// to find `inner_scan`. In between, expect to find a node of type +// `expected_scan_kind` (unless `expected_scan_kind` is std::nullopt). +// Other scan types in `allowed_extra_scan_types` are also allowed. +// `node_context` is a name for the error message. +// +// Note: This only works for the node types listed in the switch. +absl::Status CheckForExpectedScans( + const ResolvedScan* outer_scan, const ResolvedScan* inner_scan, + std::optional expected_scan_kind, + const char* node_context, + const std::set& allowed_extra_scan_types = {}) { + const ResolvedScan* next_scan = outer_scan; + bool found_expected = false; + while (next_scan != inner_scan) { + const ResolvedNodeKind kind = next_scan->node_kind(); + if (!found_expected && expected_scan_kind.has_value() && + kind == expected_scan_kind.value()) { + found_expected = true; + } else if (allowed_extra_scan_types.find(kind) == + allowed_extra_scan_types.end()) { + ZETASQL_RET_CHECK_FAIL() << node_context << " produced unexpected scan of type " + << ResolvedNodeKindToString(kind); + } + + switch (kind) { + case RESOLVED_PROJECT_SCAN: + next_scan = next_scan->GetAs()->input_scan(); + break; + case RESOLVED_AGGREGATE_SCAN: + next_scan = next_scan->GetAs()->input_scan(); + break; + case RESOLVED_ANALYTIC_SCAN: + next_scan = next_scan->GetAs()->input_scan(); + break; + case RESOLVED_ORDER_BY_SCAN: + next_scan = next_scan->GetAs()->input_scan(); + break; + default: + ZETASQL_RET_CHECK_FAIL() << node_context << " produced unhandled scan of type " + << ResolvedNodeKindToString(kind); + } + ZETASQL_RET_CHECK(next_scan != nullptr); + } + + if (expected_scan_kind.has_value() && !found_expected) { + ZETASQL_RET_CHECK_FAIL() << node_context + << " failed to produce a scan with expected type " + << ResolvedNodeKindToString(expected_scan_kind.value()); + } + return absl::OkStatus(); +} + // `PartitionedComputedColumns` represents a partitioning of computed columns // such that: // - Each 'dependee_column' is depended upon by at least 1 other column in @@ -557,7 +612,12 @@ absl::Status Resolver::ResolveQuery( const Type* inferred_type_for_query) { RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); - const Type* inferred_type_for_base_query = inferred_type_for_query; + // If we have pipe operators, the inferred type applies to the last pipe + // operator, not to the initial query. + const Type* inferred_type_for_base_query = nullptr; + if (query->pipe_operator_list().empty()) { + inferred_type_for_base_query = inferred_type_for_query; + } ZETASQL_ASSIGN_OR_RETURN( std::vector> with_entries, @@ -566,8 +626,16 @@ absl::Status Resolver::ResolveQuery( inferred_type_for_base_query, output, output_name_list)); + ZETASQL_RETURN_IF_ERROR(ResolvePipeOperatorList( + query, scope, output, output_name_list, inferred_type_for_query)); + // Add coercions to the final column output types if needed. if (is_outer_query && !analyzer_options().get_target_column_types().empty()) { + // TODO The function below assumes the outer scan is a + // ResolvedProjectScan, so it needs to be generalized for pipes. + ZETASQL_RET_CHECK(query->pipe_operator_list().empty()) + << "Coercing statement to expected types not implemented yet " + "for pipe queries"; ZETASQL_RETURN_IF_ERROR(CoerceQueryStatementResultToTypes( query, analyzer_options().get_target_column_types(), output, output_name_list)); @@ -651,6 +719,932 @@ static absl::Status UpdateNameListForTableAlias( return absl::OkStatus(); } +absl::Status Resolver::ResolvePipeOperatorList( + const ASTQuery* query, const NameScope* outer_scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list, + const Type* inferred_type_for_query) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + + for (const ASTPipeOperator* pipe_operator : query->pipe_operator_list()) { + if (pipe_operator == query->pipe_operator_list().front() && + !language().LanguageFeatureEnabled(FEATURE_PIPES)) { + return MakeSqlErrorAt(pipe_operator) << "Pipe query syntax not supported"; + } + + // We'll infer the type for the last pipe operator only. + // TODO: We could could push this to earlier pipe operators if later + // ones like WHERE don't change output. + const Type* inferred_type_for_pipe = nullptr; + if (pipe_operator == query->pipe_operator_list().back()) { + inferred_type_for_pipe = inferred_type_for_query; + } + + NameScope current_scope(outer_scope, *current_name_list); + + switch (pipe_operator->node_kind()) { + case AST_PIPE_WHERE: + ZETASQL_RETURN_IF_ERROR(ResolvePipeWhere(pipe_operator->GetAs(), + ¤t_scope, current_scan)); + break; + case AST_PIPE_LIMIT_OFFSET: + ZETASQL_RETURN_IF_ERROR( + ResolvePipeLimitOffset(pipe_operator->GetAs(), + ¤t_scope, current_scan)); + break; + case AST_PIPE_SELECT: + ZETASQL_RETURN_IF_ERROR(ResolvePipeSelect( + pipe_operator->GetAs(), ¤t_scope, current_scan, + current_name_list, inferred_type_for_pipe)); + break; + case AST_PIPE_EXTEND: + // There is currently no type inference for columns in PipeExtend. + // Supporting it would require matching newly added columns against + // some expected type. + ZETASQL_RETURN_IF_ERROR(ResolvePipeExtend(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_AGGREGATE: + ZETASQL_RETURN_IF_ERROR(ResolvePipeAggregate( + pipe_operator->GetAs(), ¤t_scope, + current_scan, current_name_list)); + break; + case AST_PIPE_ORDER_BY: + ZETASQL_RETURN_IF_ERROR( + ResolvePipeOrderBy(pipe_operator->GetAs(), + ¤t_scope, current_scan)); + break; + case AST_PIPE_CALL: + ZETASQL_RETURN_IF_ERROR(ResolvePipeCall(pipe_operator->GetAs(), + outer_scope, ¤t_scope, + current_scan, current_name_list)); + break; + case AST_PIPE_WINDOW: + ZETASQL_RETURN_IF_ERROR(ResolvePipeWindow(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_JOIN: + ZETASQL_RETURN_IF_ERROR(ResolvePipeJoin(pipe_operator->GetAs(), + outer_scope, ¤t_scope, + current_scan, current_name_list)); + break; + case AST_PIPE_TABLESAMPLE: + ZETASQL_RETURN_IF_ERROR(ResolvePipeTablesample( + pipe_operator->GetAs(), ¤t_scope, + current_scan, current_name_list)); + break; + case AST_PIPE_SET_OPERATION: + return MakeSqlErrorAt(pipe_operator) + << "Pipe set operations not supported yet; Try using standard " + "set operator syntax between parenthesized pipe queries"; + break; + case AST_PIPE_DISTINCT: + return MakeSqlErrorAt(pipe_operator) + << "Pipe DISTINCT operator not supported yet; Try using " + "|> AGGREGATE GROUP BY ... or |> SELECT DISTINCT ..."; + break; + case AST_PIPE_AS: + ZETASQL_RETURN_IF_ERROR(ResolvePipeAs(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_STATIC_DESCRIBE: + ZETASQL_RETURN_IF_ERROR(ResolvePipeStaticDescribe( + pipe_operator->GetAs(), ¤t_scope, + current_scan, current_name_list)); + break; + case AST_PIPE_ASSERT: + ZETASQL_RETURN_IF_ERROR(ResolvePipeAssert(pipe_operator->GetAs(), + ¤t_scope, current_scan)); + break; + case AST_PIPE_DROP: + ZETASQL_RETURN_IF_ERROR(ResolvePipeDrop(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_RENAME: + ZETASQL_RETURN_IF_ERROR(ResolvePipeRename(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_SET: + ZETASQL_RETURN_IF_ERROR(ResolvePipeSet(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_PIVOT: + ZETASQL_RETURN_IF_ERROR(ResolvePipePivot(pipe_operator->GetAs(), + ¤t_scope, current_scan, + current_name_list)); + break; + case AST_PIPE_UNPIVOT: + ZETASQL_RETURN_IF_ERROR(ResolvePipeUnpivot( + pipe_operator->GetAs(), ¤t_scope, + current_scan, current_name_list)); + break; + default: + // When all operators are fully implemented, this can become + // a ZETASQL_RET_CHECK. + return MakeSqlErrorAt(pipe_operator) + << "Unsupported pipe operator: " + << pipe_operator->GetNodeKindString(); + } + } + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeWhere( + const ASTPipeWhere* where, const NameScope* scope, + std::unique_ptr* current_scan) { + std::unique_ptr resolved_where; + // Some error cases require different leading capitals on these strings. + static constexpr char kPipeWhereClause[] = "pipe WHERE clause"; + static constexpr char kWhereClause[] = "WHERE clause"; + const ASTExpression* ast_expr = where->where()->expression(); + + QueryResolutionInfo query_resolution_info(this); + query_resolution_info.analytic_resolver()->DisableNamedWindowRefs( + kPipeWhereClause); + ExprResolutionInfo expr_resolution_info(scope, &query_resolution_info, + /*top_level_ast_expr_in=*/nullptr, + /*column_alias_in=*/IdString(), + kPipeWhereClause); + + ZETASQL_RETURN_IF_ERROR( + ResolveExpr(ast_expr, &expr_resolution_info, &resolved_where)); + + ZETASQL_RETURN_IF_ERROR(CoerceExprToBool(ast_expr, kWhereClause, &resolved_where)); + + ZETASQL_RET_CHECK(!query_resolution_info.HasAggregation()); + ZETASQL_RETURN_IF_ERROR(query_resolution_info.CheckComputedColumnListsAreEmpty()); + if (query_resolution_info.HasAnalytic()) { + // Add an AnalyticScan if any window functions were present. + // This also makes a ProjectScan if necessary before the AnalyticScan. + ZETASQL_RETURN_IF_ERROR(AddAnalyticScan(&query_resolution_info, current_scan)); + } + + const auto& tmp_column_list = (*current_scan)->column_list(); + *current_scan = MakeResolvedFilterScan( + tmp_column_list, std::move(*current_scan), std::move(resolved_where)); + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeLimitOffset( + const ASTPipeLimitOffset* limit_offset, const NameScope* scope, + std::unique_ptr* current_scan) { + return ResolveLimitOffsetScan(limit_offset->limit_offset(), scope, + current_scan); +} + +absl::Status Resolver::ResolvePipeTablesample( + const ASTPipeTablesample* tablesample, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + // This takes NameList rather than NameScope. Mostly it does not resolve + // expressions, although PARTITION BY is an exception, and correlated + // references won't work. + // It may add a weight column into `current_name_list`. + return ResolveTablesampleClause(tablesample->sample(), current_name_list, + current_scan); +} + +absl::Status Resolver::ResolvePipeSelect( + const ASTPipeSelect* pipe_select, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list, + const Type* inferred_type_for_pipe) { + const ASTSelect* select = pipe_select->select(); + + if (select->select_with() != nullptr) { + return MakeSqlErrorAt(select->select_with()) + << "Pipe SELECT does not support SELECT WITH"; + } + if (select->hint() != nullptr) { + // The reason hints aren't supported on pipe SELECT yet is because these + // hints would show up on ResolvedProjectScan, which in normal syntax, + // indicates a hint that applies to an entire query. The semantics seem + // different for the hint here, but we haven't indicated how yet. + return MakeSqlErrorAt(select->hint()) + << "Pipe SELECT does not support hints yet"; + } + + // By construction in the parser, the ASTSelect shouldn't have a FROM clause + // or any other clauses after the SELECT list. + // The is_distinct modifier is allowed, but doesn't have an AST node. + ZETASQL_RETURN_IF_ERROR(CheckForUnwantedSelectClauseChildNodes( + select, {select->select_list(), select->select_as(), select->hint()}, + "Pipe SELECT")); + // Repeating this, to assert we are resolving this like a no-FROM-clause + // SELECT, which will block grouping and many other operations. + ZETASQL_RET_CHECK(select->from_clause() == nullptr); + ZETASQL_RET_CHECK(select->select_list() != nullptr); + + const ResolvedScan* original_input_scan = current_scan->get(); + const IdString query_alias = MakeIdString("$pipe_select"); + + std::shared_ptr new_name_list; + + ZETASQL_RETURN_IF_ERROR(ResolveSelectAfterFrom( + select, /*order_by=*/nullptr, + /*limit_offset=*/nullptr, scope, query_alias, SelectForm::kPipeSelect, + SelectWithMode::NONE, + /*force_new_columns_for_projected_outputs=*/false, inferred_type_for_pipe, + current_scan, *current_name_list, &new_name_list)); + + *current_name_list = std::move(new_name_list); + + // Check that we added ResolvedProjectScans, a ResolvedAggregateScan if we + // had DISTINCT, possibly a ResolvedAnalyticScan, and no other scans. + ZETASQL_RETURN_IF_ERROR(CheckForExpectedScans( + current_scan->get(), original_input_scan, + select->distinct() ? RESOLVED_AGGREGATE_SCAN : RESOLVED_PROJECT_SCAN, + "Pipe SELECT", + /*allowed_extra_scan_types=*/ + {RESOLVED_PROJECT_SCAN, RESOLVED_ANALYTIC_SCAN})); + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeExtend( + const ASTPipeExtend* extend, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + const ASTSelect* select = extend->select(); + + // By construction in the parser, the ASTSelect shouldn't have anything + // other than the SELECT list. + ZETASQL_RETURN_IF_ERROR(CheckForUnwantedSelectClauseChildNodes( + select, {select->select_list()}, "Pipe EXTEND")); + ZETASQL_RET_CHECK_NE(select->select_list(), nullptr); + ZETASQL_RET_CHECK(!select->distinct()); + + const ResolvedScan* original_input_scan = current_scan->get(); + const IdString query_alias = MakeIdString("$pipe_extend"); + + std::shared_ptr new_name_list; + + ZETASQL_RETURN_IF_ERROR( + ResolveSelectAfterFrom(select, /*order_by=*/nullptr, + /*limit_offset=*/nullptr, scope, query_alias, + SelectForm::kPipeExtend, SelectWithMode::NONE, + /*force_new_columns_for_projected_outputs=*/false, + /*inferred_type_for_query=*/nullptr, current_scan, + *current_name_list, &new_name_list)); + + // We could get ProjectScans or AnalyticScans, but we're not required + // to get either of them. + ZETASQL_RETURN_IF_ERROR( + CheckForExpectedScans(current_scan->get(), original_input_scan, + /*expected_scan_kind=*/std::nullopt, "Pipe EXTEND", + /*allowed_extra_scan_types=*/ + {RESOLVED_ANALYTIC_SCAN, RESOLVED_PROJECT_SCAN})); + + *current_name_list = std::move(new_name_list); + return absl::OkStatus(); +} + +// Give an error if both aggregate list and group by list are empty. +static absl::Status CheckForEmptyPipeAggregate(const ASTSelect* select) { + if (select->select_list()->columns().empty()) { + bool group_by_is_empty = true; + if (select->group_by() != nullptr) { + for (const ASTGroupingItem* item : select->group_by()->grouping_items()) { + // To detect "()", we have to look for the absence of the other + // possible field types. + if (item->expression() != nullptr || item->rollup() != nullptr || + item->cube() != nullptr || item->grouping_set_list() != nullptr) { + group_by_is_empty = false; + break; + } + } + } + if (group_by_is_empty) { + return MakeSqlErrorAt(select) + << "Pipe AGGREGATE cannot have both an empty aggregate list " + "and an empty GROUP BY"; + } + } + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeAggregate( + const ASTPipeAggregate* aggregate, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + const ASTSelect* select = aggregate->select(); + + // By construction in the parser, this ASTSelect should only have a + // SELECT list and possibly a GROUP BY. + ZETASQL_RETURN_IF_ERROR(CheckForUnwantedSelectClauseChildNodes( + select, {select->select_list(), select->group_by()}, "Pipe AGGREGATE")); + ZETASQL_RET_CHECK_NE(select->select_list(), nullptr); + + const ASTGroupBy* group_by = select->group_by(); + if (group_by != nullptr && group_by->all()) { + // Currently unreachable because the parser doesn't let this through, + // but that was just an artifact of how some conflicts were resolved. + return MakeSqlErrorAt(select) + << "Pipe AGGREGATE does not support GROUP BY ALL"; + } + + // Give an error if both aggregate list and group by list are empty. + ZETASQL_RETURN_IF_ERROR(CheckForEmptyPipeAggregate(select)); + + const ResolvedScan* original_input_scan = current_scan->get(); + const IdString query_alias = MakeIdString("$aggregate"); + + std::shared_ptr new_name_list; + + ZETASQL_RETURN_IF_ERROR( + ResolveSelectAfterFrom(select, /*order_by=*/nullptr, + /*limit_offset=*/nullptr, scope, query_alias, + SelectForm::kPipeAggregate, SelectWithMode::NONE, + /*force_new_columns_for_projected_outputs=*/false, + /*inferred_type_for_query=*/nullptr, current_scan, + *current_name_list, &new_name_list)); + + *current_name_list = std::move(new_name_list); + + // Check that we added one AggregateScan, maybe some ProjectScans, maybe + // an OrderByScan, and nothing else. + ZETASQL_RETURN_IF_ERROR( + CheckForExpectedScans(current_scan->get(), original_input_scan, + RESOLVED_AGGREGATE_SCAN, "Pipe AGGREGATE", + /*allowed_extra_scan_types=*/ + {RESOLVED_PROJECT_SCAN, RESOLVED_ORDER_BY_SCAN})); + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeOrderBy( + const ASTPipeOrderBy* order_by, const NameScope* scope, + std::unique_ptr* current_scan) { + return ResolveOrderBySimple(order_by->order_by(), scope, + "pipe ORDER BY clause", OrderBySimpleMode::kPipes, + current_scan); +} + +// ResolvePipeCall takes `outer_scope` because TVF arguments cannot +// reference columns from the input table, which are not constant over +// the invocation of this TVF call. +absl::Status Resolver::ResolvePipeCall( + const ASTPipeCall* call, const NameScope* outer_scope, + const NameScope* scope, std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + const ASTTVF* tvf = call->tvf(); + + // The input scan becomes the first table-typed TVF argument. + ResolvedTVFArg table_arg; + table_arg.SetScan(std::move(*current_scan), *current_name_list, + /*is_pipe_input_table=*/true); + + std::shared_ptr new_name_list; + + ZETASQL_RETURN_IF_ERROR(ResolveTVF(tvf, outer_scope, scope, &table_arg, current_scan, + &new_name_list)); + + *current_name_list = new_name_list; + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeWindow( + const ASTPipeWindow* window, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + const ASTSelect* select = window->select(); + + // By construction in the parser, the ASTSelect shouldn't have anything + // other than the SELECT list. + ZETASQL_RETURN_IF_ERROR(CheckForUnwantedSelectClauseChildNodes( + select, {select->select_list()}, "Pipe WINDOW")); + ZETASQL_RET_CHECK(select->select_list() != nullptr); + ZETASQL_RET_CHECK(!select->distinct()); + + const ResolvedScan* original_input_scan = current_scan->get(); + const IdString query_alias = MakeIdString("$pipe_window"); + + std::shared_ptr new_name_list; + + ZETASQL_RETURN_IF_ERROR( + ResolveSelectAfterFrom(select, /*order_by=*/nullptr, + /*limit_offset=*/nullptr, scope, query_alias, + SelectForm::kPipeWindow, SelectWithMode::NONE, + /*force_new_columns_for_projected_outputs=*/false, + /*inferred_type_for_query=*/nullptr, current_scan, + *current_name_list, &new_name_list)); + + // Check that we added a ResolvedAnalyticScan, maybe some + // ResolvedProjectScans, and no other scan kinds. + ZETASQL_RETURN_IF_ERROR(CheckForExpectedScans( + current_scan->get(), original_input_scan, RESOLVED_ANALYTIC_SCAN, + "Pipe WINDOW", + /*allowed_extra_scan_types=*/{RESOLVED_PROJECT_SCAN})); + + *current_name_list = std::move(new_name_list); + + return absl::OkStatus(); +} + +// ResolvePipeJoin takes `outer_scope`, which most other pipe operators don't. +// This is necessary because TVF input arguments and parenthesized joins on +// the RHS of a JOIN cannot see items from the join LHS, and can only see +// correlated references to outer queries. The pipe input acts as the join LHS +// here, so we don't want those cases to see names from the pipe input. +absl::Status Resolver::ResolvePipeJoin( + const ASTPipeJoin* join, const NameScope* outer_scope, + const NameScope* scope, std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + const ASTJoin* ast_join = join->join(); + ZETASQL_RET_CHECK(ast_join->lhs() != nullptr); + ZETASQL_RET_CHECK_EQ(ast_join->lhs()->node_kind(), AST_PIPE_JOIN_LHS_PLACEHOLDER); + + ZETASQL_RETURN_IF_ERROR(ResolveJoinRhs(ast_join, outer_scope, scope, + *current_name_list, std::move(*current_scan), + current_scan, current_name_list)); + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeAs( + const ASTPipeAs* pipe_as, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + IdString alias = pipe_as->alias()->GetAsIdString(); + auto output_name_list = std::make_shared(); + + ZETASQL_RETURN_IF_ERROR(UpdateNameListForTableAlias( + pipe_as, alias, *current_name_list, &output_name_list)); + *current_name_list = output_name_list; + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeAssert( + const ASTPipeAssert* pipe_assert, const NameScope* scope, + std::unique_ptr* current_scan) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + + if (!language().LanguageFeatureEnabled(FEATURE_PIPE_ASSERT)) { + return MakeSqlErrorAt(pipe_assert) << "Pipe ASSERT not supported"; + } + + std::unique_ptr condition_expr; + static constexpr char kAssertClause[] = "ASSERT condition"; + ZETASQL_RETURN_IF_ERROR(ResolveScalarExpr(pipe_assert->condition(), scope, + kAssertClause, &condition_expr)); + ZETASQL_RETURN_IF_ERROR(CoerceExprToBool(pipe_assert->condition(), kAssertClause, + &condition_expr)); + + std::vector> message_expr_list; + message_expr_list.reserve(pipe_assert->message_list().size()); + + for (const ASTExpression* ast_message : pipe_assert->message_list()) { + std::unique_ptr expr; + ZETASQL_RETURN_IF_ERROR( + ResolveScalarExpr(ast_message, scope, kAssertClause, &expr)); + + ZETASQL_RETURN_IF_ERROR(CoerceExprToType( + ast_message, type_factory_->get_string(), kExplicitCoercion, + "ASSERT message value has type $1 which cannot be coerced to $0", + &expr)); + + ExprResolutionInfo expr_resolution_info(scope, "pipe ASSERT"); + + // Wrap the message in an IFNULL(, "NULL") function call so that if + // a payload is NULL, it is printed as "NULL". + // + // We do not add the function call for non-NULL literals because it is + // unnecessary and non-null literals as payload can be common. + if (!expr->Is() || + expr->GetAs()->value().is_null()) { + std::unique_ptr ifnull_expr; + std::vector> ifnull_args; + ifnull_args.push_back(std::move(expr)); + ifnull_args.push_back(MakeResolvedLiteral(/*ast_location=*/nullptr, + Value::StringValue("NULL"))); + ZETASQL_RETURN_IF_ERROR(ResolveFunctionCallWithResolvedArguments( + pipe_assert, /*arg_locations=*/{ast_message, pipe_assert}, + /*function_name=*/"IFNULL", std::move(ifnull_args), + /*named_arguments=*/{}, &expr_resolution_info, &ifnull_expr)); + expr = std::move(ifnull_expr); + } + message_expr_list.push_back(std::move(expr)); + } + + std::unique_ptr final_message_expr; + if (message_expr_list.empty()) { + // If there's no payload, construct a literal string message using + // the query text of the condition expression. + const ParseLocationRange& parse_range = + pipe_assert->condition()->GetParseLocationRange(); + const absl::string_view substr = + absl::ClippedSubstr(sql_, parse_range.start().GetByteOffset(), + parse_range.end().GetByteOffset() - + parse_range.start().GetByteOffset()); + + final_message_expr = MakeResolvedLiteral( + /*ast_location=*/nullptr, Value::StringValue(std::string(substr))); + } else if (message_expr_list.size() == 1) { + // The message is the single payload argument. + final_message_expr = std::move(message_expr_list[0]); + } else { + // Construct a CONCAT call to build the error message, with spaces. + std::vector> concat_args; + std::vector arg_locations; + + for (int i = 0; i < message_expr_list.size(); ++i) { + if (i > 0) { + concat_args.push_back(MakeResolvedLiteral(/*ast_location=*/nullptr, + Value::StringValue(" "))); + arg_locations.push_back(pipe_assert); + } + + concat_args.push_back(std::move(message_expr_list[i])); + arg_locations.push_back(pipe_assert->message_list(i)); + } + + ExprResolutionInfo expr_resolution_info(scope, "pipe ASSERT"); + + ZETASQL_RETURN_IF_ERROR(ResolveFunctionCallWithResolvedArguments( + pipe_assert, arg_locations, "CONCAT", std::move(concat_args), + /*named_arguments=*/{}, &expr_resolution_info, &final_message_expr)); + } + + ZETASQL_RET_CHECK(final_message_expr != nullptr); + ZETASQL_RET_CHECK(final_message_expr->type()->IsString()); + + ResolvedColumnList column_list = (*current_scan)->column_list(); + *current_scan = MakeResolvedAssertScan(column_list, std::move(*current_scan), + std::move(condition_expr), + std::move(final_message_expr)); + + analyzer_output_properties_.MarkRelevant(REWRITE_PIPE_ASSERT); + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeStaticDescribe( + const ASTPipeStaticDescribe* pipe_static_describe, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + if (!language().LanguageFeatureEnabled(FEATURE_PIPE_STATIC_DESCRIBE)) { + return MakeSqlErrorAt(pipe_static_describe) + << "Pipe STATIC_DESCRIBE not supported"; + } + + const std::string output = (*current_name_list)->DebugString(); + + // Make the no-op scan that holds the STATIC_DESCRIBE output. + const ResolvedScan* input_scan = current_scan->get(); + *current_scan = MakeResolvedStaticDescribeScan( + input_scan->column_list(), std::move(*current_scan), output); + + return absl::OkStatus(); +} + +// This is used for more helpful errors for pipe SET and pipe DROP to describe +// the NameTarget that doesn't work. +static const char* GetTargetKindNameForPipeSetOrDropError( + const NameTarget& target) { + std::string target_kind; + switch (target.kind()) { + case NameTarget::RANGE_VARIABLE: + return "a table alias"; + case NameTarget::FIELD_OF: + return "a field inside a value table"; + case NameTarget::IMPLICIT_COLUMN: + case NameTarget::EXPLICIT_COLUMN: + // This includes at least pseudo-columns and correlated columns. + // We don't have an easy way to tell what kind of column it is. + return "present but is not a column on the pipe input table"; + case NameTarget::AMBIGUOUS: + return "ambiguous"; + default: + return "present but is not a column"; + } +} + +// Shared helper for pipe DROP, RENAME and SET that fills in the map of +// columns to update, handling several error conditions. +// `item_struct_map` is map from target column name to a struct with at least +// these fields: +// struct ItemStruct { +// const ASTIdentifier* ast_identifier; // Target column name +// bool found; // false means not found yet. This will be updated. +// } +template +static absl::Status FillItemStructFromNameListColumns( + ItemStructMap* item_struct_map, const NameList& name_list, + const NameScope* scope, bool must_be_unambiguous_in_name_list, + bool must_be_unambiguous_in_scope, const std::string_view operator_name, + const std::string_view operator_verb) { + // For each NameList column, update `found` on items it matches. + for (const NamedColumn& named_column : name_list.columns()) { + auto* item_struct = zetasql_base::FindOrNull(*item_struct_map, named_column.name()); + if (item_struct != nullptr) { + if (must_be_unambiguous_in_name_list && item_struct->found) { + return MakeSqlErrorAt(item_struct->ast_identifier) + << "Column name in pipe " << operator_name + << " exists more than once in input table: " + << ToIdentifierLiteral(named_column.name()); + } + item_struct->found = true; + } + } + + // Handle item names that weren't found in the NameList. + for (const auto& map_entry : *item_struct_map) { + const auto& item_struct = map_entry.second; + + // Do a NameScope lookup if we might need it below. + NameTarget target; + bool found_in_scope = false; + if (!item_struct.found || must_be_unambiguous_in_scope) { + found_in_scope = scope->LookupName(map_entry.first, &target); + } + + // Give an error if we didn't find a matching column in the NameList. + // + // If a name was in the NameScope (a non-column or non-local column or + // ambiguity), the error will say what was found instead of a column. + // + // The second condition is used in RENAME. Since NameScope makes range + // variables always override columns, it never reports an ambiguity between + // columns and range variables. We searched the NameList for columns, + // which won't find the range variable that's in the scope, which + // the name would resolve to in expressions. To avoid confusion, we make it + // an error if a non-column was found in the NameScope lookup. + // + // This doesn't apply for SET and DROP, where it's allowed to drop/hide a + // range variable, as long as there was also a matching column in the + // NameList. + if (!item_struct.found || + (must_be_unambiguous_in_scope && !target.IsColumn())) { + if (found_in_scope) { + return MakeSqlErrorAt(item_struct.ast_identifier) + << "Name in pipe " << operator_name << " is " + << GetTargetKindNameForPipeSetOrDropError(target) << "; " + << operator_name << " can only " << operator_verb + << " columns: " << ToIdentifierLiteral(map_entry.first); + } else { + return MakeSqlErrorAt(item_struct.ast_identifier) + << "Column name in pipe " << operator_name + << " not found in input table: " + << ToIdentifierLiteral(map_entry.first); + } + } + } + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeDrop( + const ASTPipeDrop* pipe_drop, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + // Set of names to drop, for MergeOptions. + IdStringSetCase column_names_to_drop; + + // Map with additional info for the columns we intend to drop. + struct DropItem { + const ASTIdentifier* ast_identifier; + bool found; // false means not found yet. + }; + absl::flat_hash_map + columns_to_drop; + + // Build the set and map, checking the names are valid to drop. + for (const ASTIdentifier* identifier : + pipe_drop->column_list()->identifier_list()) { + const IdString column_name = identifier->GetAsIdString(); + if (IsInternalAlias(column_name)) { + return MakeSqlErrorAt(identifier) + << "Cannot use pipe DROP with internal alias " + << ToIdentifierLiteral(column_name); + } + if (!zetasql_base::InsertIfNotPresent(&columns_to_drop, column_name, + DropItem{identifier, /*found=*/false})) { + return MakeSqlErrorAt(identifier) + << "Duplicate column name in pipe DROP: " + << ToIdentifierLiteral(column_name); + } + column_names_to_drop.insert(column_name); + } + ZETASQL_RET_CHECK(!columns_to_drop.empty()); + + // Fill the map and check column names exist in the input table. + ZETASQL_RETURN_IF_ERROR(FillItemStructFromNameListColumns( + &columns_to_drop, **current_name_list, scope, + /*must_be_unambiguous_in_name_list=*/false, + /*must_be_unambiguous_in_scope=*/false, "DROP", "drop")); + + // Make the new NameList, dropping unwanted columns. Range variables and + // pseudo-columns in scope matching the name will also be dropped. + auto new_name_list = std::make_shared(); + ZETASQL_RETURN_IF_ERROR(new_name_list->MergeFrom( + **current_name_list, pipe_drop, + {.excluded_field_names = &column_names_to_drop})); + + if (new_name_list->num_columns() == 0) { + return MakeSqlErrorAt(pipe_drop) + << "Pipe DROP dropped all columns in the input table"; + } + + // We don't add a ProjectScan with a reduced column_list since that isn't + // necessary. Some columns may also still be referencable through the + // original range variables, even if they were dropped as top-level columns. + + *current_name_list = std::move(new_name_list); + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeRename( + const ASTPipeRename* pipe_rename, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + NameList::MergeOptions::ColumnsToRenameMap columns_to_rename_map; + + // Map with additional info for the columns we intend to rename. + struct RenameItem { + const ASTIdentifier* ast_identifier; + const ASTIdentifier* ast_new_identifier; + bool found; // false means not found yet. + }; + absl::flat_hash_map + columns_to_rename; + + // Build the set and map, checking the names are valid to drop. + for (const auto& rename_item : pipe_rename->rename_item_list()) { + const IdString old_name = rename_item->old_name()->GetAsIdString(); + const IdString new_name = rename_item->new_name()->GetAsIdString(); + if (IsInternalAlias(old_name)) { + return MakeSqlErrorAt(rename_item->old_name()) + << "Cannot use pipe RENAME with internal alias " + << ToIdentifierLiteral(old_name); + } + if (IsInternalAlias(new_name)) { + return MakeSqlErrorAt(rename_item->new_name()) + << "Cannot use pipe RENAME with internal alias " + << ToIdentifierLiteral(new_name); + } + + if (!zetasql_base::InsertIfNotPresent( + &columns_to_rename, old_name, + RenameItem{rename_item->old_name(), rename_item->new_name(), + /*found=*/false})) { + return MakeSqlErrorAt(rename_item->old_name()) + << "Duplicate column name in pipe RENAME: " + << ToIdentifierLiteral(old_name); + } + ZETASQL_RET_CHECK( + zetasql_base::InsertIfNotPresent(&columns_to_rename_map, old_name, new_name)); + } + ZETASQL_RET_CHECK(!columns_to_rename.empty()); + + // Fill the map and check column names exist in the input table. + ZETASQL_RETURN_IF_ERROR(FillItemStructFromNameListColumns( + &columns_to_rename, **current_name_list, scope, + /*must_be_unambiguous_in_name_list=*/true, + /*must_be_unambiguous_in_scope=*/true, "RENAME", "rename")); + + // Make the new NameList, applying the renames. + auto new_name_list = std::make_shared(); + ZETASQL_RETURN_IF_ERROR( + new_name_list->MergeFrom(**current_name_list, pipe_rename, + {.columns_to_rename = &columns_to_rename_map})); + + // We don't add a ProjectScan with a reduced column_list since that isn't + // necessary. Some columns may also still be referencable through the + // original range variables, even if they were dropped as top-level columns. + + *current_name_list = std::move(new_name_list); + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipeSet( + const ASTPipeSet* pipe_set, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + static constexpr char kSetClause[] = "pipe SET clause"; + + // Map of columns to replace in MergeFrom. + NameList::MergeOptions::ColumnsToReplaceMap columns_to_replace_map; + + // Map with additional info for the columns we intend to replace. + struct ReplaceItem { + const ASTIdentifier* ast_identifier; + bool found; // false means not found yet. + ResolvedColumn new_column; + }; + absl::flat_hash_map + columns_to_replace; + + const IdString scan_alias = MakeIdString("$pipe_set"); + std::vector> computed_columns; + + QueryResolutionInfo query_resolution_info(this); + query_resolution_info.analytic_resolver()->DisableNamedWindowRefs(kSetClause); + + // Build the two maps, checking the names are valid to replace. + for (const ASTPipeSetItem* ast_set_item : pipe_set->set_item_list()) { + const ASTIdentifier* identifier = ast_set_item->column(); + const IdString column_name = identifier->GetAsIdString(); + if (IsInternalAlias(column_name)) { + return MakeSqlErrorAt(identifier) + << "Cannot use pipe SET with internal alias " + << ToIdentifierLiteral(column_name); + } + + ExprResolutionInfo expr_resolution_info( + scope, &query_resolution_info, + /*top_level_ast_expr_in=*/ast_set_item->expression(), + /*column_alias_in=*/column_name, kSetClause); + + std::unique_ptr resolved_expr; + ZETASQL_RETURN_IF_ERROR(ResolveExpr(ast_set_item->expression(), + &expr_resolution_info, &resolved_expr)); + + ResolvedColumn column(AllocateColumnId(), scan_alias, column_name, + resolved_expr->annotated_type()); + computed_columns.push_back( + MakeResolvedComputedColumn(column, std::move(resolved_expr))); + + // Don't let the new column be pruned, even if not accessed. + // The SQLBuilder ends up not accessing the value if the column is pruned, + // and other engines may have the same issue. + RecordColumnAccess(column); + + if (!zetasql_base::InsertIfNotPresent( + &columns_to_replace, column_name, + ReplaceItem{identifier, /*found=*/false, column})) { + return MakeSqlErrorAt(identifier) << "Duplicate column name in pipe SET: " + << ToIdentifierLiteral(column_name); + } + ZETASQL_RET_CHECK( + zetasql_base::InsertIfNotPresent(&columns_to_replace_map, column_name, column)); + } + ZETASQL_RET_CHECK(!columns_to_replace.empty()); + + ZETASQL_RET_CHECK(!query_resolution_info.HasAggregation()); + ZETASQL_RETURN_IF_ERROR(query_resolution_info.CheckComputedColumnListsAreEmpty()); + if (query_resolution_info.HasAnalytic()) { + // Add an AnalyticScan if any window functions were present. + // This also makes a ProjectScan if necessary before the AnalyticScan. + ZETASQL_RETURN_IF_ERROR(AddAnalyticScan(&query_resolution_info, current_scan)); + } + + // Add the ProjectScan to compute the replacement columns. We don't prune + // the column_list here because it's not necessary, and because some + // some columns may also still be referencable through the original range + // variables, even if they were replaced as top-level columns. + ZETASQL_RETURN_IF_ERROR(MaybeAddProjectForComputedColumns(std::move(computed_columns), + current_scan)); + + // Fill the map and check column names exist in the input table. + ZETASQL_RETURN_IF_ERROR(FillItemStructFromNameListColumns( + &columns_to_replace, **current_name_list, scope, + /*must_be_unambiguous_in_name_list=*/true, + /*must_be_unambiguous_in_scope=*/false, "SET", "update")); + + // Make the new NameList, applying the updates. Range variables and + // pseudo-columns in scope matching the updated names will also be dropped. + auto new_name_list = std::make_shared(); + ZETASQL_RETURN_IF_ERROR(new_name_list->MergeFrom( + **current_name_list, pipe_set, + {.columns_to_replace = &columns_to_replace_map})); + + *current_name_list = std::move(new_name_list); + + return absl::OkStatus(); +} + +absl::Status Resolver::ResolvePipePivot( + const ASTPipePivot* pipe_pivot, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + // `input_is_subquery` is only used for producing errors in strict mode, + // which is not relevant here, so just pass true to avoid the errors. + return ResolvePivotClause(std::move(*current_scan), *current_name_list, scope, + /*input_is_subquery=*/true, + pipe_pivot->pivot_clause(), current_scan, + current_name_list); +} + +absl::Status Resolver::ResolvePipeUnpivot( + const ASTPipeUnpivot* pipe_unpivot, const NameScope* scope, + std::unique_ptr* current_scan, + std::shared_ptr* current_name_list) { + return ResolveUnpivotClause(std::move(*current_scan), *current_name_list, + scope, pipe_unpivot->unpivot_clause(), + current_scan, current_name_list); +} + static absl::Status VerifyNoLimitOrOrderByInRecursiveQuery( const ASTQuery* query) { if (query->order_by() != nullptr) { @@ -984,6 +1978,11 @@ absl::Status Resolver::AddAggregateScan( aggregate_func_call->function()->Is()) { analyzer_output_properties_.MarkRelevant(REWRITE_INLINE_SQL_UDAS); } + if (aggregate_func_call->order_by_item_list_size() > 0 || + aggregate_func_call->limit() != nullptr) { + analyzer_output_properties_.MarkRelevant( + REWRITE_ORDER_BY_AND_LIMIT_IN_AGGREGATE); + } } // If the feature is not enabled, any collation annotation that might exist on @@ -1010,8 +2009,9 @@ absl::Status Resolver::AddAggregateScan( ZETASQL_RETURN_IF_ERROR( ResolveHintsForNode(select->group_by()->hint(), aggregate_scan.get())); if (query_resolution_info->is_group_by_all()) { - analyzer_output_properties_.MarkTargetSyntax( - aggregate_scan.get(), SQLBuildTargetSyntax::kGroupByAll); + InternalAnalyzerOutputProperties::MarkTargetSyntax( + analyzer_output_properties_, aggregate_scan.get(), + SQLBuildTargetSyntax::kGroupByAll); } } @@ -1055,6 +2055,19 @@ absl::Status Resolver::AddAnonymizedAggregateScan( &resolved_anonymization_options)); } + for (const auto& aggregate_column : + query_resolution_info->aggregate_columns_to_compute()) { + if (aggregate_column->expr()->Is()) { + auto* aggregate_func_call = + aggregate_column->expr()->GetAs(); + if (aggregate_func_call->order_by_item_list_size() > 0 || + aggregate_func_call->limit() != nullptr) { + analyzer_output_properties_.MarkRelevant( + REWRITE_ORDER_BY_AND_LIMIT_IN_AGGREGATE); + } + } + } + switch (query_resolution_info->select_with_mode()) { case SelectWithMode::ANONYMIZATION: { auto anonymized_scan = MakeResolvedAnonymizedAggregateScan( @@ -1159,6 +2172,11 @@ Resolver::AddAggregationThresholdAggregateScan( aggregate_func_call->function()->Is()) { analyzer_output_properties_.MarkRelevant(REWRITE_INLINE_SQL_UDAS); } + if (aggregate_func_call->order_by_item_list_size() > 0 || + aggregate_func_call->limit() != nullptr) { + analyzer_output_properties_.MarkRelevant( + REWRITE_ORDER_BY_AND_LIMIT_IN_AGGREGATE); + } } // Add the aggregation threshold rewriter. analyzer_output_properties_.MarkRelevant(REWRITE_AGGREGATION_THRESHOLD); @@ -1230,6 +2248,12 @@ static absl::Status CreatePostGroupByNameScope( std::unique_ptr* post_group_by_scope_out) { const ValidFieldInfoMap* valid_field_info_map = &query_resolution_info->group_by_valid_field_info_map(); + ValidFieldInfoMap empty_map; + if (query_resolution_info->IsPipeAggregate()) { + // In pipe AGGREGATE, we pass an empty ValidFieldInfoMap because we + // don't want any non-aggregated column references to be valid. + valid_field_info_map = &empty_map; + } std::unique_ptr post_group_by_scope; ZETASQL_RETURN_IF_ERROR(pre_group_by_scope->CreateNameScopeGivenValidNamePaths( @@ -1436,7 +2460,7 @@ absl::Status Resolver::AddRemainingScansForSelect( ZETASQL_RETURN_IF_ERROR(MakeResolvedOrderByScan( order_by->hint(), query_resolution_info->GetResolvedColumnList(), {query_resolution_info->order_by_item_info()}, - current_scan)); + /*is_pipe_order_by=*/false, current_scan)); } if (order_by == nullptr && !select->distinct()) { @@ -1446,10 +2470,43 @@ absl::Status Resolver::AddRemainingScansForSelect( // with the original plans. We can probably do better about avoiding // unnecessary PROJECT nodes and delay projecting the non-aliased // expressions once this refactoring is submitted. + // That backwards compatibility issue does not apply for pipe operators. + // We do preserve this for pipe SELECT though, so we are consistent in + // always producing a ProjectScan reflecting the output schema. + if (!query_resolution_info->select_list_columns_to_compute()->empty() || + !query_resolution_info->IsPipeOp() || + query_resolution_info->IsPipeSelect()) { + // For pipe EXTEND, if projected columns are pruned from the column_list, + // the SQLBuilder will fail. + if (query_resolution_info->IsPipeExtend()) { + for (const auto& computed_column : + *query_resolution_info->select_list_columns_to_compute()) { + RecordColumnAccess(computed_column->column()); + } + } + *current_scan = MakeResolvedProjectScan( query_resolution_info->GetResolvedColumnList(), query_resolution_info->release_select_list_columns_to_compute(), std::move(*current_scan)); + } + } + + // If we have ordering from the AGGREGATE list or GROUP BY (in pipe + // AGGREGATE), add the ResolvedOrderByScan for that. + if (!query_resolution_info->aggregate_order_by_item_info().empty() || + !query_resolution_info->group_by_order_by_item_info().empty()) { + ZETASQL_RET_CHECK(query_resolution_info->order_by_item_info().empty()); + + // All columns we need to order on already exist, so we don't need + // to compute any additional expressions. + // The order columns include GROUP BY items first and then AGGREGATE items. + ZETASQL_RETURN_IF_ERROR(MakeResolvedOrderByScan( + /*order_by_hint=*/nullptr, + query_resolution_info->GetResolvedColumnList(), + {query_resolution_info->group_by_order_by_item_info(), + query_resolution_info->aggregate_order_by_item_info()}, + /*is_pipe_order_by=*/true, current_scan)); } if (limit_offset != nullptr) { @@ -1457,6 +2514,16 @@ absl::Status Resolver::AddRemainingScansForSelect( limit_offset, having_and_order_by_scope, current_scan)); } + // Check column count here, because if there is SELECT AS STRUCT or + // SELECT AS PROTO then the column counts will no longer match. + // This doesn't work for pipe EXTEND or WINDOW because the column lists may + // include pseudo-columns, which aren't counted in NameList::num_columns. + if (!query_resolution_info->IsPipeExtendOrWindow()) { + ZETASQL_RET_CHECK_EQ(query_resolution_info->pipe_extra_select_items()->size() + + select_column_state_list->Size(), + (*output_name_list)->num_columns()); + } + if (select->select_as() != nullptr) { ZETASQL_RETURN_IF_ERROR( ResolveSelectAs(select->select_as(), *select_column_state_list, @@ -1527,10 +2594,14 @@ absl::Status Resolver::AddColumnsForOrderByExprs( return absl::OkStatus(); } +// This is currently used for ORDER BY after set operations and for +// pipe ORDER BY. Resolution of ORDER BY for SELECT is done elsewhere since +// it gets resolved in two phases. absl::Status Resolver::ResolveOrderBySimple( const ASTOrderBy* order_by, const NameScope* scope, const char* clause_name, OrderBySimpleMode mode, std::unique_ptr* scan) { - ZETASQL_RET_CHECK(mode == OrderBySimpleMode::kNormal // + ZETASQL_RET_CHECK(mode == OrderBySimpleMode::kNormal || + mode == OrderBySimpleMode::kPipes ); // We use a new QueryResolutionInfo because resolving the ORDER BY @@ -1542,6 +2613,8 @@ absl::Status Resolver::ResolveOrderBySimple( query_resolution_info->analytic_resolver()->DisableNamedWindowRefs( clause_name); + bool is_pipes = mode == OrderBySimpleMode::kPipes; + ExprResolutionInfo expr_resolution_info( scope, scope, scope, /*allows_aggregation_in=*/false, /*allows_analytic_in=*/true, @@ -1586,16 +2659,20 @@ absl::Status Resolver::ResolveOrderBySimple( return MakeResolvedOrderByScan(order_by->hint(), output_columns, {query_resolution_info->order_by_item_info()}, - scan); + is_pipes, scan); } absl::Status Resolver::ResolveOrderByItems( const std::vector& output_column_list, - const OrderByItemInfoVectorList& order_by_info_lists, + const OrderByItemInfoVectorList& order_by_info_lists, bool is_pipe_order_by, std::vector>* resolved_order_by_items) { resolved_order_by_items->clear(); + // There is always exactly one vector of OrderByInfo for standard ORDER BY + // and for pipe ORDER BY. + // For pipe AGGREGATE, there can be two vectors, from the GROUP BY and + // AGGREGATE lists, and the nested loops will concatenate them. ZETASQL_RET_CHECK(!order_by_info_lists.empty()); for (const auto& order_by_info_vector : order_by_info_lists) { for (const OrderByItemInfo& item_info : order_by_info_vector.get()) { @@ -1603,6 +2680,15 @@ absl::Status Resolver::ResolveOrderByItems( if (item_info.is_select_list_index()) { if (item_info.select_list_index < 0 || item_info.select_list_index >= output_column_list.size()) { + if (is_pipe_order_by) { + // Note that the error for ordinal < 1 is produced earlier, in + // ResolveOrderByExprs. + return MakeSqlErrorAt(item_info.ast_location) + << "ORDER BY column number exceeds input table column " + "count: " + << item_info.select_list_index + 1 << " vs " + << output_column_list.size(); + } return MakeSqlErrorAt(item_info.ast_location) << "ORDER BY is out of SELECT column number range: " << item_info.select_list_index + 1; @@ -1628,6 +2714,8 @@ absl::Status Resolver::ResolveOrderByItems( auto resolved_order_by_item = MakeResolvedOrderByItem( std::move(resolved_column_ref), std::move(resolved_collation_name), item_info.is_descending, item_info.null_order); + resolved_order_by_item->SetParseLocationRange( + item_info.ast_location->GetParseLocationRange()); if (language().LanguageFeatureEnabled(FEATURE_V_1_3_COLLATION_SUPPORT)) { ZETASQL_RETURN_IF_ERROR( CollationAnnotation::ResolveCollationForResolvedOrderByItem( @@ -1655,14 +2743,14 @@ absl::Status Resolver::ResolveOrderByItems( absl::Status Resolver::MakeResolvedOrderByScan( const ASTHint* order_by_hint, const std::vector& output_column_list, - const OrderByItemInfoVectorList& order_by_info_lists, + const OrderByItemInfoVectorList& order_by_info_lists, bool is_pipe_order_by, std::unique_ptr* scan) { std::vector> resolved_order_by_items; - ZETASQL_RETURN_IF_ERROR( - ResolveOrderByItems(output_column_list, order_by_info_lists, - &resolved_order_by_items)); + ZETASQL_RETURN_IF_ERROR(ResolveOrderByItems(output_column_list, order_by_info_lists, + is_pipe_order_by, + &resolved_order_by_items)); std::unique_ptr order_by_scan = zetasql::MakeResolvedOrderByScan(output_column_list, std::move(*scan), @@ -1699,6 +2787,13 @@ absl::Status Resolver::ResolveQueryExpression( return ResolveQuery(query_expr->GetAsOrDie(), scope, query_alias, /*is_outer_query=*/false, output, output_name_list, inferred_type_for_query); + case AST_ALIASED_QUERY_EXPRESSION: + return ResolveAliasedQueryExpression( + query_expr->GetAsOrDie(), scope, output, + output_name_list, inferred_type_for_query); + case AST_FROM_QUERY: + return ResolveFromQuery(query_expr->GetAsOrDie(), scope, + output, output_name_list); default: break; @@ -1708,12 +2803,77 @@ absl::Status Resolver::ResolveQueryExpression( << query_expr->DebugString(); } +absl::Status Resolver::ResolveAliasedQueryExpression( + const ASTAliasedQueryExpression* aliased_query, const NameScope* scope, + std::unique_ptr* output, + std::shared_ptr* output_name_list, + const Type* inferred_type_for_query) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + + const IdString alias = aliased_query->alias()->GetAsIdString(); + + std::shared_ptr query_name_list; + ZETASQL_RETURN_IF_ERROR(ResolveQuery(aliased_query->query(), scope, alias, + /*is_outer_query=*/false, output, + &query_name_list, inferred_type_for_query)); + + *output_name_list = std::make_shared(); + ZETASQL_RETURN_IF_ERROR(UpdateNameListForTableAlias( + aliased_query, alias, query_name_list, output_name_list)); + + return absl::OkStatus(); +} + +// Note: inferred_type_for_query is not passed in. In normal queries, it +// never propagates into a FROM clause. For FROM queries with just a single +// table subquery, it could be applied, but this seems like an obscure case +// since there is little reason to write `FROM (SELECT ...);`. +absl::Status Resolver::ResolveFromQuery( + const ASTFromQuery* from_query, const NameScope* scope, + std::unique_ptr* output, + std::shared_ptr* output_name_list) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + + // The parser won't let ASTFromQuery through if the feature is disabled. + ZETASQL_RET_CHECK(language().LanguageFeatureEnabled(FEATURE_PIPES)); + + const ASTFromClause* from_clause = from_query->from_clause(); + ZETASQL_RET_CHECK(from_clause != nullptr); + ZETASQL_RET_CHECK(from_clause->table_expression() != nullptr); + + std::shared_ptr name_list; + ZETASQL_RETURN_IF_ERROR(ResolveTableExpression( + from_clause->table_expression(), + /*external_scope=*/scope, /*local_scope=*/scope, output, &name_list)); + + // If we have a non-value-table NameList with exactly one column, and + // that column is a value table column, then we make a new NameList marked + // as a value table. This will make the final query result a value + // table if this NameList becomes the final result. + // + // Value table resolution is inconsistent in ResolveTableExpression's cases. + // Some cases set is_value_table on the outer NameList and some don't. + if (!name_list->is_value_table() && name_list->num_columns() == 1 && + name_list->column(0).is_value_table_column()) { + std::shared_ptr new_name_list(new NameList); + + ZETASQL_RETURN_IF_ERROR(new_name_list->MergeFrom(*name_list, from_clause)); + + ZETASQL_RETURN_IF_ERROR(new_name_list->SetIsValueTable()); + name_list = new_name_list; + } + + *output_name_list = name_list; + return absl::OkStatus(); +} + absl::Status Resolver::ResolveAdditionalExprsSecondPass( const NameScope* from_clause_or_group_by_scope, QueryResolutionInfo* query_resolution_info) { for (const auto& entry : *query_resolution_info ->dot_star_columns_with_aggregation_for_second_pass_resolution()) { + ZETASQL_RET_CHECK(!query_resolution_info->IsPipeOp()); // Re-resolve the source expression for the dot-star expressions that // contain aggregation. ExprResolutionInfo expr_resolution_info( @@ -1922,6 +3082,45 @@ absl::Status Resolver::ResolveSelect( // // For a more detailed discussion, see (broken link). // +// For some pipe operators (listed below), this is called from methods other +// than ResolveSelect. We reuse this function because we are trying to share +// all the complex functionality for the SELECT list and GROUP BY and don't +// want two forks of that code. +// +// Resolving code for these clauses is not easily separable from others +// due to complex entanglement of SELECT, aggregation, window functions, +// etc in classic SQL queries. For pipe operators, the logic becomes +// mostly no-ops since none of the other clauses are present. +// +// For pipe operators, the input scan and NameList come from the input pipe +// instead of a FROM clause. The SelectForm enum in QueryResolutionInfo +// records which case we're in, and is used to restrict or modify behavior +// and give better error messages earlier. +// +// For pipe SELECT, called from ResolvePipeSelect: +// * The ASTSelect has a SELECT list and nothing else. +// * It resolves similarly to no-from-clause SELECTs. e.g. No aggregation. +// * The result is one or more ProjectScans, and nothing else. +// +// For pipe AGGREGATE, called from ResolvePipeAggregate: +// * The ASTSelect just has a SELECT list (representing aggregate expressions) +// and optionally a GROUP BY. +// * The SELECT list may be empty (if the GROUP BY is non-empty). +// * Expressions must include aggregation. +// * Window functions are not allowed. +// * The final output includes the GROUP BY columns, followed by the SELECT +// columns. +// * Name scoping is simplified so the SELECT and GROUP BY expressions are +// independent and cannot see each other. +// * The result includes an AggregateScan, and sometimes ProjectScans. +// +// For pipe WINDOW, called from ResolvePipeWindow: +// * The ASTSelect just has a SELECT list (representing window expressions). +// * Expressions must include a window function. +// * Aggregate functions are not allowed. +// * The operator projects new columns onto an existing dataset, so the final +// output includes the columns from the input NameList. +// * The result includes an AnalyticScan, and sometimes ProjectScans. absl::Status Resolver::ResolveSelectAfterFrom( const ASTSelect* select, const ASTOrderBy* order_by, const ASTLimitOffset* limit_offset, const NameScope* external_scope, @@ -1969,6 +3168,22 @@ absl::Status Resolver::ResolveSelectAfterFrom( // before SELECT). query_resolution_info->set_from_clause_name_list(from_clause_name_list); + // For pipe EXTEND and WINDOW, all columns and names from the input pass + // through, including pseudo-columns and range variables, so we preserve the + // full input column_list. We will also preserve the input NameList, + // so the names from this list won't be used. + if (query_resolution_info->IsPipeExtendOrWindow()) { + const IdString unused_name = MakeIdString("$unused_name"); + for (const ResolvedColumn& column : (*scan)->column_list()) { + query_resolution_info->pipe_extra_select_items()->push_back( + PipeExtraSelectItem(unused_name, column)); + } + } + if (query_resolution_info->IsPipeOp()) { + query_resolution_info->analytic_resolver()->DisableNamedWindowRefs( + query_resolution_info->SelectFormClauseName()); + } + // We avoid passing down the inferred type to the SELECT list if we have a // SELECT AS query. const Type* inferred_type_for_select_list = nullptr; @@ -2087,6 +3302,18 @@ absl::Status Resolver::ResolveSelectAfterFrom( } if (select->group_by() != nullptr) { + if (select->group_by()->and_order_by()) { + if (!query_resolution_info->IsPipeAggregate()) { + return MakeSqlErrorAt(select->group_by()) + << "GROUP AND ORDER BY is not supported outside pipe AGGREGATE"; + } + if (order_by != nullptr) { + return MakeSqlErrorAt(select->group_by()) + << "Cannot use GROUP AND ORDER BY in a query that also " + "has ORDER BY"; + } + query_resolution_info->set_group_by_has_and_order_by(); + } if (select->group_by()->all() != nullptr) { ZETASQL_RETURN_IF_ERROR(ResolveGroupByAll(select->group_by(), from_scan_scope.get(), @@ -2120,6 +3347,11 @@ absl::Status Resolver::ResolveSelectAfterFrom( if (!query_resolution_info->SelectFormAllowsAnalytic()) { ZETASQL_RET_CHECK(!query_resolution_info->HasAnalytic()); } + if (query_resolution_info->IsPipeAggregate()) { + ZETASQL_RET_CHECK(query_resolution_info->HasGroupByOrAggregation()); + } else if (query_resolution_info->IsPipeWindow()) { + ZETASQL_RET_CHECK(query_resolution_info->HasAnalytic()); + } if (!query_resolution_info->HasGroupByOrAggregation() && !query_resolution_info->HasAnalytic()) { @@ -2176,6 +3408,21 @@ absl::Status Resolver::ResolveSelectAfterFrom( std::shared_ptr final_project_name_list(new NameList); + if (query_resolution_info->IsPipeExtendOrWindow()) { + // For pipe EXTEND and WINDOW, we preserve all existing names from the + // input table, including pseudo-columns and range variables. + ZETASQL_RETURN_IF_ERROR( + final_project_name_list->MergeFrom(*from_clause_name_list, select)); + } else { + // Add the columns from pipe AGGREGATE group by items into the output + // name list. They'll show up first, before the aggregate columns. + for (const PipeExtraSelectItem& item : + *query_resolution_info->pipe_extra_select_items()) { + ZETASQL_RETURN_IF_ERROR(final_project_name_list->AddColumn( + item.alias, item.column, /*is_explicit=*/true)); + } + } + ZETASQL_RETURN_IF_ERROR(ResolveSelectListExprsSecondPass( query_alias, from_clause_or_group_by_scope, &final_project_name_list, query_resolution_info.get())); @@ -2272,9 +3519,13 @@ absl::Status Resolver::ResolveSelectAfterFrom( &resolved_having_expr, &resolved_qualify_expr, query_resolution_info.get(), output_name_list, scan)); + // Exclude pipe EXTEND and WINDOW because they can include pseudo-columns, + // which should be prunable. + if (!query_resolution_info->IsPipeExtendOrWindow()) { // Any columns produced in a SELECT list (for the final query or any // subquery) count as referenced and cannot be pruned. RecordColumnAccess((*scan)->column_list()); + } // Some sanity checks. // Note that we cannot check that the number of columns in the @@ -3227,6 +4478,8 @@ absl::Status Resolver::ResolveSelectStarModifiers( // Override the ast_expr to point at the replacement expression // rather than `ast_select_column`, which points at the star expression. select_column_state->ast_expr = ast_replace_item->expression(); + // If order suffixes become possible here, make sure we get the right one. + ZETASQL_RET_CHECK(select_column_state->ast_grouping_item_order == nullptr); if (!column_replacements->replaced_columns .emplace(identifier, std::move(select_column_state)) @@ -3610,6 +4863,28 @@ absl::Status Resolver::AddColumnFieldsToSelectList( absl::Status Resolver::CheckExprResolutionInfoForQuery( const ASTNode* ast_location, QueryResolutionInfo* query_resolution_info, const ExprResolutionInfo& expr_resolution_info) { + // Errors for expressions that cannot occur are raised during ResolveExpr, + // with specific locations. + // Errors for missing required expression types are raised here. + if (query_resolution_info->IsPipeSelect() || + query_resolution_info->IsPipeExtend()) { + ZETASQL_RET_CHECK(!expr_resolution_info.has_aggregation); + } else if (query_resolution_info->IsPipeAggregate()) { + ZETASQL_RET_CHECK(!expr_resolution_info.has_analytic); + if (!expr_resolution_info.has_aggregation) { + return MakeSqlErrorAt(ast_location) + << "Pipe AGGREGATE cannot include non-aggregate expressions"; + } + } else if (query_resolution_info->IsPipeWindow()) { + // Note that ResolveSelectDotStar exits early above, so this error + // check needs to be duplicated in that function. + ZETASQL_RET_CHECK(!expr_resolution_info.has_aggregation); + if (!expr_resolution_info.has_analytic) { + return MakeSqlErrorAt(ast_location) + << "Pipe WINDOW expression must include a window function " + "call (with an OVER clause)"; + } + } return absl::OkStatus(); } @@ -3623,6 +4898,12 @@ absl::Status Resolver::ResolveSelectColumnFirstPass( ZETASQL_RET_CHECK_LE(select_column_state_list_write_idx, query_resolution_info->select_column_state_list()->Size()); + // The parser can only produce ordering suffixes in ASTSelectColumns + // produced for pipe AGGREGATE. + if (!query_resolution_info->IsPipeAggregate()) { + ZETASQL_RET_CHECK(ast_select_column->grouping_item_order() == nullptr); + } + const ASTExpression* ast_select_expr = ast_select_column->expression(); switch (ast_select_expr->node_kind()) { @@ -3878,6 +5159,7 @@ absl::Status Resolver::HandleGroupBySelectColumn( QueryResolutionInfo* query_resolution_info, std::unique_ptr* resolved_expr, const ResolvedExpr** pre_group_by_expr, ResolvedColumn* group_by_column) { + ZETASQL_RET_CHECK(!query_resolution_info->IsPipeOp()); // If this SELECT list column is already being grouped by then we should // not be calling this. @@ -4299,13 +5581,15 @@ absl::Status Resolver::ResolveGroupingSetExpressions( ZETASQL_RET_CHECK(field_expression != nullptr); ZETASQL_RETURN_IF_ERROR(ResolveGroupingItemExpression( field_expression, - from_clause_scope, + /*ast_alias=*/nullptr, + /*ast_grouping_item_order=*/nullptr, from_clause_scope, /*from_grouping_set=*/true, query_resolution_info, &column_list)); } } else { ZETASQL_RETURN_IF_ERROR(ResolveGroupingItemExpression( expr, - from_clause_scope, + /*ast_alias=*/nullptr, + /*ast_grouping_item_order=*/nullptr, from_clause_scope, /*from_grouping_set=*/true, query_resolution_info, &column_list)); } grouping_set_info.grouping_set_item_list.push_back(column_list); @@ -4328,9 +5612,21 @@ absl::Status Resolver::ResolveGroupByExprs( bool has_rollup_or_cube = false; for (const ASTGroupingItem* grouping_item : group_by->grouping_items()) { + if (grouping_item->alias() != nullptr) { + // The parser only allows aliases on expressions. + ZETASQL_RET_CHECK(grouping_item->expression() != nullptr); + } + if (grouping_item->grouping_item_order() != nullptr) { + // The parser only allows ASC/DESC ordering suffixes on expressions. + ZETASQL_RET_CHECK(grouping_item->expression() != nullptr); + } if (grouping_item->rollup() != nullptr) { // GROUP BY ROLLUP + if (query_resolution_info->group_by_has_and_order_by()) { + return MakeSqlErrorAt(grouping_item) + << "GROUP AND ORDER BY cannot be used with ROLLUP"; + } has_rollup_or_cube = true; const ASTRollup* rollup = grouping_item->rollup(); ZETASQL_RETURN_IF_ERROR(ValidateRollup(rollup, language(), @@ -4343,6 +5639,10 @@ absl::Status Resolver::ResolveGroupByExprs( break; } else if (grouping_item->cube() != nullptr) { // GROUP BY CUBE + if (query_resolution_info->group_by_has_and_order_by()) { + return MakeSqlErrorAt(grouping_item) + << "GROUP AND ORDER BY cannot be used with CUBE"; + } has_rollup_or_cube = true; const ASTCube* cube = grouping_item->cube(); ZETASQL_RETURN_IF_ERROR( @@ -4352,6 +5652,10 @@ absl::Status Resolver::ResolveGroupByExprs( query_resolution_info)); } else if (grouping_item->grouping_set_list() != nullptr) { // GROUP BY GROUPING SETS + if (query_resolution_info->group_by_has_and_order_by()) { + return MakeSqlErrorAt(grouping_item) + << "GROUP AND ORDER BY cannot be used with GROUPING SETS"; + } const ASTGroupingSetList* grouping_set_list = grouping_item->grouping_set_list(); ZETASQL_RETURN_IF_ERROR(ValidateGroupingSetList( @@ -4398,9 +5702,17 @@ absl::Status Resolver::ResolveGroupByExprs( } } else if (grouping_item->expression() != nullptr) { // GROUP BY expressions + if (grouping_item->grouping_item_order() != nullptr) { + if (!query_resolution_info->IsPipeAggregate()) { + return MakeSqlErrorAt(grouping_item->grouping_item_order()) + << "GROUP BY does not support order specification outside " + "pipe AGGREGATE"; + } + } + ZETASQL_RETURN_IF_ERROR(ResolveGroupingItemExpression( - grouping_item->expression(), - from_clause_scope, + grouping_item->expression(), grouping_item->alias(), + grouping_item->grouping_item_order(), from_clause_scope, /*from_grouping_set=*/false, query_resolution_info)); } else { // This is GROUP BY () @@ -4414,7 +5726,16 @@ absl::Status Resolver::ResolveGroupByExprs( } else { return MakeSqlErrorAt(grouping_item) << "GROUP BY () is not supported"; } + if (query_resolution_info->group_by_has_and_order_by()) { + return MakeSqlErrorAt(grouping_item) + << "GROUP AND ORDER BY cannot be used with GROUP BY ()"; + } } + + // GROUP AND ORDER BY is only allowed on expression items. + // Errors should have been produced in all other cases above. + ZETASQL_RET_CHECK(grouping_item->expression() != nullptr || + !query_resolution_info->group_by_has_and_order_by()); } if (has_rollup_or_cube && language().LanguageFeatureEnabled(FEATURE_V_1_4_GROUPING_SETS)) { @@ -4463,8 +5784,14 @@ absl::Status Resolver::ResolveGroupByExprs( // 7) Add a ResolvedComputedColumn for the GROUP BY expression. // 8) If the expression is from a grouping set, then add the // ResolvedComputedColumn to the passed-in GroupingSetItem. +// 9) For pipe aggregate, record the item in PipeExtraSelectItems so the column +// can be added to the output. +// 10) If adding an implicit order by for this GROUP BY column, add an entry +// to group_by_order_by_item_info. absl::Status Resolver::ResolveGroupingItemExpression( const ASTExpression* ast_group_by_expr, + const ASTAlias* ast_alias, + const ASTGroupingItemOrder* ast_grouping_item_order, const NameScope* from_clause_scope, bool from_grouping_set, QueryResolutionInfo* query_resolution_info, ResolvedComputedColumnList* column_list) { @@ -4481,6 +5808,10 @@ absl::Status Resolver::ResolveGroupingItemExpression( const SelectColumnState* group_by_column_state = nullptr; // Determine if the GROUP BY expression exactly matches a SELECT list alias. if (ast_group_by_expr->node_kind() == AST_PATH_EXPRESSION + // For pipe AGGREGATE, we bypass this case that matches GROUP BY + // expressions against SELECT-list aliases. We don't want aliases + // of aggregate expressions to be referencable in the GROUP BY. + && !query_resolution_info->IsPipeAggregate() ) { const ASTPathExpression* path_expr = ast_group_by_expr->GetAsOrDie(); @@ -4527,6 +5858,10 @@ absl::Status Resolver::ResolveGroupingItemExpression( !resolved_expr->GetAs()->has_explicit_type()) { const Value& value = resolved_expr->GetAs()->value(); if (value.type_kind() == TYPE_INT64 && !value.is_null()) { + if (query_resolution_info->IsPipeAggregate()) { + return MakeSqlErrorAt(ast_group_by_expr) + << "GROUP BY ordinal not allowed in pipe AGGREGATE"; + } ZETASQL_RETURN_IF_ERROR(query_resolution_info->select_column_state_list() ->FindAndValidateSelectColumnStateByOrdinal( /*expr_description=*/"GROUP BY", @@ -4537,11 +5872,18 @@ absl::Status Resolver::ResolveGroupingItemExpression( } IdString alias = GetAliasForExpression(ast_group_by_expr); + if (ast_alias != nullptr) { + if (!query_resolution_info->IsPipeAggregate()) { + return MakeSqlErrorAt(ast_alias) << "GROUP BY does not support aliases"; + } + alias = ast_alias->GetAsIdString(); + } else { if (alias.empty()) { alias = MakeIdString(absl::StrCat( "$groupbycol", query_resolution_info->group_by_column_state_list().size() + 1)); } + } ResolvedColumn group_by_column; // This means the current group by column is a select column alias. @@ -4551,6 +5893,9 @@ absl::Status Resolver::ResolveGroupingItemExpression( // to do more unless the query uses GROUPING SETS, in which case we need // to add another entry in the grouping set list for it. if (!from_grouping_set) { + // This early exit seems unreachable, so hasn't been tested. + // We don't want by bypass PipeExtraSelectItem tracking below. + ZETASQL_RET_CHECK(!query_resolution_info->IsPipeAggregate()); return absl::OkStatus(); } @@ -4622,6 +5967,45 @@ absl::Status Resolver::ResolveGroupingItemExpression( column_list->push_back(computed_column); } + // For pipe AGGREGATE, record the GROUP BY item, which will become an + // output column in the result table. + if (query_resolution_info->IsPipeAggregate()) { + query_resolution_info->pipe_extra_select_items()->push_back( + PipeExtraSelectItem(alias, group_by_column)); + } + + // Set up the ordering column if we have GROUP AND ORDER BY or the + // expression has ASC/DESC attached. + if (ast_grouping_item_order != nullptr || + query_resolution_info->group_by_has_and_order_by()) { + ResolvedOrderByItemEnums::NullOrderMode null_order = + ResolvedOrderByItemEnums::ORDER_UNSPECIFIED; + if (ast_grouping_item_order != nullptr) { + if (!query_resolution_info->group_by_has_and_order_by() && + ast_grouping_item_order->null_order() != nullptr && + ast_grouping_item_order->ordering_spec() == + ASTOrderingExpression::UNSPECIFIED) { + return MakeSqlErrorAt(ast_grouping_item_order->null_order()) + << "GROUP BY items cannot have NULLS FIRST/LAST without " + "ASC/DESC except when using GROUP AND ORDER BY"; + } + ZETASQL_ASSIGN_OR_RETURN(null_order, ResolveNullOrderMode( + ast_grouping_item_order->null_order())); + } + + // We will order by directly, rather than a + // ResolvedExpression. AddColumnsForOrderByExprs (which normally adds + // these columns) will not be called on this vector. + query_resolution_info->mutable_group_by_order_by_item_info()->emplace_back( + ast_group_by_expr, + /*ast_collate_in=*/nullptr, /*order_column=*/group_by_column, + /*is_descending=*/ + (ast_grouping_item_order != nullptr + ? ast_grouping_item_order->descending() + : false), + null_order); + } + return absl::OkStatus(); } @@ -4737,10 +6121,14 @@ absl::Status Resolver::ResolveSelectColumnSecondPass( } } else { const char* clause_name = "SELECT list"; + if (query_resolution_info->IsPipeAggregate()) { + clause_name = "AGGREGATE list"; + } ExprResolutionInfo expr_resolution_info( group_by_scope, group_by_scope, group_by_scope, /*allows_aggregation_in=*/true, /*allows_analytic_in=*/true, + /*use_post_grouping_columns_in=*/ query_resolution_info->HasGroupByOrAggregation(), clause_name, query_resolution_info, select_column_state->ast_expr, select_column_state->alias); @@ -4753,6 +6141,10 @@ absl::Status Resolver::ResolveSelectColumnSecondPass( // expression that exactly matches the ResolvedExpr from the // first pass resolution. bool found_group_by_expression = false; + // I didn't find any cases where the loop can find a match, but I don't + // think we ever want matches here anyway, so I'm skipping the search + // rather than asserting it finds nothing. + if (!query_resolution_info->IsPipeAggregate()) { for (const GroupByColumnState& group_by_column_state : query_resolution_info->group_by_column_state_list()) { ZETASQL_ASSIGN_OR_RETURN( @@ -4771,6 +6163,7 @@ absl::Status Resolver::ResolveSelectColumnSecondPass( break; } } + } if (!found_group_by_expression) { // TODO: Improve error message to say that expressions didn't // match. @@ -4802,6 +6195,24 @@ absl::Status Resolver::ResolveSelectColumnSecondPass( select_column_state->resolved_select_column, select_column_state->is_explicit)); + // If the select item has an ordering suffix (which can occur only in + // pipe AGGREGATE), add the OrderByItemInfo for it, pointing at the + // post-aggregate ResolvedColumn. + const ASTGroupingItemOrder* ast_grouping_item_order = + select_column_state->ast_grouping_item_order; + if (ast_grouping_item_order != nullptr) { + ZETASQL_RET_CHECK(query_resolution_info->IsPipeAggregate()); + + ZETASQL_ASSIGN_OR_RETURN( + const ResolvedOrderByItemEnums::NullOrderMode null_order, + ResolveNullOrderMode(ast_grouping_item_order->null_order())); + + query_resolution_info->mutable_aggregate_order_by_item_info()->emplace_back( + select_column_state->ast_expr, + /*ast_collate_in=*/nullptr, select_column_state->resolved_select_column, + ast_grouping_item_order->descending(), null_order); + } + return absl::OkStatus(); } @@ -7188,11 +8599,10 @@ absl::Status Resolver::ResolveTableExpression( table_expr->GetAsOrDie(), external_scope, local_scope, output, output_name_list); - case AST_TVF: { - std::vector tvf_args; + case AST_TVF: return ResolveTVF(table_expr->GetAsOrDie(), external_scope, - local_scope, &tvf_args, output, output_name_list); - } + local_scope, /*pipe_input_arg=*/nullptr, output, + output_name_list); default: return MakeSqlErrorAt(table_expr) @@ -8278,6 +9688,12 @@ absl::Status Resolver::ResolveTableSubquery( // but it doesn't have a value table column. This happens for UNION // of value tables, for one example. Calling this function // builds a NameList containing a value table column. + // TODO This should be fixed by setting up the value table + // NameList properly in the first place, rather than fixing it when + // handling a table subquery. If we have a pipe operator after this + // input table without a table subquery, the value table won't work. + // e.g. The test with a parenthesized UNION ALL producing a value + // table exposes this problem. auto new_name_list = std::make_shared(); ZETASQL_RETURN_IF_ERROR(ConvertValueTableNameListToNameListWithValueTable( table_ref, alias, subquery_name_list, &new_name_list)); @@ -9329,8 +10745,7 @@ absl::StatusOr FindTVFOrMakeNiceError( absl::Status Resolver::ResolveTVF( const ASTTVF* ast_tvf, const NameScope* external_scope, - const NameScope* local_scope, - std::vector* resolved_tvf_args, + const NameScope* local_scope, ResolvedTVFArg* pipe_input_arg, std::unique_ptr* output, std::shared_ptr* output_name_list) { RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); @@ -9359,13 +10774,15 @@ absl::Status Resolver::ResolveTVF( FindTVFOrMakeNiceError(tvf_name_string, ast_tvf, analyzer_options_, catalog_)); + std::vector resolved_tvf_args; std::unique_ptr result_signature; std::vector tvf_input_arguments; ZETASQL_RETURN_IF_ERROR(PrepareTVFInputArguments( tvf_name_string, ast_tvf, tvf_catalog_entry, external_scope, local_scope, - &result_signature, resolved_tvf_args, &tvf_input_arguments)); + pipe_input_arg, &result_signature, &resolved_tvf_args, + &tvf_input_arguments)); - ZETASQL_RET_CHECK_EQ(resolved_tvf_args->size(), tvf_input_arguments.size()); + ZETASQL_RET_CHECK_EQ(resolved_tvf_args.size(), tvf_input_arguments.size()); // Call the TableValuedFunction::Resolve method to get the output schema. // Use a new empty cycle detector, or the cycle detector from an enclosing @@ -9471,7 +10888,7 @@ absl::Status Resolver::ResolveTVF( // Create the resolved TVF scan. std::vector final_resolved_tvf_args; - for (ResolvedTVFArg& arg : *resolved_tvf_args) { + for (ResolvedTVFArg& arg : resolved_tvf_args) { if (arg.IsExpr()) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr expr, arg.MoveExpr()); @@ -9593,13 +11010,15 @@ absl::Status Resolver::ResolveTVF( absl::StatusOr Resolver::MatchTVFSignature( const ASTTVF* ast_tvf, const TableValuedFunction* tvf_catalog_entry, const NameScope* external_scope, const NameScope* local_scope, - const FunctionResolver& function_resolver, + const FunctionResolver& function_resolver, ResolvedTVFArg* pipe_input_arg, std::unique_ptr* result_signature, std::vector* arg_locations, std::vector* resolved_tvf_args, std::vector* named_arguments, SignatureMatchResult* signature_match_result) { - ZETASQL_RET_CHECK_EQ(arg_locations->size(), resolved_tvf_args->size()); + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + ABSL_DCHECK(arg_locations->empty()); + ABSL_DCHECK(resolved_tvf_args->empty()); // Get the TVF signature. Each TVF has exactly one signature; overloading is // not currently allowed. @@ -9609,40 +11028,111 @@ absl::StatusOr Resolver::MatchTVFSignature( const FunctionSignature& function_signature = *tvf_catalog_entry->GetSignature(signature_idx); - const int num_initial_args = static_cast(resolved_tvf_args->size()); const int num_ast_args = static_cast(ast_tvf->argument_entries().size()); - const int num_tvf_args = num_initial_args + num_ast_args; + const int num_tvf_args = num_ast_args + (pipe_input_arg != nullptr ? 1 : 0); + + int pipe_input_arg_idx_in_sig = -1; + // If we have an initial table argument, find the first table argument in the + // signature to match it with. It must not be a named-only argument or + // follow any optional arguments. + if (pipe_input_arg != nullptr) { + ZETASQL_RET_CHECK(pipe_input_arg->IsScan()); - if (num_initial_args > 0) { // Make sure the signature has enough table arguments as positional // arguments at the front to support these initial table args. - bool signature_accepts_initial_args = true; - bool named_only = false; - if (function_signature.arguments().size() < num_initial_args) { - signature_accepts_initial_args = false; - } else { - for (int sig_idx = 0; sig_idx < num_initial_args; ++sig_idx) { - ZETASQL_RET_CHECK((*resolved_tvf_args)[sig_idx].IsScan()); - const FunctionArgumentType& arg_type = - function_signature.argument(sig_idx); - if (!arg_type.IsRelation()) { - signature_accepts_initial_args = false; - break; - } + for (int sig_idx = 0; sig_idx < function_signature.arguments().size(); + ++sig_idx) { + const FunctionArgumentType& arg_type = + function_signature.argument(sig_idx); + if (arg_type.IsRelation()) { if (arg_type.options().named_argument_kind() == kNamedOnly) { - signature_accepts_initial_args = false; - named_only = true; - break; + return MakeSqlErrorAt(ast_tvf) + << "Table-valued function cannot be called with pipe CALL " + "syntax because its first table-typed argument is a " + "named-only argument; Supported signature: " + << tvf_catalog_entry->GetSupportedSignaturesUserFacingText( + language(), /*print_template_and_name_details=*/true); } + // Found the signature argument to use. + pipe_input_arg_idx_in_sig = sig_idx; + break; } } - if (!signature_accepts_initial_args) { + if (pipe_input_arg_idx_in_sig < 0) { return MakeSqlErrorAt(ast_tvf) - << "Table-valued function cannot be called " - "because its signature does not start with " - << (num_initial_args == 1 ? "a" : absl::StrCat(num_initial_args)) - << (named_only ? " positional" : "") << " TABLE argument" - << (num_initial_args > 1 ? "s" : "") << "; Supported signature: " + << "Table-valued function cannot be called with pipe CALL syntax " + "because its signature does not include a table-typed argument" + "; Supported signature: " + << tvf_catalog_entry->GetSupportedSignaturesUserFacingText( + language(), /*print_template_and_name_details=*/true); + } + // If any optional arguments occur before the table argument, that makes + // signature matching more complicated because we can't just insert the + // table argument in the right place in positional arguments ahead of time. + // We also get more confusing error messages. + // This case seems obscure so we disallow it. + for (int sig_idx = 0; sig_idx < pipe_input_arg_idx_in_sig; ++sig_idx) { + const FunctionArgumentType& arg_type = + function_signature.argument(sig_idx); + if (arg_type.optional()) { + return MakeSqlErrorAt(ast_tvf) + << "Table-valued function cannot be called with pipe CALL " + "syntax because it has an optional argument before " + "its first table-typed argument; " + "Supported signature: " + << tvf_catalog_entry->GetSupportedSignaturesUserFacingText( + language(), /*print_template_and_name_details=*/true); + } + // It shouldn't be possible that there's a named-only argument before + // the table argument since we checked that the table argument is + // not named-only above. + ZETASQL_RET_CHECK_NE(arg_type.options().named_argument_kind(), kNamedOnly); + } + + // The CALL doesn't work if it doesn't have enough arguments to + // fill in all arguments that precede the pipe input argument. + if (num_ast_args < pipe_input_arg_idx_in_sig) { + return MakeSqlErrorAt(ast_tvf) + << "Table-valued function call in pipe CALL requires at least " + << pipe_input_arg_idx_in_sig << " positional argument " + << (pipe_input_arg_idx_in_sig == 1 ? "" : "s") + << "because its first table argument is in position " + << (pipe_input_arg_idx_in_sig + 1) << ", but the call has " + << (num_ast_args == 0 + ? "no arguments" + : (num_ast_args == 1 ? "only 1 argument" + : absl::StrCat("only ", num_ast_args, + " arguments"))) + << "; Supported signature: " + << tvf_catalog_entry->GetSupportedSignaturesUserFacingText( + language(), /*print_template_and_name_details=*/true); + } + + // Require that all arguments passed in the CALL before the input table + // argument are positional. This could be relaxed but it complicates the + // signature matching because we can't just insert the table argument + // in the correct place. Errors can also be confusing. + int num_ast_positional_args = 0; + const ASTNode* ast_node_for_error = ast_tvf; + for (; num_ast_positional_args < num_ast_args; ++num_ast_positional_args) { + const ASTTVFArgument* ast_arg = + ast_tvf->argument_entries()[num_ast_positional_args]; + const ASTExpression* ast_expr = ast_arg->expr(); + if (ast_expr != nullptr && ast_expr->node_kind() == AST_NAMED_ARGUMENT) { + // If we stopped at a named argument, use that as the error location. + // If we had too few total arguments, just point at the function name. + ast_node_for_error = ast_expr; + break; + } + } + if (num_ast_positional_args < pipe_input_arg_idx_in_sig) { + return MakeSqlErrorAt(ast_node_for_error) + << "Table-valued function call in pipe CALL requires at least " + << pipe_input_arg_idx_in_sig << " positional argument" + << (pipe_input_arg_idx_in_sig == 1 ? "" : "s") + << " before any named arguments because " + "its first table argument is in position " + << (pipe_input_arg_idx_in_sig + 1) << "; Supported signature: " << tvf_catalog_entry->GetSupportedSignaturesUserFacingText( language(), /*print_template_and_name_details=*/true); } @@ -9668,58 +11158,86 @@ absl::StatusOr Resolver::MatchTVFSignature( sig_idx_to_name_scope_map; arg_locations->reserve(num_tvf_args); resolved_tvf_args->reserve(num_tvf_args); - for (int ast_idx = 0; ast_idx < num_ast_args; ++ast_idx) { - int sig_idx = num_initial_args + ast_idx; // Position in FunctionSignature - const ASTExpression* ast_expr = - ast_tvf->argument_entries()[ast_idx]->expr(); - if (ast_expr == nullptr) { - arg_locations->push_back(ast_tvf->argument_entries()[ast_idx]); + for (int arg_idx = 0, current_ast_idx = 0; arg_idx < num_tvf_args; + ++arg_idx) { + // `arg_idx` is the implicit argument number in the call after the + // pipe_input_arg has been inserted. + + if (arg_idx == pipe_input_arg_idx_in_sig) { + // The resolved input arg is just the `pipe_input_arg` table scan, + // which has already been resolved. + resolved_tvf_args->emplace_back(std::move(*pipe_input_arg)); + + // For the initial table argument, we don't have an argument location, so + // just use the location of the TVF call. + arg_locations->push_back(ast_tvf); } else { - arg_locations->push_back(ast_expr); - if (ast_expr->node_kind() == AST_NAMED_ARGUMENT) { - // Make sure the language feature is enabled. - if (!language().LanguageFeatureEnabled(FEATURE_NAMED_ARGUMENTS)) { - return MakeSqlErrorAt(ast_expr) - << "Named arguments are not supported"; - } - // Add the named argument to the map. - const ASTNamedArgument* named_arg = ast_expr->GetAs(); - if (IsNamedLambda(named_arg)) { - return MakeSqlErrorAt(named_arg->expr()) - << "Lambda arguments are not implemented for TVF"; - } - named_arguments->emplace_back(named_arg->name()->GetAsIdString(), - sig_idx, named_arg); - const absl::string_view arg_name = - named_arg->name()->GetAsIdString().ToStringView(); - - sig_idx = -1; - for (int j = 0; j < function_signature.arguments().size(); ++j) { - const FunctionArgumentType& arg_type = function_signature.argument(j); - if (arg_type.has_argument_name() && - zetasql_base::CaseEqual(arg_type.argument_name(), arg_name)) { - sig_idx = j; - break; + // ast_idx is the arg position in the AST arguments. + int ast_idx = current_ast_idx++; + + // `sig_idx` is the position of the arg in the signature. + // Positional arguments come first, so this is initially `arg_idx`. + // `sig_idx` will be adjusted below for named arguments. + int sig_idx = arg_idx; + + // Hitting this would mean we went off the end of AST args before we + // got to use the pipe_input_arg. + ZETASQL_RET_CHECK_LT(ast_idx, num_ast_args); + + const ASTTVFArgument* ast_arg = ast_tvf->argument_entries()[ast_idx]; + const ASTExpression* ast_expr = ast_arg->expr(); + if (ast_expr == nullptr) { + arg_locations->push_back(ast_arg); + } else { + arg_locations->push_back(ast_expr); + if (ast_expr->node_kind() == AST_NAMED_ARGUMENT) { + // Make sure the language feature is enabled. + if (!language().LanguageFeatureEnabled(FEATURE_NAMED_ARGUMENTS)) { + return MakeSqlErrorAt(ast_expr) + << "Named arguments are not supported"; + } + // Add the named argument to the map. + const ASTNamedArgument* named_arg = + ast_expr->GetAs(); + if (IsNamedLambda(named_arg)) { + return MakeSqlErrorAt(named_arg->expr()) + << "Lambda arguments are not implemented for TVF"; + } + named_arguments->emplace_back(named_arg->name()->GetAsIdString(), + arg_idx, named_arg); + const absl::string_view arg_name = + named_arg->name()->GetAsIdString().ToStringView(); + + sig_idx = -1; + for (int j = 0; j < function_signature.arguments().size(); ++j) { + const FunctionArgumentType& arg_type = + function_signature.argument(j); + if (arg_type.has_argument_name() && + zetasql_base::CaseEqual(arg_type.argument_name(), arg_name)) { + sig_idx = j; + break; + } + } + if (sig_idx == -1) { + return MakeSqlErrorAt(ast_expr) + << "Named argument " << arg_name + << " not found in signature for call to function " + << tvf_catalog_entry->FullName(); } - } - if (sig_idx == -1) { - return MakeSqlErrorAt(ast_expr) - << "Named argument " << arg_name - << " not found in signature for call to function " - << tvf_catalog_entry->FullName(); } } + // NOTE: This function is not called for the pipe CALL lhs args. + auto tvf_arg_or_status = ResolveTVFArg( + ast_arg, external_scope, local_scope, + sig_idx < function_signature.arguments().size() + ? &function_signature.argument(sig_idx) + : nullptr, + tvf_catalog_entry, sig_idx, + descriptor_arg_present ? &sig_idx_to_name_scope_map : nullptr); + + ZETASQL_RETURN_IF_ERROR(tvf_arg_or_status.status()); + resolved_tvf_args->push_back(std::move(tvf_arg_or_status).value()); } - // `sig_idx` is the position of the arg in the signature. - auto tvf_arg_or_status = ResolveTVFArg( - ast_tvf->argument_entries()[ast_idx], external_scope, local_scope, - sig_idx < function_signature.arguments().size() - ? &function_signature.argument(sig_idx) - : nullptr, - tvf_catalog_entry, sig_idx, - descriptor_arg_present ? &sig_idx_to_name_scope_map : nullptr); - ZETASQL_RETURN_IF_ERROR(tvf_arg_or_status.status()); - resolved_tvf_args->push_back(std::move(tvf_arg_or_status).value()); } ZETASQL_RET_CHECK_EQ(arg_locations->size(), num_tvf_args); ZETASQL_RET_CHECK_EQ(resolved_tvf_args->size(), num_tvf_args); @@ -9731,20 +11249,31 @@ absl::StatusOr Resolver::MatchTVFSignature( // (descriptor columns can reference table arguments that appear after them // in the function call). if (descriptor_arg_present) { - // Store the NameLists for the initial table args so descriptor matching - // can see them. - for (int sig_idx = 0; sig_idx < num_initial_args; ++sig_idx) { - ZETASQL_RET_CHECK(!sig_idx_to_name_scope_map.contains(sig_idx)); - ZETASQL_ASSIGN_OR_RETURN(auto name_list, - (*resolved_tvf_args)[sig_idx].GetNameList()); - sig_idx_to_name_scope_map[sig_idx] = + // Store the NameList for the pipe_input_arg so descriptor matching + // can see it. This happened in ResolveTVFArg for other table args. + if (pipe_input_arg != nullptr) { + ZETASQL_RET_CHECK_GE(pipe_input_arg_idx_in_sig, 0); + ZETASQL_RET_CHECK(!sig_idx_to_name_scope_map.contains(pipe_input_arg_idx_in_sig)); + ZETASQL_ASSIGN_OR_RETURN( + auto name_list, + (*resolved_tvf_args)[pipe_input_arg_idx_in_sig].GetNameList()); + sig_idx_to_name_scope_map[pipe_input_arg_idx_in_sig] = std::make_unique(external_scope, name_list); } - for (int ast_idx = 0; ast_idx < num_ast_args; ast_idx++) { - const int sig_idx = ast_idx + num_initial_args; + for (int sig_idx = 0, current_ast_idx = 0; sig_idx < num_tvf_args; + ++sig_idx) { + // `sig_idx` is the index in the FunctionSignature. + // `ast_idx` is the arg position in the AST arguments, or -1 for + // pipe_input_arg. + int ast_idx = -1; + if (sig_idx != pipe_input_arg_idx_in_sig) { + ast_idx = current_ast_idx++; + } + ResolvedTVFArg* resolved_tvf_arg = &(*resolved_tvf_args)[sig_idx]; if (resolved_tvf_arg->IsDescriptor()) { + ZETASQL_RET_CHECK_NE(ast_idx, -1); // pipe_input_arg is not a descriptor. const ASTTVFArgument* ast_tvf_arg = ast_tvf->argument_entries()[ast_idx]; const FunctionArgumentType* function_argument = @@ -9850,17 +11379,18 @@ absl::Status Resolver::PrepareTVFInputArguments( absl::string_view tvf_name_string, const ASTTVF* ast_tvf, const TableValuedFunction* tvf_catalog_entry, const NameScope* external_scope, const NameScope* local_scope, + ResolvedTVFArg* pipe_input_arg, std::unique_ptr* result_signature, std::vector* resolved_tvf_args, std::vector* tvf_input_arguments) { - // `resolved_tvf_args` can be non-empty to provide initial args. + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + + ZETASQL_RET_CHECK(resolved_tvf_args->empty()); ZETASQL_RET_CHECK(tvf_input_arguments->empty()); // and reflect the concrete function - // call arguments in and match 1:1 to them. For initial - // arguments, we don't have an argument location, so just use the location - // of the TVF call. - std::vector arg_locations(resolved_tvf_args->size(), ast_tvf); + // call arguments in and match 1:1 to them. + std::vector arg_locations; std::vector named_arguments; SignatureMatchResult signature_match_result; @@ -9868,8 +11398,8 @@ absl::Status Resolver::PrepareTVFInputArguments( ZETASQL_ASSIGN_OR_RETURN( const int matching_signature_idx, MatchTVFSignature(ast_tvf, tvf_catalog_entry, external_scope, local_scope, - function_resolver, result_signature, &arg_locations, - resolved_tvf_args, &named_arguments, + function_resolver, pipe_input_arg, result_signature, + &arg_locations, resolved_tvf_args, &named_arguments, &signature_match_result)); ZETASQL_RET_CHECK_EQ(arg_locations.size(), resolved_tvf_args->size()); const FunctionSignature* function_signature = @@ -10035,6 +11565,9 @@ absl::Status Resolver::GenerateTVFNotMatchError( analyzer_options_.show_function_signature_mismatch_details()); } +// NOTE: This will not be called for initial arguments like the pipe CALL lhs. +// This only does processing of the args on the rhs in the parentheses. +// So we can't do any processing or checking required on all args here. absl::StatusOr Resolver::ResolveTVFArg( const ASTTVFArgument* ast_tvf_arg, const NameScope* external_scope, const NameScope* local_scope, const FunctionArgumentType* function_argument, @@ -10059,12 +11592,13 @@ absl::StatusOr Resolver::ResolveTVFArg( // When X is a value table, TABLE X produces a scan of it as a value // table. (The same is true when we have TABLE tvf(...), and the tvf // returns a value table.) - std::vector tvf_args; std::unique_ptr scan; std::shared_ptr name_list; ZETASQL_RETURN_IF_ERROR(ResolveTVF(ast_table_clause->tvf(), external_scope, - local_scope, &tvf_args, &scan, &name_list)); - resolved_tvf_arg.SetScan(std::move(scan), name_list); + local_scope, /*pipe_input_arg=*/nullptr, &scan, + &name_list)); + resolved_tvf_arg.SetScan(std::move(scan), name_list, + /*is_pipe_input_table=*/false); } else { // If the TVF argument is a TABLE clause, then the table name can be a // WITH clause entry, one of the table arguments to the TVF, or a table @@ -10075,7 +11609,8 @@ absl::StatusOr Resolver::ResolveTVFArg( std::shared_ptr name_list; ZETASQL_RETURN_IF_ERROR(ResolveNamedSubqueryRef(table_path, /*hint=*/nullptr, &scan, &name_list)); - resolved_tvf_arg.SetScan(std::move(scan), name_list); + resolved_tvf_arg.SetScan(std::move(scan), name_list, + /*is_pipe_input_table=*/false); } else if (table_path->num_names() == 1 && function_argument_info_ != nullptr && function_argument_info_->FindTableArg( @@ -10088,7 +11623,8 @@ absl::StatusOr Resolver::ResolveTVFArg( ZETASQL_RETURN_IF_ERROR(ResolvePathExpressionAsFunctionTableArgument( table_path, /*hint=*/nullptr, GetAliasForExpression(table_path), /*ast_location=*/ast_table_clause, &scan, &name_list)); - resolved_tvf_arg.SetScan(std::move(scan), std::move(name_list)); + resolved_tvf_arg.SetScan(std::move(scan), std::move(name_list), + /*is_pipe_input_table=*/false); } else { ZETASQL_RET_CHECK(ast_expr == nullptr); std::unique_ptr table_scan; @@ -10099,7 +11635,8 @@ absl::StatusOr Resolver::ResolveTVFArg( /*hints=*/nullptr, /*for_system_time=*/nullptr, external_scope, /*remaining_names=*/nullptr, &table_scan, &name_list, resolved_columns_from_table_scans_)); - resolved_tvf_arg.SetScan(std::move(table_scan), std::move(name_list)); + resolved_tvf_arg.SetScan(std::move(table_scan), std::move(name_list), + /*is_pipe_input_table=*/false); } } ZETASQL_ASSIGN_OR_RETURN(std::shared_ptr name_list, @@ -10166,7 +11703,8 @@ absl::StatusOr Resolver::ResolveTVFArg( sig_idx_to_name_scope_map->emplace( sig_idx, std::make_unique(external_scope, name_list)); } - resolved_tvf_arg.SetScan(std::move(scan), std::move(name_list)); + resolved_tvf_arg.SetScan(std::move(scan), std::move(name_list), + /*is_pipe_input_table=*/false); } else if (function_argument->IsConnection()) { return MakeSqlErrorAt(ast_expr) << "Table-valued function " << tvf_catalog_entry->FullName() @@ -10229,7 +11767,8 @@ absl::StatusOr Resolver::GetTVFArgType( resolved_tvf_arg.GetNameList()); if (name_list->is_value_table()) { input_arg_type = InputArgumentType::RelationInputArgumentType( - TVFRelation::ValueTable(name_list->column(0).column().type())); + TVFRelation::ValueTable(name_list->column(0).column().type()), + resolved_tvf_arg.IsPipeInputTable()); } else { TVFRelation::ColumnList provided_input_relation_columns; provided_input_relation_columns.reserve(name_list->num_columns()); @@ -10243,7 +11782,8 @@ absl::StatusOr Resolver::GetTVFArgType( named_column.name().ToString(), named_column.column().type()); } input_arg_type = InputArgumentType::RelationInputArgumentType( - TVFRelation(provided_input_relation_columns)); + TVFRelation(provided_input_relation_columns), + resolved_tvf_arg.IsPipeInputTable()); } } else if (resolved_tvf_arg.IsConnection()) { ZETASQL_ASSIGN_OR_RETURN(const ResolvedConnection* const connection, @@ -10407,7 +11947,7 @@ absl::Status Resolver::CoerceOrRearrangeTVFRelationArgColumns( resolved_tvf_arg->SetScan( MakeResolvedProjectScan(new_column_list, std::move(new_project_columns), std::move(scan)), - std::move(new_project_name_list)); + std::move(new_project_name_list), resolved_tvf_arg->IsPipeInputTable()); return absl::OkStatus(); } @@ -10686,8 +12226,33 @@ absl::Status Resolver::ResolveArrayScan( ZETASQL_RET_CHECK_EQ(*resolved_input_scan == nullptr, name_list_input == nullptr); if (table_ref->sample_clause() != nullptr) { - return MakeSqlErrorAt(table_ref) - << "TABLESAMPLE cannot be used with arrays"; + return MakeSqlErrorAt(table_ref->sample_clause()) + << "TABLESAMPLE is not allowed with array scans"; + } + if (table_ref->pivot_clause() != nullptr) { + if (language().LanguageFeatureEnabled( + FEATURE_V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS)) { + return MakeSqlErrorAt(table_ref->pivot_clause()) + << "PIVOT is not allowed with array scans"; + } else { + ZETASQL_RETURN_IF_ERROR(AddDeprecationWarning( + table_ref->pivot_clause(), + DeprecationWarning::PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN, + "PIVOT is not allowed with array scans. This will become an error")); + } + } + if (table_ref->unpivot_clause() != nullptr) { + if (language().LanguageFeatureEnabled( + FEATURE_V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS)) { + return MakeSqlErrorAt(table_ref->unpivot_clause()) + << "UNPIVOT is not allowed with array scans"; + } else { + ZETASQL_RETURN_IF_ERROR(AddDeprecationWarning( + table_ref->unpivot_clause(), + DeprecationWarning::PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN, + "UNPIVOT is not allowed with array scans. This will become an " + "error")); + } } // We have either an array reference or UNNEST. @@ -11210,15 +12775,21 @@ absl::Status Resolver::FinishResolvingDescriptor( absl::Status Resolver::ResolveConnection( const ASTExpression* path_expr_or_default, - std::unique_ptr* resolved_connection) { + std::unique_ptr* resolved_connection, + bool is_default_connection_allowed) { if (path_expr_or_default->Is()) { return ResolveConnectionPath( path_expr_or_default->GetAsOrDie(), resolved_connection); } ZETASQL_RET_CHECK(path_expr_or_default->Is()); - return MakeSqlErrorAt(path_expr_or_default) - << "CONNECTION DEFAULT is not supported"; + if (!is_default_connection_allowed) { + return MakeSqlErrorAt(path_expr_or_default) + << "CONNECTION DEFAULT is not supported"; + } + return ResolveDefaultConnection( + path_expr_or_default->GetAsOrDie(), + resolved_connection); } absl::Status Resolver::ResolveConnectionPath( @@ -11239,6 +12810,21 @@ absl::Status Resolver::ResolveConnectionPath( return absl::OkStatus(); } +absl::Status Resolver::ResolveDefaultConnection( + const ASTDefaultLiteral* default_literal, + std::unique_ptr* resolved_connection) { + const Connection* connection = nullptr; + const absl::Status find_status = catalog_->FindConnection( + {"$connection_default"}, &connection, analyzer_options_.find_options()); + if (find_status.code() == absl::StatusCode::kNotFound) { + return MakeSqlErrorAt(default_literal) << "Default connection not found"; + } + ZETASQL_RETURN_IF_ERROR(find_status); + + *resolved_connection = MakeResolvedConnection(connection); + return absl::OkStatus(); +} + bool Resolver::IsPathExpressionStartingFromNamedSubquery( const ASTPathExpression& path_expr) const { std::vector path; diff --git a/zetasql/analyzer/resolver_stmt.cc b/zetasql/analyzer/resolver_stmt.cc index 86e3cf093..2fc5389a8 100644 --- a/zetasql/analyzer/resolver_stmt.cc +++ b/zetasql/analyzer/resolver_stmt.cc @@ -260,6 +260,7 @@ static absl::Status ValidateStatementIsReturnable( case RESOLVED_CREATE_PRIVILEGE_RESTRICTION_STMT: case RESOLVED_ALTER_PRIVILEGE_RESTRICTION_STMT: case RESOLVED_CREATE_ROW_ACCESS_POLICY_STMT: + case RESOLVED_CREATE_CONNECTION_STMT: case RESOLVED_CREATE_CONSTANT_STMT: case RESOLVED_CREATE_PROCEDURE_STMT: case RESOLVED_CLONE_DATA_STMT: @@ -290,6 +291,7 @@ static absl::Status ValidateStatementIsReturnable( case RESOLVED_REVOKE_STMT: case RESOLVED_MERGE_STMT: case RESOLVED_TRUNCATE_STMT: + case RESOLVED_ALTER_CONNECTION_STMT: case RESOLVED_ALTER_ROW_ACCESS_POLICY_STMT: case RESOLVED_ALTER_ALL_ROW_ACCESS_POLICIES_STMT: case RESOLVED_ALTER_MATERIALIZED_VIEW_STMT: @@ -649,8 +651,9 @@ absl::Status Resolver::ResolveStatement( case AST_DROP_STATEMENT: if (language().SupportsStatementKind(RESOLVED_DROP_STMT)) { - ZETASQL_RETURN_IF_ERROR(ResolveDropStatement( - statement->GetAsOrDie(), &stmt)); + const zetasql::ASTDropStatement* drop_statement = + statement->GetAsOrDie(); + ZETASQL_RETURN_IF_ERROR(ResolveDropStatement(drop_statement, &stmt)); } break; @@ -890,6 +893,18 @@ absl::Status Resolver::ResolveStatement( statement->GetAsOrDie(), &stmt)); } break; + case AST_CREATE_CONNECTION_STATEMENT: + if (language().SupportsStatementKind(RESOLVED_CREATE_CONNECTION_STMT)) { + ZETASQL_RETURN_IF_ERROR(ResolveCreateConnectionStatement( + statement->GetAsOrDie(), &stmt)); + } + break; + case AST_ALTER_CONNECTION_STATEMENT: + if (language().SupportsStatementKind(RESOLVED_ALTER_CONNECTION_STMT)) { + ZETASQL_RETURN_IF_ERROR(ResolveAlterConnectionStatement( + statement->GetAsOrDie(), &stmt)); + } + break; case AST_CREATE_DATABASE_STATEMENT: if (language().SupportsStatementKind(RESOLVED_CREATE_DATABASE_STMT)) { ZETASQL_RETURN_IF_ERROR(ResolveCreateDatabaseStatement( @@ -2371,6 +2386,27 @@ absl::Status Resolver::ResolveCreateTablePartitionByList( return absl::OkStatus(); } +absl::Status Resolver::ResolveCreateConnectionStatement( + const ASTCreateConnectionStatement* ast_statement, + std::unique_ptr* output) { + ResolvedCreateStatement::CreateScope create_scope; + ResolvedCreateStatement::CreateMode create_mode; + std::vector> resolved_options; + + // Resolve CREATE clauses then OPTIONS clauses. + ZETASQL_RETURN_IF_ERROR(ResolveCreateStatementOptions( + ast_statement, /*statement_type=*/"CREATE CONNECTION", &create_scope, + &create_mode)); + + ZETASQL_RETURN_IF_ERROR(ResolveOptionsList(ast_statement->options_list(), + /*allow_alter_array_operators=*/false, + &resolved_options)); + *output = MakeResolvedCreateConnectionStmt( + ast_statement->name()->ToIdentifierVector(), create_scope, create_mode, + std::move(resolved_options)); + return absl::OkStatus(); +} + absl::Status Resolver::ResolveCreateDatabaseStatement( const ASTCreateDatabaseStatement* ast_statement, std::unique_ptr* output) { @@ -2612,6 +2648,29 @@ absl::Status Resolver::ResolveCreateIndexStatement( } } + std::vector> resolved_partition_by; + if (ast_statement->optional_partition_by() != nullptr) { + if (!language().LanguageFeatureEnabled(FEATURE_CREATE_INDEX_PARTITION_BY)) { + return MakeSqlErrorAt(ast_statement->optional_partition_by()) + << "CREATE INDEX with PARTITION BY is not supported."; + } + // Only one of is_search() and is_vector() can be true. + if (ast_statement->is_search()) { + return MakeSqlErrorAt(ast_statement->optional_partition_by()) + << "PARTITION BY is not supported for CREATE SEARCH INDEX."; + } + if (!ast_statement->is_vector()) { + return MakeSqlErrorAt(ast_statement->optional_partition_by()) + << "PARTITION BY is not supported for CREATE INDEX."; + } + ZETASQL_RET_CHECK(ast_statement->optional_partition_by()->hint() == nullptr); + QueryResolutionInfo query_info(this); + ZETASQL_RETURN_IF_ERROR(ResolveCreateTablePartitionByList( + ast_statement->optional_partition_by()->partitioning_expressions(), + PartitioningKind::PARTITION_BY, name_scope, &query_info, + &resolved_partition_by)); + } + ResolvedCreateStatement::CreateScope create_scope; ResolvedCreateStatement::CreateMode create_mode; ZETASQL_RETURN_IF_ERROR(ResolveCreateStatementOptions(ast_statement, "CREATE INDEX", @@ -2631,8 +2690,8 @@ absl::Status Resolver::ResolveCreateIndexStatement( std::move(resolved_table_scan), ast_statement->is_unique(), ast_statement->is_search(), ast_statement->is_vector(), index_all_columns, std::move(resolved_index_items), std::move(resolved_index_storing_items), - std::move(resolved_options), std::move(resolved_computed_columns), - std::move(resolved_unnest_items)); + std::move(resolved_partition_by), std::move(resolved_options), + std::move(resolved_computed_columns), std::move(resolved_unnest_items)); return absl::OkStatus(); } @@ -2907,7 +2966,8 @@ absl::Status Resolver::ResolveCreateModelStatement( ZETASQL_RETURN_IF_ERROR(ResolveConnection(ast_statement->with_connection_clause() ->connection_clause() ->connection_path(), - &resolved_connection)); + &resolved_connection, + /*is_default_connection_allowed=*/true)); } // Resolve the query. @@ -3305,9 +3365,19 @@ absl::Status Resolver::ResolveCreateTableStmtBaseProperties( << "WITH CONNECTION clause is unsupported for CREATE TABLE"; } } - ZETASQL_RETURN_IF_ERROR(ResolveConnection( - with_connection_clause->connection_clause()->connection_path(), - &statement_base_properties->connection)); + switch (ast_statement->node_kind()) { + case AST_CREATE_EXTERNAL_TABLE_STATEMENT: + case AST_CREATE_TABLE_STATEMENT: + ZETASQL_RETURN_IF_ERROR(ResolveConnection( + with_connection_clause->connection_clause()->connection_path(), + &statement_base_properties->connection, + /*is_default_connection_allowed=*/true)); + break; + default: + ZETASQL_RETURN_IF_ERROR(ResolveConnection( + with_connection_clause->connection_clause()->connection_path(), + &statement_base_properties->connection)); + } } if (partitions_clause != nullptr) { @@ -5213,11 +5283,11 @@ absl::Status Resolver::ResolveCreateProcedureStatement( // validation and getting correct type here is lengthy. arguments_map[function_param->name()->GetAsIdString()] = nullptr; } - ZETASQL_ASSIGN_OR_RETURN( - std::unique_ptr parsed_script, - ParsedScript::CreateForRoutine(sql_, ast_statement->body(), - analyzer_options_.error_message_mode(), - std::move(arguments_map))); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr parsed_script, + ParsedScript::CreateForRoutine( + sql_, ast_statement->body(), + analyzer_options_.error_message_options(), + std::move(arguments_map))); ZETASQL_RETURN_IF_ERROR(parsed_script->CheckQueryParameters(std::nullopt)); } diff --git a/zetasql/analyzer/resolver_test.cc b/zetasql/analyzer/resolver_test.cc index 6746733bb..d332591b0 100644 --- a/zetasql/analyzer/resolver_test.cc +++ b/zetasql/analyzer/resolver_test.cc @@ -393,22 +393,6 @@ class ResolverTest : public ::testing::Test { StatusIs(_, HasSubstr(expected_error_substr))); } - void TestResolverErrorMessage(absl::string_view query, - absl::string_view expected_error_substr) { - std::unique_ptr parser_output; - std::unique_ptr resolved_expression; - // Parsing should succeed. - ZETASQL_ASSERT_OK(ParseExpression(query, ParserOptions(), &parser_output)) << query; - const ASTExpression* parsed_expression = parser_output->expression(); - ASSERT_THAT(parsed_expression, NotNull()); - EXPECT_THAT(ResolveExpr(parsed_expression, &resolved_expression), - StatusIs(_, HasSubstr(expected_error_substr))) - << "Query: " << query - << "\nParsed expression: " << Unparse(parsed_expression); - EXPECT_THAT(resolved_expression.get(), IsNull()) - << resolved_expression->DebugString(); - } - void TestResolverOK(absl::string_view query) { std::unique_ptr parser_output; std::unique_ptr resolved_expression; diff --git a/zetasql/analyzer/rewrite_resolved_ast.cc b/zetasql/analyzer/rewrite_resolved_ast.cc index 3a9390312..937ce362a 100644 --- a/zetasql/analyzer/rewrite_resolved_ast.cc +++ b/zetasql/analyzer/rewrite_resolved_ast.cc @@ -41,7 +41,6 @@ #include "zetasql/resolved_ast/validator.h" #include "absl/algorithm/container.h" #include "absl/container/btree_set.h" -#include "absl/flags/flag.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" @@ -49,12 +48,6 @@ #include "zetasql/base/ret_check.h" #include "zetasql/base/status_macros.h" -// This flag is an escape hatch to disable running the rewriter relevance -// checker invoked by `FindRelevantRewriters`. -ABSL_FLAG(bool, zetasql_disable_rewriter_checker, false, - "Disables post resolution detection of applicable ZetaSQL " - "rewriters."); - namespace zetasql { namespace { @@ -158,30 +151,10 @@ absl::Status InternalRewriteResolvedAstNoConvertErrorLocation( absl::StrAppend(s, ResolvedASTRewrite_Name(r)); }); - const absl::btree_set& resolver_detected_rewrites = - output_mutator.mutable_output_properties().relevant_rewrites(); const ResolvedNode* rewrite_input = NodeFromAnalyzerOutput(analyzer_output); ZETASQL_RET_CHECK(rewrite_input != nullptr); - absl::btree_set checker_detected_rewrites; - if (ZETASQL_DEBUG_MODE || !absl::GetFlag(FLAGS_zetasql_disable_rewriter_checker)) { - ZETASQL_ASSIGN_OR_RETURN(checker_detected_rewrites, - FindRelevantRewriters(rewrite_input)); - // This check is trying to catch any cases where the resolver is updated to - // identify an applicable rewrite but FindRelevantRewriters is not. The - // resolver's output is used on the first rewrite pass, but - // FindRelevantRewriters is used on subsequent passes. If the logic diverges - // between those components, we could miss rewrites. - if (ZETASQL_DEBUG_MODE && !resolver_detected_rewrites.empty()) { - ZETASQL_RET_CHECK( - absl::c_equal(resolver_detected_rewrites, checker_detected_rewrites)) - << "\nResolved: " << absl::StrJoin(resolver_detected_rewrites, ", ") - << "\nChecker: " << absl::StrJoin(checker_detected_rewrites, ", "); - } - } - const absl::btree_set& detected_rewrites = - absl::GetFlag(FLAGS_zetasql_disable_rewriter_checker) - ? resolver_detected_rewrites - : checker_detected_rewrites; + ZETASQL_ASSIGN_OR_RETURN(absl::btree_set detected_rewrites, + FindRelevantRewriters(rewrite_input)); if (detected_rewrites.empty() && analyzer_options.leading_rewriters().empty() && analyzer_options.trailing_rewriters().empty()) { diff --git a/zetasql/analyzer/rewrite_resolved_ast_test.cc b/zetasql/analyzer/rewrite_resolved_ast_test.cc index c864fe6f5..898b0ada7 100644 --- a/zetasql/analyzer/rewrite_resolved_ast_test.cc +++ b/zetasql/analyzer/rewrite_resolved_ast_test.cc @@ -33,9 +33,6 @@ #include "absl/time/time.h" ABSL_DECLARE_FLAG(double, zetasql_stack_usage_proportion_warning); -ABSL_DECLARE_FLAG(bool, zetasql_disable_rewriter_checker); - -using ::testing::Gt; namespace zetasql { @@ -133,6 +130,7 @@ TEST(RewriteResolvedAstTest, RewriterDoesNotConflictWithExpressionColumnNames) { | | | | +-ColumnRef(type=STRING, column=$subquery1.k#1, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem +| | | +-parse_location=192-203 | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#4) | | | +-is_descending=TRUE @@ -186,29 +184,4 @@ TEST(RewriteResolvedAstTest, RewriterWarnsComplextyJustOnce) { absl::SetFlag(&FLAGS_zetasql_stack_usage_proportion_warning, old_value); } -TEST(RewriteResolvedAstTest, RewriteWithRewriteDetectedDisabled) { - absl::SetFlag(&FLAGS_zetasql_disable_rewriter_checker, true); - AnalyzerOptions options; - options.mutable_language()->EnableMaximumLanguageFeatures(); - ZETASQL_CHECK_OK(options.AddExpressionColumn("k", types::Int64Type())); - - TypeFactory types; - const Type* map_type; - ZETASQL_CHECK_OK(types.MakeProtoType( - zetasql_test__::MessageWithMapField::descriptor(), &map_type)); - ZETASQL_CHECK_OK(options.AddExpressionColumn("mapproto", map_type)); - - SimpleCatalog catalog("catalog", &types); - catalog.AddBuiltinFunctions(BuiltinFunctionOptions::AllReleasedFunctions()); - - std::unique_ptr output; - auto status = - AnalyzeExpression("mapproto.string_int32_map[SAFE_KEY('foo')] + k", - options, &catalog, &types, &output); - ZETASQL_EXPECT_OK(status); - EXPECT_TRUE(output->resolved_expr() != nullptr); - EXPECT_THAT(output->runtime_info().rewriters_timed_value().elapsed_duration(), - Gt(absl::Nanoseconds(0))); -} - } // namespace zetasql diff --git a/zetasql/analyzer/rewriters/BUILD b/zetasql/analyzer/rewriters/BUILD index edc68ca73..2c1d13ae0 100644 --- a/zetasql/analyzer/rewriters/BUILD +++ b/zetasql/analyzer/rewriters/BUILD @@ -558,3 +558,54 @@ cc_library( "@com_google_absl//absl/status:statusor", ], ) + +cc_library( + name = "pipe_assert_rewriter", + srcs = ["pipe_assert_rewriter.cc"], + hdrs = ["pipe_assert_rewriter.h"], + deps = [ + "//zetasql/base:ret_check", + "//zetasql/base:status", + "//zetasql/public:analyzer_options", + "//zetasql/public:catalog", + "//zetasql/public:function_headers", + "//zetasql/public:rewriter_interface", + "//zetasql/public:value", + "//zetasql/public/types", + "//zetasql/resolved_ast", + "//zetasql/resolved_ast:resolved_ast_builder", + "//zetasql/resolved_ast:resolved_ast_rewrite_visitor", + "//zetasql/resolved_ast:rewrite_utils", + "@com_google_absl//absl/status:statusor", + ], +) + +cc_library( + name = "order_by_and_limit_in_aggregate_rewriter", + srcs = ["order_by_and_limit_in_aggregate_rewriter.cc"], + hdrs = ["order_by_and_limit_in_aggregate_rewriter.h"], + deps = [ + "//zetasql/base:ret_check", + "//zetasql/base:status", + "//zetasql/public:analyzer_options", + "//zetasql/public:analyzer_output_properties", + "//zetasql/public:builtin_function_cc_proto", + "//zetasql/public:catalog", + "//zetasql/public:options_cc_proto", + "//zetasql/public:rewriter_interface", + "//zetasql/public:value", + "//zetasql/public/types", + "//zetasql/resolved_ast", + "//zetasql/resolved_ast:column_factory", + "//zetasql/resolved_ast:resolved_ast_builder", + "//zetasql/resolved_ast:resolved_ast_rewrite_visitor", + "//zetasql/resolved_ast:rewrite_utils", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/memory", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", + ], +) diff --git a/zetasql/analyzer/rewriters/anonymization_helper.cc b/zetasql/analyzer/rewriters/anonymization_helper.cc index 5f0e4534d..c90f2765b 100644 --- a/zetasql/analyzer/rewriters/anonymization_helper.cc +++ b/zetasql/analyzer/rewriters/anonymization_helper.cc @@ -235,7 +235,7 @@ class PublicGroupsState { MaybeRewritePublicGroupsJoinAndReturnUid( const UidColumnState& left_scan_uid_state, const UidColumnState& right_scan_uid_state, - const std::vector>& with_entries, + absl::Span> with_entries, ResolvedJoinScan* join); // Tracks column replacements, so that the join conditions can be rewritten @@ -3133,13 +3133,9 @@ ExtractSubmessageFromProtoColumn(const std::string& field_name, ZETASQL_RETURN_IF_ERROR(type_factory.GetProtoFieldType( field, proto_column.type()->AsProto()->CatalogNamePath(), &field_type)); - const zetasql::ProtoType* proto_field_type; - ZETASQL_RETURN_IF_ERROR( - type_factory.MakeProtoType(&proto_descriptor, &proto_field_type)); - return MakeResolvedGetProtoField( field_type, BuildResolvedColumnRef(proto_column), field, - Value::Null(proto_field_type), + Value::Null(field_type), /* get_has_bit=*/false, ProtoType::GetFormatAnnotation(field), /* return_default_value_when_unset=*/false); } @@ -3280,8 +3276,9 @@ MakeExtractCountFromAnonOutputWithReportJson( ZETASQL_RETURN_IF_ERROR(catalog.FindFunction({std::string("json_query")}, &json_query_fn, options.find_options())); FunctionSignature json_query_signature( - type_factory.get_json(), - {type_factory.get_json(), type_factory.get_string()}, FN_JSON_QUERY_JSON); + {type_factory.get_json(), 1}, + {{type_factory.get_json(), 1}, {type_factory.get_string(), 1}}, + FN_JSON_QUERY_JSON); std::vector> json_query_fn_args(2); json_query_fn_args[0] = BuildResolvedColumnRef(unique_users_count_column); json_query_fn_args[1] = @@ -3291,8 +3288,9 @@ MakeExtractCountFromAnonOutputWithReportJson( const Function* json_to_int64_fn = nullptr; ZETASQL_RETURN_IF_ERROR(catalog.FindFunction( {std::string("int64")}, &json_to_int64_fn, options.find_options())); - FunctionSignature json_to_int64_signature( - type_factory.get_int64(), {type_factory.get_json()}, FN_JSON_TO_INT64); + FunctionSignature json_to_int64_signature({type_factory.get_int64(), 1}, + {{type_factory.get_json(), 1}}, + FN_JSON_TO_INT64); std::vector> json_to_int64_fn_args(1); json_to_int64_fn_args[0] = MakeResolvedFunctionCall( types::JsonType(), json_query_fn, json_query_signature, @@ -3526,7 +3524,7 @@ inline constexpr absl::string_view kPrivacyUnitColumnOptionName = // Factory method that extracts the aggregation threshold options from the raw // options of the aggregation threshold aggregate scan. inline absl::StatusOr FromScanOptions( - const std::vector>& scan_options, + absl::Span> scan_options, const LanguageOptions& language_options) { std::optional threshold; std::optional max_groups_contributed; @@ -3901,7 +3899,7 @@ absl::StatusOr> PublicGroupsState::MaybeRewritePublicGroupsJoinAndReturnUid( const UidColumnState& left_scan_uid_state, const UidColumnState& right_scan_uid_state, - const std::vector>& with_entries, + absl::Span> with_entries, ResolvedJoinScan* join) { std::optional uid_columns = GetPublicGroupsUserDataAndPublicDataUidColumn(join, left_scan_uid_state, @@ -4506,7 +4504,7 @@ void AppendResolvedComputedColumnsToList( } void AppendResolvedComputedColumnsToList( - const std::vector>& + absl::Span> resolved_computed_columns, std::vector& out_column_list) { for (const std::unique_ptr& computed_column : diff --git a/zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.cc b/zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.cc new file mode 100644 index 000000000..ad5839e64 --- /dev/null +++ b/zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.cc @@ -0,0 +1,840 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.h" + +#include +#include +#include +#include +#include + +#include "zetasql/public/analyzer_options.h" +#include "zetasql/public/analyzer_output_properties.h" +#include "zetasql/public/builtin_function.pb.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/options.pb.h" +#include "zetasql/public/rewriter_interface.h" +#include "zetasql/public/types/type.h" +#include "zetasql/public/types/type_factory.h" +#include "zetasql/public/value.h" +#include "zetasql/resolved_ast/column_factory.h" +#include "zetasql/resolved_ast/resolved_ast.h" +#include "zetasql/resolved_ast/resolved_ast_builder.h" +#include "zetasql/resolved_ast/resolved_ast_rewrite_visitor.h" +#include "zetasql/resolved_ast/resolved_column.h" +#include "zetasql/resolved_ast/resolved_node.h" +#include "zetasql/resolved_ast/rewrite_utils.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" +#include "zetasql/base/ret_check.h" +#include "zetasql/base/status_macros.h" + +namespace zetasql { + +namespace { + +const std::string_view kRewriter = "$agg_rewriter"; + +// OrderByAndLimitInAggregateRewriter rewrites ARRAY_AGG, STRING_AGG, and +// ARRAY_CONCAT_AGG function calls with an ORDER BY or LIMIT clause into an +// ARRAY subquery and an ARRAY_AGG without an ORDER BY or LIMIT clause. +// +// Consider the transformation for ARRAY_AGG: +// +// SELECT ARRAY_AGG(DISTINCT f IGNORE NULLS HAVING MAX g ORDER BY ord LIMIT c) +// AS result +// FROM input_table +// GROUP BY gby +// +// is transformed into a ResolvedAST like: +// +// SELECT WITH($result AS ARRAY( +// SELECT DISTINCT e.f +// FROM (SELECT e +// FROM UNNEST($arr) AS e +// WHERE e.f IS NOT NULL +// ) +// ORDER BY e.ord +// LIMIT c +// ), IF(ARRAY_LENGTH($result) >= 1, $result, NULL)) AS result +// FROM (SELECT ARRAY_AGG( +// STRUCT() +// HAVING MAX g) as $arr +// FROM from +// GROUP BY gby) +// +// If there are multiple aggregates that must be transformed, we keep the same +// high level structure but use multiple columns: +// +// SELECT , FROM .. +// +// is transformed into: +// +// SELECT , +// FROM (SELECT , FROM ..) +// +// where looks like `WITH($result as ARRAY(..), ..)` +// and looks like `ARRAY_AGG(STRUCT() HAVING MAX g)` +// +// In this transformation, e. means the expression where all the +// columns are replaced with the struct field of the same name. +// +// The mechanism is to stash all the variables involved in a STRUCT in an +// initial ARRAY_AGG and then use those stashed variables inside the ARRAY +// subquery. +// +// The same transformation is used with small modifications for STRING_AGG and +// ARRAY_CONCAT_AGG. +// - For STRING_AGG, we perform an ARRAY_AGG with IGNORE NULLS and then +// call ARRAY_TO_STRING on the resulting array. +// - For ARRAY_CONCAT_AGG, we do the same ARRAY_AGG on a dummy struct (to avoid +// creating an array of arrays) and then call FLATTEN. +class OrderByAndLimitInAggregateRewriterVisitor + : public ResolvedASTRewriteVisitor { + public: + explicit OrderByAndLimitInAggregateRewriterVisitor( + const AnalyzerOptions& analyzer_options, Catalog& catalog, + ColumnFactory& column_factory, TypeFactory& type_factory) + : column_factory_(column_factory), + type_factory_(type_factory), + function_builder_(analyzer_options, catalog, type_factory) {} + + OrderByAndLimitInAggregateRewriterVisitor( + const OrderByAndLimitInAggregateRewriterVisitor&) = delete; + OrderByAndLimitInAggregateRewriterVisitor& operator=( + const OrderByAndLimitInAggregateRewriterVisitor&) = delete; + + private: + // The result of handling a single aggregate function call. + // `array_of_struct` and `result` are described in the description above. + struct AggregateFunctionCallRewriteResult { + // A newly minted column naming the built array (`$arr` in the example + // above) + ResolvedColumn array_column; + // The ARRAY_AGG function call that builds `array_column`. + // `ARRAY_AGG(STRUCT() + // HAVING MAX g)` in the example above. + std::unique_ptr array_of_struct; + // The result. References the expression in `array_of_struct` as + // `array_column`. + std::unique_ptr result; + // A rewrite occurred. + bool is_rewritten = false; + }; + + // Handles a single ARRAY_AGG function call and returns a struct that + // PostVisitResolvedAggregateScan() will stitch together. + // + // We don't use PostVisitResolvedAggregateFunctionCall() because we only can + // rewrite cases where we're doing an aggregate scan. Some cases we don't want + // to handle: ArrayAggregates that have an aggregate function call but no + // scan and aggregate function calls in CREATE AGGREGATE FUNCTION. + absl::StatusOr + HandleAggregateFunctionCall( + std::unique_ptr function_call); + + // Rewrites the ARRAY_AGG function calls in the aggregate scan. + absl::StatusOr> + PostVisitResolvedAggregateScan( + std::unique_ptr aggregate_scan) override; + + // If the aggregate scan has any aggregate function calls with ORDER BY or + // LIMIT, returns an unimplemented error. + absl::StatusOr> CheckRewriterDoesNotApply( + std::unique_ptr scan); + + // TODO: Remove these PostVisit functions and the static_assert + // once we have a DefaultVisit on ResolvedASTRewriteVisitor. + + // If there are any anonymized aggregate scans with ORDER BY or LIMIT, returns + // an unimplemented error. Today this is impossible because the anonymization + // rewriter don't allow ARRAY_AGG/STRING_AGG/ARRAY_CONCAT_AGG. + absl::StatusOr> + PostVisitResolvedAnonymizedAggregateScan( + std::unique_ptr scan) override { + return CheckRewriterDoesNotApply(std::move(scan)); + } + + // If there are any differentially private aggregate scans with ORDER BY or + // LIMIT, returns an unimplemented error. Today this is impossible because the + // anonymization rewriter don't allow ARRAY_AGG/STRING_AGG/ARRAY_CONCAT_AGG. + absl::StatusOr> + PostVisitResolvedDifferentialPrivacyAggregateScan( + std::unique_ptr scan) + override { + return CheckRewriterDoesNotApply(std::move(scan)); + } + + // If there are any threshold aggregate scans with ORDER BY or LIMIT, returns + // an unimplemented error. Today this is impossible because the anonymization + // rewriter don't allow ARRAY_AGG/STRING_AGG/ARRAY_CONCAT_AGG. + absl::StatusOr> + PostVisitResolvedAggregationThresholdAggregateScan( + std::unique_ptr scan) + override { + return CheckRewriterDoesNotApply(std::move(scan)); + } + + static_assert(ResolvedAggregateScanBase::NUM_DESCENDANT_LEAF_TYPES == 4, + "Add support for new aggregate scans to " + "OrderByAndLimitInAggregateRewriter"); + + ColumnFactory& column_factory_; + TypeFactory& type_factory_; + FunctionCallBuilder function_builder_; +}; + +absl::StatusOr> +OrderByAndLimitInAggregateRewriterVisitor::CheckRewriterDoesNotApply( + std::unique_ptr scan) { + for (const auto& aggregate_computed_column : scan->aggregate_list()) { + if (aggregate_computed_column->expr() + ->Is()) { + auto* function_call = aggregate_computed_column->expr() + ->GetAs(); + if (function_call->order_by_item_list_size() > 0 || + function_call->limit() != nullptr) { + return absl::UnimplementedError( + "ORDER_BY_AND_LIMIT_IN_AGGREGATE rewriter does not support " + "ORDER BY or LIMIT in differentially private aggregate " + "functions"); + } + } + } + return scan; +} + +// Handle a ResolvedAggregateScan. Gather the AggregateFunctionCallRewriteResult +// from each aggregate function call and combine them. +absl::StatusOr> +OrderByAndLimitInAggregateRewriterVisitor::PostVisitResolvedAggregateScan( + std::unique_ptr aggregate_scan) { + ResolvedAggregateScanBuilder builder = ToBuilder(std::move(aggregate_scan)); + std::vector column_list = builder.release_column_list(); + ResolvedProjectScanBuilder project_scan_builder; + project_scan_builder.set_column_list(column_list); + absl::flat_hash_set seen_columns; + bool any_rewritten = false; + for (auto& aggregate_computed_column : builder.release_aggregate_list()) { + ResolvedColumn result_column = aggregate_computed_column->column(); + seen_columns.insert(result_column); + ZETASQL_RET_CHECK( + aggregate_computed_column->expr()->Is()); + if (!aggregate_computed_column->Is()) { + // TODO: Today in the ResolvedAST the only place we can put + // side effecting columns (ResolvedComputedColumnBase) is in + // ResolvedAggregateScanBase (and child classes) but here we want to move + // a side-effecting column from an aggregation into an ARRAY subquery. + return absl::UnimplementedError( + "ResolvedDeferredComputedColumn is not supported in " + "ORDER_BY_AND_LIMIT_IN_AGGREGATE rewriter"); + } + ResolvedComputedColumnBuilder computed_column_builder = + ToBuilder(absl::WrapUnique(aggregate_computed_column.release() + ->GetAs())); + ZETASQL_ASSIGN_OR_RETURN(AggregateFunctionCallRewriteResult result, + HandleAggregateFunctionCall(absl::WrapUnique( + const_cast( + computed_column_builder.release_expr().release()) + ->GetAs()))); + if (result.is_rewritten) { + any_rewritten = true; + builder.add_column_list(result.array_column); + builder.add_aggregate_list(MakeResolvedComputedColumn( + result.array_column, std::move(result.array_of_struct))); + project_scan_builder.add_expr_list( + MakeResolvedComputedColumn(result_column, std::move(result.result))); + } else { + builder.add_column_list(result_column); + builder.add_aggregate_list(MakeResolvedComputedColumn( + result_column, std::move(result.array_of_struct))); + } + } + // The rewrite relevance checker may have told us to run on the query but + // there may be multiple aggregate scans in the query and this one doesn't + // have any ORDER BY or LIMIT to rewrite away. + if (!any_rewritten) { + // Take back the original column list to avoid reordering it. + return std::move(builder).set_column_list(std::move(column_list)).Build(); + } + + // Columns that aren't involved in an aggregate need to be in our transformed + // builder. For example in `SELECT a, ARRAY_AGG(b ORDER BY b)`, the column `a` + // needs to be in the transformed builder. + for (const ResolvedColumn& column : column_list) { + if (!seen_columns.contains(column)) { + builder.add_column_list(column); + } + } + return std::move(project_scan_builder) + .set_input_scan(std::move(builder)) + .Build(); +} + +// Aggregate functions we support in this rewriter. +enum class AggFunction { + kArrayAgg, + kStringAgg, + kArrayConcatAgg, +}; + +// Intermediate state while processing an aggregate function call. +struct AggregateFunctionState { + ColumnFactory& column_factory; + TypeFactory& type_factory; + FunctionCallBuilder& function_builder; + + // The non-correlated column references in the original aggregate function + // call. + const std::vector> refs; + // The result type of the original aggregate function call. + const Type* const result_type = nullptr; + // The column that holds the aggregate's first argument. It's the original + // value: most users will want to get the current column using + // `column_map_.at(original_arg_column_)`. + const ResolvedColumn original_arg_column; +}; + +absl::StatusOr> +ProjectColumnsFromStructAndComputeArg( + const AggregateFunctionState& state, const ResolvedColumn& array_column, + std::unique_ptr arg, + absl::flat_hash_map& column_map) { + ResolvedColumn struct_column = state.column_factory.MakeCol( + kRewriter, "$struct", array_column.type()->AsArray()->element_type()); + ResolvedProjectScanBuilder project_scan_builder = + ResolvedProjectScanBuilder().set_input_scan( + ResolvedArrayScanBuilder() + .add_column_list(struct_column) + .add_array_expr_list(ResolvedColumnRefBuilder() + .set_column(array_column) + .set_type(array_column.type()) + .set_is_correlated(true)) + .add_element_column_list(struct_column)); + int idx = 0; + for (const std::unique_ptr& ref : state.refs) { + ResolvedColumn new_col = column_map.at(ref->column()); + project_scan_builder.add_column_list(new_col); + project_scan_builder.add_expr_list( + ResolvedComputedColumnBuilder().set_column(new_col).set_expr( + ResolvedGetStructFieldBuilder() + .set_type(new_col.type()) + .set_expr(MakeResolvedColumnRef(struct_column.type(), + struct_column, + /*is_correlated=*/false)) + .set_field_idx(idx++))); + } + std::unique_ptr input_scan; + if (project_scan_builder.column_list().empty()) { + // An aggregate that doesn't reference any real columns, like + // `ARRAY_AGG(1 LIMIT 100)` + // We keep the array scan builder so that the containing subquery's + // parameter list is constant. + input_scan = project_scan_builder.release_input_scan(); + } else { + ZETASQL_ASSIGN_OR_RETURN(input_scan, std::move(project_scan_builder).Build()); + } + if (arg->Is() && + state.original_arg_column == arg->GetAs()->column()) { + return std::move(input_scan); + } + // We will always access arg_column through column_map_. It's a unique + // column so it won't interfere with any existing refs. + column_map[state.original_arg_column] = state.original_arg_column; + std::vector column_list = input_scan->column_list(); + return ResolvedProjectScanBuilder() + .set_column_list(std::move(column_list)) + .add_column_list(column_map.at(state.original_arg_column)) + .add_expr_list( + ResolvedComputedColumnBuilder() + .set_column(column_map.at(state.original_arg_column)) + .set_expr(RemapSpecifiedColumns(std::move(arg), column_map))) + .set_input_scan(std::move(input_scan)) + .Build(); +} + +absl::StatusOr> HandleNullHandlingModifier( + const AggregateFunctionState& state, + std::unique_ptr input_scan, + ResolvedNonScalarFunctionCallBase::NullHandlingModifier + null_handling_modifier, + absl::flat_hash_map& column_map) { + switch (null_handling_modifier) { + case ResolvedNonScalarFunctionCallBase::DEFAULT_NULL_HANDLING: + case ResolvedNonScalarFunctionCallBase::RESPECT_NULLS: + return input_scan; + case ResolvedNonScalarFunctionCallBase::IGNORE_NULLS: { + std::vector column_list = input_scan->column_list(); + return ResolvedFilterScanBuilder() + .set_column_list(std::move(column_list)) + .set_input_scan(std::move(input_scan)) + .set_filter_expr( + state.function_builder.IsNotNull(MakeResolvedColumnRef( + column_map.at(state.original_arg_column).type(), + column_map.at(state.original_arg_column), + /*is_correlated=*/false))) + .Build(); + } + } +} + +// Implementing DISTINCT is accomplished by a ResolvedAggregateScan with no +// aggregate function that groups the argument column. +absl::StatusOr> HandleDistinct( + const AggregateFunctionState& state, + std::unique_ptr input_scan, bool distinct, + absl::flat_hash_map& column_map) { + if (!distinct) { + return input_scan; + } + // If DISTINCT is present, we need to end up with the argument of the + // aggregate as the only column. + // + // If there is no order by clause, no other column is needed: the argument to + // limit must be a constant. If there is an order by clause, why is it okay to + // project away the other columns? If there is DISTINCT and an ORDER BY clause + // the order by list must only refer to the argument to the aggregate. The + // resolver will create a ResolvedColumnRef for the shared expressions so + // there should only be one column in the incoming column_list. + if (input_scan->column_list().size() > 1) { + ZETASQL_ASSIGN_OR_RETURN( + input_scan, + ResolvedProjectScanBuilder() + .add_column_list(column_map.at(state.original_arg_column)) + .set_input_scan(std::move(input_scan)) + .Build()); + } + + ResolvedAggregateScanBuilder aggregate_scan_builder; + ResolvedColumn arg_column = column_map.at(state.original_arg_column); + ResolvedColumn distinct_arg = state.column_factory.MakeCol( + kRewriter, "distinct.arg", arg_column.type()); + aggregate_scan_builder.add_column_list(distinct_arg); + aggregate_scan_builder.add_group_by_list(MakeResolvedComputedColumn( + distinct_arg, MakeResolvedColumnRef(arg_column.type(), arg_column, + /*is_correlated=*/false))); + // There's only one column now. + column_map = {{state.original_arg_column, distinct_arg}}; + return std::move(aggregate_scan_builder) + .set_input_scan(std::move(input_scan)) + .Build(); +} + +absl::StatusOr> HandleOrderByIfPresent( + const AggregateFunctionState& state, + std::unique_ptr input_scan, + std::vector> + original_order_by_list, + absl::flat_hash_map& column_map) { + // Remap the order by items using `column_map`. + std::vector> order_by_item_list; + for (std::unique_ptr& order_by_item : + std::move(original_order_by_list)) { + ZETASQL_ASSIGN_OR_RETURN(order_by_item, RemapSpecifiedColumns( + std::move(order_by_item), column_map)); + order_by_item_list.push_back(std::move(order_by_item)); + } + + // Whether or not there is an ORDER BY clause, we need to project out the + // argument since the ARRAY subquery expects just one column. + if (order_by_item_list.empty()) { + // This project scan isn't necessary for the ResolvedAST but it allows the + // SQLBuilder to see that we're projecting away everything but arg_column. + return ResolvedProjectScanBuilder() + .add_column_list(column_map.at(state.original_arg_column)) + .set_input_scan(std::move(input_scan)) + .Build(); + } else { + // Some ORDER BY items may be correlated which is not supported in some + // engines. Create equivalent non-correlated columns for those items. + ResolvedProjectScanBuilder project_scan_builder; + bool any_correlated = false; + for (std::unique_ptr& order_by_item : + order_by_item_list) { + if (order_by_item->column_ref()->is_correlated()) { + any_correlated = true; + ResolvedColumn new_col = state.column_factory.MakeCol( + kRewriter, + absl::StrCat("non_correlated.", + order_by_item->column_ref()->column().name()), + order_by_item->column_ref()->type()); + project_scan_builder.add_column_list(new_col); + project_scan_builder.add_expr_list( + ResolvedComputedColumnBuilder().set_column(new_col).set_expr( + MakeResolvedColumnRef(order_by_item->column_ref()->type(), + order_by_item->column_ref()->column(), + /*is_correlated=*/true))); + ZETASQL_ASSIGN_OR_RETURN( + order_by_item, + ToBuilder(std::move(order_by_item)) + .set_column_ref(MakeResolvedColumnRef(new_col.type(), new_col, + /*is_correlated=*/false)) + .Build()); + } else { + project_scan_builder.add_column_list( + order_by_item->column_ref()->column()); + } + } + if (any_correlated) { + ZETASQL_ASSIGN_OR_RETURN(input_scan, std::move(project_scan_builder) + .set_input_scan(std::move(input_scan)) + .Build()); + } + return ResolvedOrderByScanBuilder() + .add_column_list(column_map.at(state.original_arg_column)) + .set_input_scan(std::move(input_scan)) + .set_is_ordered(true) + .set_order_by_item_list(std::move(order_by_item_list)) + .Build(); + } +} + +absl::StatusOr> HandleLimit( + const AggregateFunctionState& state, + std::unique_ptr input_scan, + std::unique_ptr limit, + absl::flat_hash_map& column_map) { + if (limit == nullptr) { + return input_scan; + } + return ResolvedLimitOffsetScanBuilder() + .add_column_list(column_map.at(state.original_arg_column)) + .set_input_scan(std::move(input_scan)) + .set_limit(std::move(limit)) + .set_offset(MakeResolvedLiteral(Value::Int64(0))) + .Build(); +} + +absl::StatusOr> MakeArrayExpr( + const AggregateFunctionState& state, + std::unique_ptr input_scan, + const ResolvedColumn& array_column, + std::vector> correlated_refs) { + // If the input to ARRAY_AGG/STRING_AGG/ARRAY_CONCAT_AGG is empty, we need + // to return NULL. If the input to an ARRAY subquery is empty, the return is + // the empty list. In order to convert between them we produce NULL if the + // ARRAY_LENGTH is 0. + ZETASQL_RET_CHECK_EQ(input_scan->column_list().size(), 1); + const Type* array_result_type = nullptr; + ZETASQL_RETURN_IF_ERROR(state.type_factory.MakeArrayType( + input_scan->column_list()[0].type(), &array_result_type)); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr array_subquery, + ResolvedSubqueryExprBuilder() + .set_subquery_type(ResolvedSubqueryExpr::ARRAY) + .set_subquery(std::move(input_scan)) + .set_parameter_list(std::move(correlated_refs)) + .add_parameter_list(ResolvedColumnRefBuilder() + .set_column(array_column) + .set_type(array_column.type()) + .set_is_correlated(false)) + .set_type(array_result_type) + .Build()); + return state.function_builder.MakeNullIfEmptyArray(state.column_factory, + std::move(array_subquery)); +} + +absl::StatusOr> MakeArrayConcatExpr( + const AggregateFunctionState& state, + std::unique_ptr input_scan, + const ResolvedColumn& array_column, + std::vector> correlated_refs, + absl::flat_hash_map& column_map) { + // We cannot build an array of arrays, so we pack up our array into a struct + const StructType* array_concat_struct_type = nullptr; + ResolvedColumn arg_column = column_map.at(state.original_arg_column); + ZETASQL_RETURN_IF_ERROR(state.type_factory.MakeStructType( + {{"array", arg_column.type()}}, &array_concat_struct_type)); + ResolvedColumn struct_column = state.column_factory.MakeCol( + kRewriter, "$struct", array_concat_struct_type); + ZETASQL_ASSIGN_OR_RETURN( + input_scan, + ResolvedProjectScanBuilder() + .add_column_list(struct_column) + .add_expr_list( + ResolvedComputedColumnBuilder() + .set_column(struct_column) + .set_expr(ResolvedMakeStructBuilder() + .set_type(array_concat_struct_type) + .add_field_list(MakeResolvedColumnRef( + arg_column.type(), arg_column, + /*is_correlated=*/false)))) + .set_input_scan(std::move(input_scan)) + .Build()); + + // We wrapped every child array in a struct above; unwrap them. + return ResolvedFlattenBuilder() + .set_type(state.result_type) + .set_expr(MakeArrayExpr(state, std::move(input_scan), array_column, + std::move(correlated_refs))) + .add_get_field_list(ResolvedGetStructFieldBuilder() + .set_type(state.result_type) + .set_expr(ResolvedFlattenedArgBuilder().set_type( + array_concat_struct_type)) + .set_field_idx(0)) + .Build(); +} + +absl::StatusOr> GenerateAggregate( + AggFunction agg_function, const AggregateFunctionState& state, + std::unique_ptr input_scan, + const ResolvedColumn& array_column, + std::vector> correlated_refs, + std::unique_ptr arg2, + absl::flat_hash_map& column_map) { + switch (agg_function) { + case AggFunction::kArrayAgg: { + return MakeArrayExpr(state, std::move(input_scan), array_column, + std::move(correlated_refs)); + } + case AggFunction::kStringAgg: { + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr array_expr, + MakeArrayExpr(state, std::move(input_scan), array_column, + std::move(correlated_refs))); + // We don't have to worry about replacing the columns in arg2 like we did + // for arg1 because arg2 (the delimiter) must be a literal. + return state.function_builder.ArrayToString(std::move(array_expr), + std::move(arg2)); + break; + } + case AggFunction::kArrayConcatAgg: { + return MakeArrayConcatExpr(state, std::move(input_scan), array_column, + std::move(correlated_refs), column_map); + } + } +} + +absl::StatusOr> +GenerateArrayOfStruct( + const AggregateFunctionState& state, + std::unique_ptr having_modifier, + absl::flat_hash_map& column_map) { + // Create `ARRAY_AGG(STRUCT() HAVING MAX g)` + std::vector struct_fields; + std::vector> struct_fields_exprs; + for (int i = 0; i < state.refs.size(); ++i) { + const std::unique_ptr& ref = state.refs[i]; + struct_fields.emplace_back(absl::StrCat(ref->column().name(), "_", i), + ref->column().type()); + struct_fields_exprs.push_back(MakeResolvedColumnRef( + ref->column().type(), ref->column(), ref->is_correlated())); + column_map[ref->column()] = state.column_factory.MakeCol( + kRewriter, ref->column().name(), ref->column().type()); + } + const StructType* struct_type = nullptr; + ZETASQL_RETURN_IF_ERROR(state.type_factory.MakeStructType( + absl::MakeConstSpan(struct_fields), &struct_type)); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr struct_arg, + ResolvedMakeStructBuilder() + .set_type(struct_type) + .set_field_list(std::move(struct_fields_exprs)) + .Build()); + std::unique_ptr having_expr = nullptr; + ResolvedAggregateHavingModifier::HavingModifierKind having_kind = + ResolvedAggregateHavingModifier::INVALID; + if (having_modifier != nullptr) { + having_kind = having_modifier->kind(); + having_expr = ToBuilder(std::move(having_modifier)).release_having_expr(); + } + return state.function_builder.ArrayAgg(std::move(struct_arg), + std::move(having_expr), having_kind); +} + +// Given an aggregate function call, return AggregateFunctionCallRewriteResult. +absl::StatusOr +OrderByAndLimitInAggregateRewriterVisitor::HandleAggregateFunctionCall( + std::unique_ptr aggregate_function_call) { + if (aggregate_function_call->order_by_item_list_size() == 0 && + aggregate_function_call->limit() == nullptr) { + return AggregateFunctionCallRewriteResult{ + .array_of_struct = std::move(aggregate_function_call), + .is_rewritten = false}; + } + AggFunction agg_function; + if (aggregate_function_call->function()->IsZetaSQLBuiltin(FN_ARRAY_AGG)) { + agg_function = AggFunction::kArrayAgg; + } else if (aggregate_function_call->function()->IsZetaSQLBuiltin( + FN_STRING_AGG_STRING)) { + agg_function = AggFunction::kStringAgg; + } else if (aggregate_function_call->function()->IsZetaSQLBuiltin( + FN_ARRAY_CONCAT_AGG)) { + agg_function = AggFunction::kArrayConcatAgg; + } else { + return absl::UnimplementedError( + "Only ARRAY_AGG, STRING_AGG and ARRAY_CONCAT_AGG are supported in " + "ORDER_BY_AND_LIMIT_IN_AGGREGATE rewriter"); + } + if (aggregate_function_call->with_group_rows_subquery() != nullptr) { + return absl::UnimplementedError( + "WITH GROUP_ROWS subqueries are not supported in " + "ORDER_BY_AND_LIMIT_IN_AGGREGATE rewriter"); + } + + // Columns used in the HAVING clause are used in the same context as the + // original aggregates so we don't need any special handling for the refs they + // contain. Release the having modifier before CollectSortUniqueColumnRefs. + std::unique_ptr having_modifier = + aggregate_function_call->release_having_modifier(); + std::vector> all_refs; + ZETASQL_RETURN_IF_ERROR(CollectSortUniqueColumnRefs(*aggregate_function_call, + all_refs, + /*correlate=*/false)); + std::vector> refs; + std::vector> correlated_refs; + for (std::unique_ptr& ref : all_refs) { + if (ref->is_correlated()) { + correlated_refs.push_back(std::move(ref)); + } else { + refs.push_back(std::move(ref)); + } + } + + const Type* const result_type = aggregate_function_call->type(); + switch (agg_function) { + case AggFunction::kStringAgg: + case AggFunction::kArrayConcatAgg: + // STRING_AGG and ARRAY_CONCAT_AGG don't accept a null handling modifier + // but implicitly ignore nulls. + aggregate_function_call->set_null_handling_modifier( + ResolvedNonScalarFunctionCallBase::IGNORE_NULLS); + break; + case AggFunction::kArrayAgg: + break; + } + + std::vector> args = + aggregate_function_call->release_argument_list(); + std::unique_ptr arg1 = std::move(args[0]); + std::unique_ptr arg2; + switch (agg_function) { + case AggFunction::kArrayAgg: + case AggFunction::kArrayConcatAgg: + ZETASQL_RET_CHECK_EQ(args.size(), 1); + break; + case AggFunction::kStringAgg: + if (args.size() == 1) { + // We will call ARRAY_TO_STRING which doesn't have an optional delimiter + // argument. STRING_AGG's default is ",". + if (arg1->type()->IsString()) { + arg2 = MakeResolvedLiteral(Value::String(",")); + } else { + arg2 = MakeResolvedLiteral(Value::Bytes(",")); + } + } else { + ZETASQL_RET_CHECK_EQ(args.size(), 2); + arg2 = std::move(args[1]); + } + break; + } + + if (aggregate_function_call->distinct() && + !aggregate_function_call->order_by_item_list().empty()) { + ZETASQL_RET_CHECK(arg1->Is()) + << "The argument must be a ResolvedColumnRef if DISTINCT and ORDER BY " + "are both present"; + for (const std::unique_ptr& order_by_item : + aggregate_function_call->order_by_item_list()) { + ZETASQL_RET_CHECK(arg1->GetAs()->column() == + order_by_item->column_ref()->column()) + << "Order by item is referring to a column other than the argument " + "column. First arg: " + << arg1->DebugString() + << "; order by item: " << order_by_item->DebugString(); + ZETASQL_RET_CHECK(!arg1->GetAs()->is_correlated()); + ZETASQL_RET_CHECK(!order_by_item->column_ref()->is_correlated()); + } + } + AggregateFunctionState state{ + column_factory_, type_factory_, function_builder_, std::move(refs), + result_type, + /*original_arg_column=*/ + arg1->Is() && + !arg1->GetAs()->is_correlated() + ? arg1->GetAs()->column() + : column_factory_.MakeCol(kRewriter, "$arg", arg1->type())}; + absl::flat_hash_map column_map; + ZETASQL_ASSIGN_OR_RETURN( + std::unique_ptr array_of_struct, + GenerateArrayOfStruct(state, std::move(having_modifier), column_map)); + ResolvedColumn array_column = + column_factory_.MakeCol(kRewriter, "$array", array_of_struct->type()); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input_scan, + ProjectColumnsFromStructAndComputeArg( + state, array_column, std::move(arg1), column_map)); + ZETASQL_ASSIGN_OR_RETURN( + input_scan, + HandleNullHandlingModifier( + state, std::move(input_scan), + aggregate_function_call->null_handling_modifier(), column_map)); + ZETASQL_ASSIGN_OR_RETURN( + input_scan, + HandleDistinct(state, std::move(input_scan), + aggregate_function_call->distinct(), column_map)); + ZETASQL_ASSIGN_OR_RETURN( + input_scan, + HandleOrderByIfPresent( + state, std::move(input_scan), + aggregate_function_call->release_order_by_item_list(), column_map)); + ZETASQL_ASSIGN_OR_RETURN( + input_scan, + HandleLimit(state, std::move(input_scan), + aggregate_function_call->release_limit(), column_map)); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, + GenerateAggregate(agg_function, state, std::move(input_scan), + array_column, std::move(correlated_refs), + std::move(arg2), column_map)); + return AggregateFunctionCallRewriteResult{ + .array_column = std::move(array_column), + .array_of_struct = std::move(array_of_struct), + .result = std::move(result), + .is_rewritten = true}; +} + +class OrderByAndLimitInAggregateRewriter : public Rewriter { + absl::StatusOr> Rewrite( + const AnalyzerOptions& options, std::unique_ptr input, + Catalog& catalog, TypeFactory& type_factory, + AnalyzerOutputProperties& output_properties) const override { + ZETASQL_RET_CHECK(options.column_id_sequence_number() != nullptr); + ColumnFactory column_factory(/*max_seen_col_id=*/0, + *options.id_string_pool(), + *options.column_id_sequence_number()); + OrderByAndLimitInAggregateRewriterVisitor visitor( + options, catalog, column_factory, type_factory); + return visitor.VisitAll(std::move(input)); + } + + std::string Name() const override { + return "OrderByAndLimitInAggregateRewriter"; + } +}; + +} // namespace + +const Rewriter* GetOrderByAndLimitInAggregateRewriter() { + static const auto* const kRewriter = new OrderByAndLimitInAggregateRewriter; + return kRewriter; +} + +} // namespace zetasql diff --git a/zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.h b/zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.h new file mode 100644 index 000000000..7d324141d --- /dev/null +++ b/zetasql/analyzer/rewriters/order_by_and_limit_in_aggregate_rewriter.h @@ -0,0 +1,29 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_ANALYZER_REWRITERS_ORDER_BY_AND_LIMIT_IN_AGGREGATE_REWRITER_H_ +#define ZETASQL_ANALYZER_REWRITERS_ORDER_BY_AND_LIMIT_IN_AGGREGATE_REWRITER_H_ + +#include "zetasql/public/rewriter_interface.h" + +namespace zetasql { + +// Gets a pointer to the order by and limit in aggregate rewriter. +const Rewriter* GetOrderByAndLimitInAggregateRewriter(); + +} // namespace zetasql + +#endif // ZETASQL_ANALYZER_REWRITERS_ORDER_BY_AND_LIMIT_IN_AGGREGATE_REWRITER_H_ diff --git a/zetasql/analyzer/rewriters/pipe_assert_rewriter.cc b/zetasql/analyzer/rewriters/pipe_assert_rewriter.cc new file mode 100644 index 000000000..538428c3f --- /dev/null +++ b/zetasql/analyzer/rewriters/pipe_assert_rewriter.cc @@ -0,0 +1,138 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/analyzer/rewriters/pipe_assert_rewriter.h" + +#include +#include +#include +#include + +#include "zetasql/public/analyzer_options.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/function.h" +#include "zetasql/public/rewriter_interface.h" +#include "zetasql/public/types/type_factory.h" +#include "zetasql/public/value.h" +#include "zetasql/resolved_ast/resolved_ast.h" +#include "zetasql/resolved_ast/resolved_ast_builder.h" +#include "zetasql/resolved_ast/resolved_ast_rewrite_visitor.h" +#include "zetasql/resolved_ast/resolved_column.h" +#include "zetasql/resolved_ast/resolved_node.h" +#include "zetasql/resolved_ast/rewrite_utils.h" +#include "absl/status/statusor.h" +#include "zetasql/base/ret_check.h" +#include "zetasql/base/status_macros.h" + +namespace zetasql { + +namespace { + +// Rewriter for AssertScan. +// +// Pipe ASSERT operators are rewritten using FilterScans and BarrierScans. For +// example, the following AssertScan: +// +// FROM Table +// |> ASSERT , +// +// is rewritten as: +// +// FROM Table +// |> Barrier +// |> WHERE IF(condition, TRUE, ERROR(error_message)) +// |> Barrier +// +// where "Barrier" is a fake syntax representing a BarrierScan. +class PipeAssertRewriteVisitor : public ResolvedASTRewriteVisitor { + public: + PipeAssertRewriteVisitor(const AnalyzerOptions& analyzer_options, + Catalog& catalog, TypeFactory& type_factory) + : fn_builder_(analyzer_options, catalog, type_factory) {} + + absl::StatusOr> + PostVisitResolvedAssertScan( + std::unique_ptr node) override { + ResolvedColumnList column_list = node->column_list(); + ResolvedAssertScanBuilder assert_scan_builder = ToBuilder(std::move(node)); + + // Construct the error message corresponding to the SQL: + // CONCAT("Assert failed: ", ) + // + // The resolver guarantees the is not NULL. + std::vector> concat_args; + concat_args.push_back( + MakeResolvedLiteral(values::String("Assert failed: "))); + concat_args.push_back(assert_scan_builder.release_message()); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr concat_expr, + fn_builder_.Concat(std::move(concat_args))); + + // ERROR(error_message) + ZETASQL_ASSIGN_OR_RETURN( + std::unique_ptr error_expr, + fn_builder_.Error(std::move(concat_expr), types::BoolType())); + + // IF(condition, TRUE, ERROR(error_message)) + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr if_expr, + fn_builder_.If(assert_scan_builder.release_condition(), + MakeResolvedLiteral(values::True()), + std::move(error_expr))); + + // FROM Table + // |> Barrier + // |> WHERE IF(condition, TRUE, ERROR(error_message)) + // |> Barrier + return ResolvedBarrierScanBuilder() + .set_column_list(column_list) + .set_input_scan( + ResolvedFilterScanBuilder() + .set_column_list(column_list) + .set_input_scan( + ResolvedBarrierScanBuilder() + .set_column_list(column_list) + .set_input_scan( + assert_scan_builder.release_input_scan())) + .set_filter_expr(std::move(if_expr))) + .Build(); + } + + private: + FunctionCallBuilder fn_builder_; +}; + +} // namespace + +class PipeAssertRewriter : public Rewriter { + public: + std::string Name() const override { return "PipeAssertRewriter"; } + + absl::StatusOr> Rewrite( + const AnalyzerOptions& options, std::unique_ptr input, + Catalog& catalog, TypeFactory& type_factory, + AnalyzerOutputProperties& output_properties) const override { + ZETASQL_RET_CHECK(options.id_string_pool() != nullptr); + ZETASQL_RET_CHECK(options.column_id_sequence_number() != nullptr); + PipeAssertRewriteVisitor rewriter(options, catalog, type_factory); + return rewriter.VisitAll(std::move(input)); + }; +}; + +const Rewriter* GetPipeAssertRewriter() { + static const auto* const kRewriter = new PipeAssertRewriter(); + return kRewriter; +} + +} // namespace zetasql diff --git a/zetasql/analyzer/rewriters/pipe_assert_rewriter.h b/zetasql/analyzer/rewriters/pipe_assert_rewriter.h new file mode 100644 index 000000000..a8031541b --- /dev/null +++ b/zetasql/analyzer/rewriters/pipe_assert_rewriter.h @@ -0,0 +1,28 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_ANALYZER_REWRITERS_PIPE_ASSERT_REWRITER_H_ +#define ZETASQL_ANALYZER_REWRITERS_PIPE_ASSERT_REWRITER_H_ + +#include "zetasql/public/rewriter_interface.h" + +namespace zetasql { + +const Rewriter* GetPipeAssertRewriter(); + +} // namespace zetasql + +#endif // ZETASQL_ANALYZER_REWRITERS_PIPE_ASSERT_REWRITER_H_ diff --git a/zetasql/analyzer/rewriters/pivot_rewriter.cc b/zetasql/analyzer/rewriters/pivot_rewriter.cc index 63ac16fec..ffa368e47 100644 --- a/zetasql/analyzer/rewriters/pivot_rewriter.cc +++ b/zetasql/analyzer/rewriters/pivot_rewriter.cc @@ -460,17 +460,6 @@ absl::Status PivotRewriterVisitor::VerifyAggregateFunctionIsSupported( if (IgnoresNullArguments(call)) { return absl::OkStatus(); } - // This rewriter previously allowed any non-builtin function, regardless - // of whether its NULL-handling behavior was known. If this feature is - // enabled, preserve the old behavior and result in a warning instead of - // an error. - if (!call->function()->IsZetaSQLBuiltin() && - analyzer_options_.language().LanguageFeatureEnabled( - FEATURE_DISABLE_PIVOT_REWRITER_UDA_ERRORS)) { - // TODO: Generate warning that PIVOT rewriter cannot - // guarantee correct results. - return absl::OkStatus(); - } // The function call cannot be supported because it does/might respect // NULL inputs, which would break our rewrite strategy of replacing the // input argument with NULL when the pivot value does not match. Provide a diff --git a/zetasql/analyzer/rewriters/rewriter_relevance_checker.cc b/zetasql/analyzer/rewriters/rewriter_relevance_checker.cc index 29e40bd85..ff501d76d 100644 --- a/zetasql/analyzer/rewriters/rewriter_relevance_checker.cc +++ b/zetasql/analyzer/rewriters/rewriter_relevance_checker.cc @@ -79,12 +79,14 @@ class RewriteApplicabilityChecker : public ResolvedASTVisitor { absl::Status VisitResolvedAnonymizedAggregateScan( const ResolvedAnonymizedAggregateScan* node) override { + ZETASQL_RETURN_IF_ERROR(VisitResolvedAggregateScanBasePrivate(node)); applicable_rewrites_->insert(REWRITE_ANONYMIZATION); return DefaultVisit(node); } absl::Status VisitResolvedDifferentialPrivacyAggregateScan( const ResolvedDifferentialPrivacyAggregateScan* node) override { + ZETASQL_RETURN_IF_ERROR(VisitResolvedAggregateScanBasePrivate(node)); applicable_rewrites_->insert(REWRITE_ANONYMIZATION); return DefaultVisit(node); } @@ -208,6 +210,12 @@ class RewriteApplicabilityChecker : public ResolvedASTVisitor { return DefaultVisit(node); } + absl::Status VisitResolvedAssertScan( + const ResolvedAssertScan* node) override { + applicable_rewrites_->insert(REWRITE_PIPE_ASSERT); + return DefaultVisit(node); + } + private: absl::btree_set* applicable_rewrites_; @@ -232,6 +240,10 @@ class RewriteApplicabilityChecker : public ResolvedASTVisitor { aggregate_func_call->function()->Is()) { applicable_rewrites_->insert(REWRITE_INLINE_SQL_UDAS); } + if (aggregate_func_call->order_by_item_list_size() > 0 || + aggregate_func_call->limit() != nullptr) { + applicable_rewrites_->insert(REWRITE_ORDER_BY_AND_LIMIT_IN_AGGREGATE); + } } return absl::OkStatus(); diff --git a/zetasql/analyzer/rewriters/sql_function_inliner.cc b/zetasql/analyzer/rewriters/sql_function_inliner.cc index 32a294fde..dba7dee17 100644 --- a/zetasql/analyzer/rewriters/sql_function_inliner.cc +++ b/zetasql/analyzer/rewriters/sql_function_inliner.cc @@ -401,7 +401,7 @@ class SqlFunctionInliner : public Rewriter { std::string Name() const override { return "SqlFunctionInliner"; } }; -// A visitor that replaces calls to SQL TDFs with the resolved function body. +// A visitor that replaces calls to SQL TVFs with the resolved function body. class SqlTableFunctionInlineVistor : public ResolvedASTDeepCopyVisitor { public: explicit SqlTableFunctionInlineVistor(ColumnFactory* column_factory) @@ -1004,7 +1004,7 @@ class SqlAggregateFunctionInlineVisitor : public ResolvedASTRewriteVisitor { ColumnFactory& column_factory_; }; -class SqlTvaInliner : public Rewriter { +class SqlUdaInliner : public Rewriter { public: absl::StatusOr> Rewrite( const AnalyzerOptions& options, std::unique_ptr input, @@ -1017,7 +1017,7 @@ class SqlTvaInliner : public Rewriter { return rewriter.VisitAll(std::move(input)); } - std::string Name() const override { return "SqlTvaInliner"; } + std::string Name() const override { return "SqlUdaInliner"; } }; } // namespace @@ -1033,7 +1033,7 @@ const Rewriter* GetSqlTvfInliner() { } const Rewriter* GetSqlAggregateInliner() { - static const auto* const kRewriter = new SqlTvaInliner; + static const auto* const kRewriter = new SqlUdaInliner; return kRewriter; } diff --git a/zetasql/analyzer/run_analyzer_test.cc b/zetasql/analyzer/run_analyzer_test.cc index 58f1d3fb9..dbcfe64a1 100644 --- a/zetasql/analyzer/run_analyzer_test.cc +++ b/zetasql/analyzer/run_analyzer_test.cc @@ -32,6 +32,7 @@ #include "google/protobuf/text_format.h" #include "zetasql/analyzer/analyzer_output_mutator.h" #include "zetasql/analyzer/analyzer_test_options.h" +#include "zetasql/common/internal_analyzer_output_properties.h" #include "zetasql/common/status_payload_utils.h" #include "zetasql/base/testing/status_matchers.h" #include "zetasql/common/unicode_utils.h" @@ -312,8 +313,6 @@ class AnalyzerTestRunner { // depending on the value of catalog_name. class CatalogHolder { public: - // "suppressed_functions" is a comma separated list of built-in function - // names to exclude when using "SampleCatalog". explicit CatalogHolder(Catalog* catalog) : catalog_(catalog) {} Catalog* catalog() { return catalog_; } @@ -1013,7 +1012,7 @@ class AnalyzerTestRunner { extracted_statement_properties, test_result); } - void TestMulti(const std::string& test_case, const AnalyzerOptions& options, + void TestMulti(absl::string_view test_case, const AnalyzerOptions& options, absl::string_view mode, Catalog* catalog, TypeFactory* type_factory, file_based_test_driver::RunTestCaseResult* test_result) { @@ -1349,7 +1348,7 @@ class AnalyzerTestRunner { } void HandleOneResult( - const std::string& test_case, const AnalyzerOptions& options, + absl::string_view test_case, const AnalyzerOptions& options, TypeFactory* type_factory, Catalog* catalog, absl::string_view mode, const absl::Status& status, const absl::Status& detailed_sig_mismatch_status, @@ -1678,6 +1677,31 @@ class AnalyzerTestRunner { } break; } + case RESOLVED_CREATE_TABLE_STMT: { + ZETASQL_ASSIGN_OR_RETURN( + std::unique_ptr table, + MakeTableFromCreateTable(*output->resolved_statement() + ->GetAs())); + const std::string table_name = table->Name(); + if (!database_entry->mutable_catalog()->AddOwnedTableIfNotPresent( + table_name, std::move(table))) { + return absl::InvalidArgumentError(absl::StrFormat( + "prepare_database duplicate table definition %s", table_name)); + } + break; + } + case RESOLVED_CREATE_TABLE_FUNCTION_STMT: { + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr tvf, + MakeTVFFromCreateTableFunction( + *output->resolved_statement() + ->GetAs())); + if (!database_entry->mutable_catalog() + ->AddOwnedTableValuedFunctionIfNotPresent(&tvf)) { + return absl::InvalidArgumentError(absl::StrFormat( + "prepare_database duplicate TVF definition %s", tvf->Name())); + } + break; + } default: return absl::InvalidArgumentError( absl::StrFormat("prepare_database does not support %s", @@ -2312,6 +2336,7 @@ class AnalyzerTestRunner { // haven't implemented any comparison yet. Some of these could do // full CompareNode comparison. case RESOLVED_ABORT_BATCH_STMT: + case RESOLVED_ALTER_CONNECTION_STMT: case RESOLVED_ALTER_ALL_ROW_ACCESS_POLICIES_STMT: case RESOLVED_ALTER_DATABASE_STMT: case RESOLVED_ALTER_MATERIALIZED_VIEW_STMT: @@ -2331,6 +2356,7 @@ class AnalyzerTestRunner { case RESOLVED_CALL_STMT: case RESOLVED_CLONE_DATA_STMT: case RESOLVED_COMMIT_STMT: + case RESOLVED_CREATE_CONNECTION_STMT: case RESOLVED_CREATE_EXTERNAL_TABLE_STMT: case RESOLVED_CREATE_FUNCTION_STMT: case RESOLVED_CREATE_MATERIALIZED_VIEW_STMT: @@ -2570,8 +2596,8 @@ class AnalyzerTestRunner { return true; } - bool ComparePath(const std::vector& output_path, - const std::vector& unparsed_path) { + bool ComparePath(absl::Span output_path, + absl::Span unparsed_path) { if (output_path.size() != unparsed_path.size()) { return false; } @@ -2638,8 +2664,7 @@ class AnalyzerTestRunner { // This is used so that we only test each statement kind once. static std::set statement_kinds_to_be_skipped = { // Skip EXPLAIN statements, which fail the below checks because they - // also - // require the explained statement kind to be supported. + // also require the explained statement kind to be supported. RESOLVED_EXPLAIN_STMT}; if (zetasql_base::ContainsKey(statement_kinds_to_be_skipped, kind)) return; @@ -2846,7 +2871,8 @@ class AnalyzerTestRunner { analyzer_output->undeclared_positional_parameters(); builder_options.catalog = catalog; builder_options.target_syntax = - analyzer_output->analyzer_output_properties().target_syntax_; + InternalAnalyzerOutputProperties::GetTargetSyntax( + analyzer_output->analyzer_output_properties()); const std::string positional_parameter_mode = test_case_options_.GetString(kUnparserPositionalParameterMode); if (positional_parameter_mode == "question_mark") { diff --git a/zetasql/analyzer/set_operation_resolver_base.cc b/zetasql/analyzer/set_operation_resolver_base.cc index 3ac69a291..e7c5fa28f 100644 --- a/zetasql/analyzer/set_operation_resolver_base.cc +++ b/zetasql/analyzer/set_operation_resolver_base.cc @@ -147,7 +147,7 @@ SetOperationResolverBase::GetSuperTypesOfSetOperation( absl::StatusOr SetOperationResolverBase::BuildFinalColumnList( - const std::vector& final_column_names, + absl::Span final_column_names, const std::vector& final_column_types, IdString table_name, std::function record_column_access) { ZETASQL_RET_CHECK_EQ(final_column_names.size(), final_column_types.size()); diff --git a/zetasql/analyzer/set_operation_resolver_base.h b/zetasql/analyzer/set_operation_resolver_base.h index 73f22ce0e..627692312 100644 --- a/zetasql/analyzer/set_operation_resolver_base.h +++ b/zetasql/analyzer/set_operation_resolver_base.h @@ -74,7 +74,7 @@ class SetOperationResolverBase { // REQUIRES: `final_column_names` and `final_column_types` must have the same // length. absl::StatusOr BuildFinalColumnList( - const std::vector& final_column_names, + absl::Span final_column_names, const std::vector& final_column_types, IdString table_name, std::function record_column_access); diff --git a/zetasql/analyzer/testdata/aggregation.test b/zetasql/analyzer/testdata/aggregation.test index 689b8ea6e..c583153ef 100644 --- a/zetasql/analyzer/testdata/aggregation.test +++ b/zetasql/analyzer/testdata/aggregation.test @@ -3059,6 +3059,7 @@ QueryStmt | +-Literal(type=INT64, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=169-209 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) == @@ -3072,6 +3073,8 @@ group by {{T.|}}KitchenSink.nested_value.nested_int64 having {{T.|}}KitchenSink.nested_value.nested_int64 > 0 order by {{T.|}}KitchenSink.nested_value.nested_int64 + 2 -- +ALTERNATION GROUP: T.,T.,T.,T. +-- QueryStmt +-output_column_list= | +-$query.$col1#5 AS `$col1` [INT64] @@ -3122,159 +3125,1062 @@ QueryStmt | +-Literal(type=INT64, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=177-220 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) -== - -select {{T.|}}KitchenSink.nested_value.nested_int64, count(n) -from TestTable T, T.KitchenSink.repeated_double_val n -group by {{T.|}}KitchenSink.nested_value.nested_int64 -having {{T.|}}KitchenSink.nested_value.nested_int64 > 0 -order by {{T.|}}KitchenSink.nested_value.nested_int64 + 2 +-- +ALTERNATION GROUP: T.,T.,T., -- QueryStmt +-output_column_list= -| +-$groupby.nested_int64#7 AS nested_int64 [INT64] -| +-$aggregate.$agg1#5 AS `$col2` [INT64] +| +-$query.$col1#5 AS `$col1` [INT64] +-query= +-OrderByScan - +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-column_list=[$query.$col1#5] +-is_ordered=TRUE +-input_scan= | +-ProjectScan - | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] | +-expr_list= - | | +-$orderbycol1#8 := + | | +-$orderbycol1#6 := | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) - | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) | | +-Literal(type=INT64, value=2) | +-input_scan= | +-FilterScan - | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] | +-input_scan= - | | +-AggregateScan - | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) | | +-input_scan= - | | | +-ProjectScan - | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] - | | | +-expr_list= - | | | | +-nested_int64#6 := - | | | | +-GetProtoField - | | | | +-type=INT64 - | | | | +-expr= - | | | | | +-GetProtoField - | | | | | +-type=PROTO - | | | | | +-expr= - | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) - | | | | | +-field_descriptor=nested_value - | | | | | +-default_value=NULL - | | | | +-field_descriptor=nested_int64 - | | | | +-default_value=88 - | | | +-input_scan= - | | | +-ArrayScan - | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] - | | | +-input_scan= - | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") - | | | +-array_expr_list= - | | | | +-GetProtoField - | | | | +-type=ARRAY - | | | | +-expr= - | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) - | | | | +-field_descriptor=repeated_double_val - | | | | +-default_value=[] - | | | +-element_column_list=[$array.n#4] - | | +-group_by_list= - | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) - | | +-aggregate_list= - | | +-$agg1#5 := - | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) - | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 | +-filter_expr= | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) - | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) | +-Literal(type=INT64, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=177-218 +-column_ref= - +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) -== - -# Accessing 'n' is not valid after grouping. -select {{T.|}}KitchenSink.nested_value.nested_int64, n -from TestTable T, T.KitchenSink.repeated_double_val n -group by {{T.|}}KitchenSink.nested_value.nested_int64 + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) -- ALTERNATION GROUPS: - T.,T. - T., + T.,T.,,T. + T.,,T.,T. + T.,T.,T. -- -ERROR: SELECT list expression references table alias n which is neither grouped nor aggregated [at 1:49] -select T.KitchenSink.nested_value.nested_int64, n - ^ +QueryStmt ++-output_column_list= +| +-$query.$col1#5 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=175-218 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) -- ALTERNATION GROUPS: - T. - --- -ERROR: SELECT list expression references table alias n which is neither grouped nor aggregated [at 1:47] -select KitchenSink.nested_value.nested_int64, n - ^ -== - -# GROUP BY struct field reference. -select sq.a -from (select as struct key as a, value as b from KeyValue) as sq -group by sq.a + T.,T.,, + T.,,T., + T.,T., -- QueryStmt +-output_column_list= -| +-$groupby.a#4 AS a [INT64] +| +-$query.$col1#5 AS `$col1` [INT64] +-query= - +-ProjectScan - +-column_list=[$groupby.a#4] + +-OrderByScan + +-column_list=[$query.$col1#5] + +-is_ordered=TRUE +-input_scan= - +-AggregateScan - +-column_list=[$groupby.a#4] - +-input_scan= - | +-ProjectScan - | +-column_list=[$make_struct.$struct#3] - | +-expr_list= - | | +-$struct#3 := - | | +-MakeStruct - | | +-type=STRUCT - | | +-field_list= - | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) - | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) - | +-input_scan= - | +-ProjectScan - | +-column_list=KeyValue.[Key#1, Value#2] - | +-input_scan= - | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) - +-group_by_list= - +-a#4 := - +-GetStructField - +-type=INT64 - +-expr= - | +-ColumnRef(type=STRUCT, column=$make_struct.$struct#3) - +-field_idx=0 -== - -# GROUP BY literal struct field reference. -select sq.a -from (select as struct 1 as a, 2 as b) as sq -group by sq.a + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=175-216 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +-- +ALTERNATION GROUPS: + T.,,,T. + T.,,T. + T.,T. -- QueryStmt +-output_column_list= -| +-$groupby.a#4 AS a [INT64] +| +-$query.$col1#5 AS `$col1` [INT64] +-query= - +-ProjectScan - +-column_list=[$groupby.a#4] + +-OrderByScan + +-column_list=[$query.$col1#5] + +-is_ordered=TRUE +-input_scan= - +-AggregateScan - +-column_list=[$groupby.a#4] - +-input_scan= - | +-ProjectScan + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=173-216 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +-- +ALTERNATION GROUPS: + T.,,, + T.,, + T., +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#5 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=173-214 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +-- +ALTERNATION GROUP: T. +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#5 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=171-214 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#5 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.nested_int64#4, $query.$col1#5] + | | +-expr_list= + | | | +-$col1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | +-group_by_list= + | | +-nested_int64#4 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-GetProtoField + | | | +-type=PROTO + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=nested_value + | | | +-default_value=NULL + | | +-field_descriptor=nested_int64 + | | +-default_value=88 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#4) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=171-212 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +== + +select {{T.|}}KitchenSink.nested_value.nested_int64, count(n) +from TestTable T, T.KitchenSink.repeated_double_val n +group by {{T.|}}KitchenSink.nested_value.nested_int64 +having {{T.|}}KitchenSink.nested_value.nested_int64 > 0 +order by {{T.|}}KitchenSink.nested_value.nested_int64 + 2 +-- +ALTERNATION GROUP: T.,T.,T.,T. +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=220-263 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUP: T.,T.,T., +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=220-261 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUPS: + T.,T.,,T. + T.,,T.,T. + T.,T.,T. +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=218-261 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUPS: + T.,T.,, + T.,,T., + T.,T., +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=218-259 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUPS: + T.,,,T. + T.,,T. + T.,T. +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=216-259 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUPS: + T.,,, + T.,, + T., +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=216-257 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUP: T. +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=214-257 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.nested_int64#7 AS nested_int64 [INT64] +| +-$aggregate.$agg1#5 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5, $orderby.$orderbycol1#8] + | +-expr_list= + | | +-$orderbycol1#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | | +-Literal(type=INT64, value=2) + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.nested_int64#7, $aggregate.$agg1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4, $pre_groupby.nested_int64#6] + | | | +-expr_list= + | | | | +-nested_int64#6 := + | | | | +-GetProtoField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-GetProtoField + | | | | | +-type=PROTO + | | | | | +-expr= + | | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | | +-field_descriptor=nested_value + | | | | | +-default_value=NULL + | | | | +-field_descriptor=nested_int64 + | | | | +-default_value=88 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[TestTable.KitchenSink#3, $array.n#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="T") + | | | +-array_expr_list= + | | | | +-GetProtoField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | | +-field_descriptor=repeated_double_val + | | | | +-default_value=[] + | | | +-element_column_list=[$array.n#4] + | | +-group_by_list= + | | | +-nested_int64#7 := ColumnRef(type=INT64, column=$pre_groupby.nested_int64#6) + | | +-aggregate_list= + | | +-$agg1#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + | | +-ColumnRef(type=DOUBLE, column=$array.n#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.nested_int64#7) + | +-Literal(type=INT64, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=214-255 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +== + +# Accessing 'n' is not valid after grouping. +select {{T.|}}KitchenSink.nested_value.nested_int64, n +from TestTable T, T.KitchenSink.repeated_double_val n +group by {{T.|}}KitchenSink.nested_value.nested_int64 +-- +ALTERNATION GROUPS: + T.,T. + T., +-- +ERROR: SELECT list expression references table alias n which is neither grouped nor aggregated [at 1:49] +select T.KitchenSink.nested_value.nested_int64, n + ^ +-- +ALTERNATION GROUPS: + T. + +-- +ERROR: SELECT list expression references table alias n which is neither grouped nor aggregated [at 1:47] +select KitchenSink.nested_value.nested_int64, n + ^ +== + +# GROUP BY struct field reference. +select sq.a +from (select as struct key as a, value as b from KeyValue) as sq +group by sq.a +-- +QueryStmt ++-output_column_list= +| +-$groupby.a#4 AS a [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.a#4] + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.a#4] + +-input_scan= + | +-ProjectScan + | +-column_list=[$make_struct.$struct#3] + | +-expr_list= + | | +-$struct#3 := + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-input_scan= + | +-ProjectScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-group_by_list= + +-a#4 := + +-GetStructField + +-type=INT64 + +-expr= + | +-ColumnRef(type=STRUCT, column=$make_struct.$struct#3) + +-field_idx=0 +== + +# GROUP BY literal struct field reference. +select sq.a +from (select as struct 1 as a, 2 as b) as sq +group by sq.a +-- +QueryStmt ++-output_column_list= +| +-$groupby.a#4 AS a [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.a#4] + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.a#4] + +-input_scan= + | +-ProjectScan | +-column_list=[$make_struct.$struct#3] | +-expr_list= | | +-$struct#3 := @@ -4089,86 +4995,466 @@ ERROR: Aggregate functions with DISTINCT cannot be used with arguments of type F select count(distinct int64), count(distinct float) from SimpleTypes ^ -- -ALTERNATION GROUP: +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [INT64] +| +-$aggregate.$agg2#21 AS `$col2` [INT64] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#20, $agg2#21] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#20, $agg2#21] + +-input_scan= + | +-TableScan(column_list=SimpleTypes.[int64#2, float#8], table=SimpleTypes, column_index_list=[1, 7]) + +-aggregate_list= + +-$agg1#20 := + | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | +-distinct=TRUE + +-$agg2#21 := + +-AggregateFunctionCall(ZetaSQL:count(FLOAT) -> INT64) + +-ColumnRef(type=FLOAT, column=SimpleTypes.float#8) + +-distinct=TRUE +== + +[language_features={{DISALLOW_GROUP_BY_FLOAT|}}] +select double, count(*) from SimpleTypes group by double +-- +ALTERNATION GROUP: DISALLOW_GROUP_BY_FLOAT +-- +ERROR: Grouping by expressions of type DOUBLE is not allowed [at 1:51] +select double, count(*) from SimpleTypes group by double + ^ +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.double#21 AS double [DOUBLE] +| +-$aggregate.$agg1#20 AS `$col2` [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.double#21, $aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.double#21, $aggregate.$agg1#20] + +-input_scan= + | +-TableScan(column_list=[SimpleTypes.double#9], table=SimpleTypes, column_index_list=[8]) + +-group_by_list= + | +-double#21 := ColumnRef(type=DOUBLE, column=SimpleTypes.double#9) + +-aggregate_list= + +-$agg1#20 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) +== + +select count(*), count(CAST(NULL AS STRUCT)) from KeyValue +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [INT64] +| +-$aggregate.$agg2#4 AS `$col2` [INT64] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#3, $agg2#4] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#3, $agg2#4] + +-input_scan= + | +-TableScan(table=KeyValue) + +-aggregate_list= + +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-$agg2#4 := + +-AggregateFunctionCall(ZetaSQL:count(STRUCT) -> INT64) + +-Literal(type=STRUCT, value=NULL, has_explicit_type=TRUE) +== + +SELECT DISTINCT MOD(int64, 10) int64_col, + CAST(MAX(int64+1) AS UINT64) uint64_col +FROM SimpleTypes +GROUP BY {{1|int64_col}} +ORDER BY {{1|int64_col}}, {{2|uint64_col}} +-- +ALTERNATION GROUP: 1,1,2 +-- +QueryStmt ++-output_column_list= +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] ++-query= + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=126-127 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=129-130 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) +-- +ALTERNATION GROUP: 1,1,uint64_col +-- +QueryStmt ++-output_column_list= +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] ++-query= + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=126-127 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=129-139 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) +-- +ALTERNATION GROUP: 1,int64_col,2 +-- +QueryStmt ++-output_column_list= +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] ++-query= + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=126-135 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=137-138 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) +-- +ALTERNATION GROUP: 1,int64_col,uint64_col -- QueryStmt +-output_column_list= -| +-$aggregate.$agg1#20 AS `$col1` [INT64] -| +-$aggregate.$agg2#21 AS `$col2` [INT64] +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] +-query= - +-ProjectScan - +-column_list=$aggregate.[$agg1#20, $agg2#21] + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE +-input_scan= - +-AggregateScan - +-column_list=$aggregate.[$agg1#20, $agg2#21] - +-input_scan= - | +-TableScan(column_list=SimpleTypes.[int64#2, float#8], table=SimpleTypes, column_index_list=[1, 7]) - +-aggregate_list= - +-$agg1#20 := - | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) - | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) - | +-distinct=TRUE - +-$agg2#21 := - +-AggregateFunctionCall(ZetaSQL:count(FLOAT) -> INT64) - +-ColumnRef(type=FLOAT, column=SimpleTypes.float#8) - +-distinct=TRUE -== - -[language_features={{DISALLOW_GROUP_BY_FLOAT|}}] -select double, count(*) from SimpleTypes group by double + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=126-135 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=137-147 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) -- -ALTERNATION GROUP: DISALLOW_GROUP_BY_FLOAT +ALTERNATION GROUP: int64_col,1,2 -- -ERROR: Grouping by expressions of type DOUBLE is not allowed [at 1:51] -select double, count(*) from SimpleTypes group by double - ^ +QueryStmt ++-output_column_list= +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] ++-query= + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=134-135 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=137-138 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) -- -ALTERNATION GROUP: +ALTERNATION GROUP: int64_col,1,uint64_col -- QueryStmt +-output_column_list= -| +-$groupby.double#21 AS double [DOUBLE] -| +-$aggregate.$agg1#20 AS `$col2` [INT64] +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] +-query= - +-ProjectScan - +-column_list=[$groupby.double#21, $aggregate.$agg1#20] + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE +-input_scan= - +-AggregateScan - +-column_list=[$groupby.double#21, $aggregate.$agg1#20] - +-input_scan= - | +-TableScan(column_list=[SimpleTypes.double#9], table=SimpleTypes, column_index_list=[8]) - +-group_by_list= - | +-double#21 := ColumnRef(type=DOUBLE, column=SimpleTypes.double#9) - +-aggregate_list= - +-$agg1#20 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) -== - -select count(*), count(CAST(NULL AS STRUCT)) from KeyValue + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=134-135 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=137-147 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) +-- +ALTERNATION GROUP: int64_col,int64_col,2 -- QueryStmt +-output_column_list= -| +-$aggregate.$agg1#3 AS `$col1` [INT64] -| +-$aggregate.$agg2#4 AS `$col2` [INT64] +| +-$distinct.int64_col#24 AS int64_col [INT64] +| +-$distinct.uint64_col#25 AS uint64_col [UINT64] +-query= - +-ProjectScan - +-column_list=$aggregate.[$agg1#3, $agg2#4] + +-OrderByScan + +-column_list=$distinct.[int64_col#24, uint64_col#25] + +-is_ordered=TRUE +-input_scan= - +-AggregateScan - +-column_list=$aggregate.[$agg1#3, $agg2#4] - +-input_scan= - | +-TableScan(table=KeyValue) - +-aggregate_list= - +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) - +-$agg2#4 := - +-AggregateFunctionCall(ZetaSQL:count(STRUCT) -> INT64) - +-Literal(type=STRUCT, value=NULL, has_explicit_type=TRUE) -== - -SELECT DISTINCT MOD(int64, 10) int64_col, - CAST(MAX(int64+1) AS UINT64) uint64_col -FROM SimpleTypes -GROUP BY {{1|int64_col}} -ORDER BY {{1|int64_col}}, {{2|uint64_col}} + | +-AggregateScan + | +-column_list=$distinct.[int64_col#24, uint64_col#25] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20, $query.uint64_col#23] + | | +-expr_list= + | | | +-uint64_col#23 := + | | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int64_col#22, $aggregate.$agg1#20] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[SimpleTypes.int64#2, $pre_groupby.int64_col#21] + | | | +-expr_list= + | | | | +-int64_col#21 := + | | | | +-FunctionCall(ZetaSQL:mod(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | | | +-Literal(type=INT64, value=10) + | | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int64#2], table=SimpleTypes, column_index_list=[1]) + | | +-group_by_list= + | | | +-int64_col#22 := ColumnRef(type=INT64, column=$pre_groupby.int64_col#21) + | | +-aggregate_list= + | | +-$agg1#20 := + | | +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) + | | +-Literal(type=INT64, value=1) + | +-group_by_list= + | +-int64_col#24 := ColumnRef(type=INT64, column=$groupby.int64_col#22) + | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=134-143 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) + +-OrderByItem + +-parse_location=145-146 + +-column_ref= + +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) +-- +ALTERNATION GROUP: int64_col,int64_col,uint64_col -- QueryStmt +-output_column_list= @@ -4214,9 +5500,11 @@ QueryStmt | +-uint64_col#25 := ColumnRef(type=UINT64, column=$query.uint64_col#23) +-order_by_item_list= +-OrderByItem + | +-parse_location=134-143 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.int64_col#24) +-OrderByItem + +-parse_location=145-155 +-column_ref= +-ColumnRef(type=UINT64, column=$distinct.uint64_col#25) == @@ -4631,9 +5919,11 @@ QueryStmt | | +-a#7 := ColumnRef(type=INT64, column=$groupby.a#4) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=107-108 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$distinct.c#6) | +-OrderByItem + | +-parse_location=110-111 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.a#7) +-input_scan= @@ -5569,6 +6859,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= | +-OrderByItem + | +-parse_location=47-50 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= @@ -5823,6 +7114,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= | +-OrderByItem + | +-parse_location=102-105 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= @@ -5854,6 +7146,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= | +-OrderByItem + | +-parse_location=102-105 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= @@ -5885,6 +7178,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= | +-OrderByItem + | +-parse_location=103-106 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= @@ -5916,6 +7210,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= | +-OrderByItem + | +-parse_location=103-106 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= @@ -6687,6 +7982,7 @@ QueryStmt | | +-$agg1#5 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=100-101 | +-column_ref= | +-ColumnRef(type=INT64, column=$aggregate.$agg1#5) +-limit= @@ -6748,6 +8044,7 @@ QueryStmt | +-$agg1#7 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) +-order_by_item_list= +-OrderByItem + +-parse_location=71-72 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#7) == @@ -6899,5 +8196,6 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=91-135 +-column_ref= - +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#4) + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#4) \ No newline at end of file diff --git a/zetasql/analyzer/testdata/alias_ambiguity.test b/zetasql/analyzer/testdata/alias_ambiguity.test index 592340c97..a995e109c 100644 --- a/zetasql/analyzer/testdata/alias_ambiguity.test +++ b/zetasql/analyzer/testdata/alias_ambiguity.test @@ -8,6 +8,93 @@ select {{key | keyvalue.key | key as key | keyvalue.key as key}} from keyvalue order by {{key | keyvalue.key}} -- +ALTERNATION GROUP: key ,key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=35-38 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: key , keyvalue.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=36-48 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: keyvalue.key ,key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=45-48 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: keyvalue.key , keyvalue.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=46-58 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: key as key ,key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=43-46 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: key as key , keyvalue.key +-- QueryStmt +-output_column_list= | +-KeyValue.Key#1 AS key [INT64] @@ -19,6 +106,41 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=44-56 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: keyvalue.key as key,key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=51-54 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: keyvalue.key as key, keyvalue.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=52-64 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -38,6 +160,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=51-55 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -65,6 +188,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=43-46 +-column_ref= +-ColumnRef(type=INT64, column=$query.key#3) == @@ -84,6 +208,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=43-48 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -123,6 +248,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-39 +-column_ref= +-ColumnRef(type=STRING, column=KeyValue.Value#2) == @@ -142,6 +268,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=43-46 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -571,6 +698,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=47-50 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.key#3) == @@ -580,6 +708,30 @@ from keyvalue group by 1 order by key -- +ALTERNATION GROUP: key +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#3 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#3] + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=46-49 + +-column_ref= + +-ColumnRef(type=INT64, column=$groupby.key#3) +-- +ALTERNATION GROUP: keyvalue.key +-- QueryStmt +-output_column_list= | +-$groupby.key#3 AS key [INT64] @@ -596,6 +748,51 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=56-59 + +-column_ref= + +-ColumnRef(type=INT64, column=$groupby.key#3) +-- +ALTERNATION GROUP: key as key +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#3 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#3] + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=54-57 + +-column_ref= + +-ColumnRef(type=INT64, column=$groupby.key#3) +-- +ALTERNATION GROUP: keyvalue.key as key +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#3 AS key [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#3] + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=62-65 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.key#3) == @@ -621,6 +818,7 @@ QueryStmt | +-key1#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=53-57 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.key1#3) == @@ -646,6 +844,7 @@ QueryStmt | +-value#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=54-59 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.value#3) == @@ -671,6 +870,7 @@ QueryStmt | +-key1#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=53-56 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.key1#3) == @@ -700,6 +900,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=52-60 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#4) == @@ -729,9 +930,11 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + | +-parse_location=52-55 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.key#3) +-OrderByItem + +-parse_location=57-65 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#4) == @@ -1015,6 +1218,45 @@ select {{kv|kv as kv}} from KeyValue kv order by kv.key -- +ALTERNATION GROUP: kv +-- +QueryStmt ++-output_column_list= +| +-$query.kv#4 AS kv [STRUCT] ++-query= + +-OrderByScan + +-column_list=[$query.kv#4] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $query.kv#4, $orderby.$orderbycol1#5] + | +-expr_list= + | | +-$orderbycol1#5 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$query.kv#4) + | | +-field_idx=0 + | +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $query.kv#4] + | +-expr_list= + | | +-kv#4 := + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") + +-order_by_item_list= + +-OrderByItem + +-parse_location=36-42 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) +-- +ALTERNATION GROUP: kv as kv +-- QueryStmt +-output_column_list= | +-$query.kv#4 AS kv [STRUCT] @@ -1046,6 +1288,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") +-order_by_item_list= +-OrderByItem + +-parse_location=42-48 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) == @@ -1116,6 +1359,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0], alias="kv") +-order_by_item_list= +-OrderByItem + +-parse_location=50-52 +-column_ref= +-ColumnRef(type=INT64, column=$query.kv#3) == @@ -1158,6 +1402,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=69-72 +-column_ref= +-ColumnRef(type=INT64, column=$query.key#4) == @@ -1202,6 +1447,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=78-81 +-column_ref= +-ColumnRef(type=INT64, column=$query.key#4) == @@ -1244,6 +1490,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=74-80 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -1277,6 +1524,7 @@ QueryStmt | +-element_column_list=[$array.v#4] +-order_by_item_list= +-OrderByItem + +-parse_location=71-72 +-column_ref= +-ColumnRef(type=INT32, column=$array.v#4) == @@ -1328,6 +1576,7 @@ QueryStmt | +-element_column_list=[$array.v#4] +-order_by_item_list= +-OrderByItem + +-parse_location=71-72 +-column_ref= +-ColumnRef(type=INT32, column=$array.v#4) == @@ -1497,4 +1746,4 @@ QueryStmt | +-input_scan= | +-SingleRowScan +-group_by_list= - +-a#2 := ColumnRef(type=INT64, column=$subquery1.a#1) + +-a#2 := ColumnRef(type=INT64, column=$subquery1.a#1) \ No newline at end of file diff --git a/zetasql/analyzer/testdata/analytic_function_named_window.test b/zetasql/analyzer/testdata/analytic_function_named_window.test index 638625668..fbff299f2 100644 --- a/zetasql/analyzer/testdata/analytic_function_named_window.test +++ b/zetasql/analyzer/testdata/analytic_function_named_window.test @@ -755,9 +755,11 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + | +-parse_location=339-359 | +-column_ref= | +-ColumnRef(type=INT64, column=$analytic.$analytic8#17) +-OrderByItem + +-parse_location=370-406 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic9#18) == diff --git a/zetasql/analyzer/testdata/analytic_function_partitionby_orderby.test b/zetasql/analyzer/testdata/analytic_function_partitionby_orderby.test index ef7faffdc..66d1eb8e8 100644 --- a/zetasql/analyzer/testdata/analytic_function_partitionby_orderby.test +++ b/zetasql/analyzer/testdata/analytic_function_partitionby_orderby.test @@ -67,6 +67,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +-order_by_item_list= +-OrderByItem + +-parse_location=59-523 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#27) == @@ -254,6 +255,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=59-570 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#27) == @@ -412,6 +414,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=39-91 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#3) == @@ -530,5 +533,6 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=53-109 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#5) diff --git a/zetasql/analyzer/testdata/analytic_functions.test b/zetasql/analyzer/testdata/analytic_functions.test index ad168a614..e92e9a7a9 100644 --- a/zetasql/analyzer/testdata/analytic_functions.test +++ b/zetasql/analyzer/testdata/analytic_functions.test @@ -663,6 +663,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=47-117 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#8) == @@ -775,6 +776,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=47-138 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#8) == @@ -1095,13 +1097,16 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + | +-parse_location=83-151 | +-column_ref= | +-ColumnRef(type=INT64, column=$analytic.$analytic1#7) +-OrderByItem + | +-parse_location=162-212 | +-column_ref= | | +-ColumnRef(type=INT64, column=$analytic.$analytic2#8) | +-is_descending=TRUE +-OrderByItem + +-parse_location=223-281 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol3#11) == @@ -1176,6 +1181,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=76-133 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#6) == @@ -1217,6 +1223,7 @@ QueryStmt | +-analyticcol#6 := ColumnRef(type=INT64, column=$analytic.analyticcol#4) +-order_by_item_list= +-OrderByItem + +-parse_location=87-98 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.analyticcol#6) == @@ -1258,9 +1265,11 @@ QueryStmt | +-analyticcol#6 := ColumnRef(type=INT64, column=$analytic.analyticcol#4) +-order_by_item_list= +-OrderByItem + | +-parse_location=87-88 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#5) +-OrderByItem + +-parse_location=90-91 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.analyticcol#6) == @@ -1813,12 +1822,15 @@ QueryStmt | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +-order_by_item_list= +-OrderByItem + | +-parse_location=46-72 | +-column_ref= | +-ColumnRef(type=INT64, column=$analytic.$analytic1#20) +-OrderByItem + | +-parse_location=83-128 | +-column_ref= | +-ColumnRef(type=INT64, column=$analytic.$analytic2#21) +-OrderByItem + +-parse_location=139-161 +-column_ref= +-ColumnRef(type=INT32, column=$analytic.$analytic3#22) == @@ -1871,6 +1883,7 @@ QueryStmt | +-$analytic1#22 := AnalyticFunctionCall(ZetaSQL:rank() -> INT64) +-order_by_item_list= +-OrderByItem + +-parse_location=94-120 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#22) == @@ -1909,6 +1922,7 @@ QueryStmt | +-$analytic1#21 := AnalyticFunctionCall(ZetaSQL:rank() -> INT64) +-order_by_item_list= +-OrderByItem + +-parse_location=69-97 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#21) == @@ -2531,6 +2545,7 @@ QueryStmt | +-$analytic1#4 := AnalyticFunctionCall(ZetaSQL:rank() -> INT64) +-order_by_item_list= +-OrderByItem + +-parse_location=47-132 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#4) == @@ -2613,6 +2628,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=CURRENT ROW) +-order_by_item_list= +-OrderByItem + +-parse_location=57-102 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#5) == diff --git a/zetasql/analyzer/testdata/anonymization.test b/zetasql/analyzer/testdata/anonymization.test index c47135028..13afff3b9 100644 --- a/zetasql/analyzer/testdata/anonymization.test +++ b/zetasql/analyzer/testdata/anonymization.test @@ -2032,6 +2032,7 @@ QueryStmt | | | +-Literal(type=INT64, value=0) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=265-272 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$aggregate.int64#13) | +-limit= @@ -2103,6 +2104,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=0) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=265-272 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$aggregate.int64#13) | | +-limit= @@ -8467,6 +8469,7 @@ QueryStmt | | | | | | +-Literal(type=STRING, value="x") | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=231-234 | | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.off#28) | | | | +-input_scan= @@ -8615,6 +8618,7 @@ QueryStmt | | | +-Literal(parse_location=65-68, type=STRING, value="x") | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#28) | +-input_scan= @@ -9748,3 +9752,71 @@ QueryStmt | +-ColumnRef(type=INT64, column=$anon.$k_threshold_col#36) +-anonymization_option_list= +-max_groups_contributed := Literal(type=INT64, value=1) + +== + +[language_features=ANONYMIZATION,V_1_1_ORDER_BY_IN_AGGREGATE] +[enabled_ast_rewrites={{DEFAULTS,+ANONYMIZATION,+ORDER_BY_AND_LIMIT_IN_AGGREGATE|DEFAULTS,+ORDER_BY_AND_LIMIT_IN_AGGREGATE}}] +# Making sure that ORDER_BY_AND_LIMIT_IN_AGGREGATE rewrite won't step into +# differentially private aggregates. +SELECT WITH ANONYMIZATION OPTIONS(epsilon=1e20, k_threshold=1) + ARRAY_AGG(int64 ORDER BY int64) +FROM SimpleTypesWithAnonymizationUid +-- +ALTERNATION GROUP: DEFAULTS,+ANONYMIZATION,+ORDER_BY_AND_LIMIT_IN_AGGREGATE +-- +[PRE-REWRITE AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#13 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#13] + +-input_scan= + +-AnonymizedAggregateScan + +-column_list=[$aggregate.$agg1#13] + +-input_scan= + | +-TableScan(column_list=[SimpleTypesWithAnonymizationUid.int64#2], table=SimpleTypesWithAnonymizationUid, column_index_list=[1]) + +-aggregate_list= + | +-$agg1#13 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=SimpleTypesWithAnonymizationUid.int64#2) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=202-207 + | +-column_ref= + | +-ColumnRef(type=INT64, column=SimpleTypesWithAnonymizationUid.int64#2) + +-anonymization_option_list= + +-epsilon := Literal(type=DOUBLE, value=1e+20) + +-k_threshold := Literal(type=INT64, value=1) + +Rewrite ERROR: Unsupported function in SELECT WITH ANONYMIZATION select list: ARRAY_AGG +-- +ALTERNATION GROUP: DEFAULTS,+ORDER_BY_AND_LIMIT_IN_AGGREGATE +-- +[PRE-REWRITE AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#13 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#13] + +-input_scan= + +-AnonymizedAggregateScan + +-column_list=[$aggregate.$agg1#13] + +-input_scan= + | +-TableScan(column_list=[SimpleTypesWithAnonymizationUid.int64#2], table=SimpleTypesWithAnonymizationUid, column_index_list=[1]) + +-aggregate_list= + | +-$agg1#13 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=SimpleTypesWithAnonymizationUid.int64#2) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=202-207 + | +-column_ref= + | +-ColumnRef(type=INT64, column=SimpleTypesWithAnonymizationUid.int64#2) + +-anonymization_option_list= + +-epsilon := Literal(type=DOUBLE, value=1e+20) + +-k_threshold := Literal(type=INT64, value=1) + +Rewrite ERROR: generic::unimplemented: ORDER_BY_AND_LIMIT_IN_AGGREGATE rewriter does not support ORDER BY or LIMIT in differentially private aggregate functions diff --git a/zetasql/analyzer/testdata/anonymization_subquery.test b/zetasql/analyzer/testdata/anonymization_subquery.test index 7300ec0a8..88a423374 100644 --- a/zetasql/analyzer/testdata/anonymization_subquery.test +++ b/zetasql/analyzer/testdata/anonymization_subquery.test @@ -501,6 +501,7 @@ QueryStmt | +-kappa := Literal(type=INT64, value=100) +-order_by_item_list= +-OrderByItem + +-parse_location=313-324 +-column_ref= | +-ColumnRef(type=INT64, column=$aggregate.weight#6) +-is_descending=TRUE @@ -599,6 +600,7 @@ QueryStmt | +-kappa := Literal(type=INT64, value=100) +-order_by_item_list= +-OrderByItem + +-parse_location=313-324 +-column_ref= | +-ColumnRef(type=INT64, column=$aggregate.weight#6) +-is_descending=TRUE diff --git a/zetasql/analyzer/testdata/anonymization_with.test b/zetasql/analyzer/testdata/anonymization_with.test index 62549ba26..c16bbbcc9 100644 --- a/zetasql/analyzer/testdata/anonymization_with.test +++ b/zetasql/analyzer/testdata/anonymization_with.test @@ -2387,6 +2387,7 @@ QueryStmt | | +-TableScan(parse_location=36-67, column_list=[SimpleTypesWithAnonymizationUid.int64#2], table=SimpleTypesWithAnonymizationUid, column_index_list=[1]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=81-87 | +-column_ref= | +-ColumnRef(type=DOUBLE, column=$orderby.$orderbycol1#13) +-query= diff --git a/zetasql/analyzer/testdata/approx_aggregate_functions.test b/zetasql/analyzer/testdata/approx_aggregate_functions.test index ac8cda63d..b2ca8fbb1 100644 --- a/zetasql/analyzer/testdata/approx_aggregate_functions.test +++ b/zetasql/analyzer/testdata/approx_aggregate_functions.test @@ -1971,12 +1971,11 @@ select kll_quantiles.init_uint64(@test_param_uint64) from SimpleTypes ^ == -[product_mode={{|internal|external}}] +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] select kll_quantiles.init_double(@test_param_double) from SimpleTypes -- -ALTERNATION GROUPS: - - internal +ALTERNATION GROUP: internal -- QueryStmt +-output_column_list= @@ -1994,11 +1993,64 @@ QueryStmt +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_double(DOUBLE, optional(0) INT64) -> BYTES) +-Parameter(type=DOUBLE, name="test_param_double") -- -ALTERNATION GROUP: external +ALTERNATION GROUPS: + external + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external -- ERROR: Function not found: kll_quantiles.init_double [at 1:8] select kll_quantiles.init_double(@test_param_double) from SimpleTypes ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(0) INT64) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") +== + +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.init_float64(@test_param_double) from SimpleTypes +-- +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.init_float64; Did you mean kll_quantiles.init_int64? [at 1:8] +select kll_quantiles.init_float64(@test_param_double) from SimpleTypes + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(0) INT64) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") == select kll_quantiles.init_int64(@test_param_int64, 100) from SimpleTypes @@ -2021,8 +2073,11 @@ QueryStmt +-Literal(type=INT64, value=100) == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.init_double(@test_param_double, 10000) from SimpleTypes -- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$aggregate.$agg1#20 AS `$col1` [BYTES] @@ -2039,25 +2094,107 @@ QueryStmt +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_double(DOUBLE, optional(1) INT64) -> BYTES) +-Parameter(type=DOUBLE, name="test_param_double") +-Literal(type=INT64, value=10000) +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(1) INT64) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") + +-Literal(type=INT64, value=10000) == -select kll_quantiles.init_int64(@test_param_int64, 1) from SimpleTypes +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.init_float64(@test_param_double, 10000) from SimpleTypes -- -ERROR: Argument 2 to KLL_QUANTILES.INIT_INT64 must be at least 2 [at 1:52] +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.init_float64; Did you mean kll_quantiles.init_int64? [at 1:8] +select kll_quantiles.init_float64(@test_param_double, 10000) from SimpleTypes + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(1) INT64) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") + +-Literal(type=INT64, value=10000) +== + select kll_quantiles.init_int64(@test_param_int64, 1) from SimpleTypes - ^ +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_int64(INT64, optional(1) INT64) -> BYTES) + +-Parameter(type=INT64, name="test_param_int64") + +-Literal(type=INT64, value=1) == select kll_quantiles.init_uint64(@test_param_uint64, 0) from SimpleTypes -- -ERROR: Argument 2 to KLL_QUANTILES.INIT_UINT64 must be at least 2 [at 1:54] -select kll_quantiles.init_uint64(@test_param_uint64, 0) from SimpleTypes - ^ +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_uint64(UINT64, optional(1) INT64) -> BYTES) + +-Parameter(type=UINT64, name="test_param_uint64") + +-Literal(type=INT64, value=0) == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.init_double(@test_param_double, @test_param_int64) from SimpleTypes -- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$aggregate.$agg1#20 AS `$col1` [BYTES] @@ -2074,6 +2211,25 @@ QueryStmt +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_double(DOUBLE, optional(1) INT64) -> BYTES) +-Parameter(type=DOUBLE, name="test_param_double") +-Parameter(type=INT64, name="test_param_int64") +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(1) INT64) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") + +-Parameter(type=INT64, name="test_param_int64") == select kll_quantiles.init_int64(@test_param_int64, key) from KeyValue @@ -2097,13 +2253,132 @@ select kll_quantiles.init_int64(@test_param_int64, null) from SimpleTypes ^ == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.init_double(@test_param_double, null) from SimpleTypes -- +ALTERNATION GROUP: +-- ERROR: Argument 2 to KLL_QUANTILES.INIT_DOUBLE must be non-NULL [at 1:54] +select kll_quantiles.init_double(@test_param_double, null) from SimpleTypes + ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +ERROR: Argument 2 to KLL_QUANTILES.INIT_FLOAT64 must be non-NULL [at 1:54] select kll_quantiles.init_double(@test_param_double, null) from SimpleTypes ^ == +[language_features={{|NAMED_ARGUMENTS|NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS|NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.init_double(@test_param_double, 100, weight => @test_param_int64 ) from SimpleTypes +-- +ALTERNATION GROUPS: + internal + NAMED_ARGUMENTS,internal +-- +ERROR: Number of arguments does not match for aggregate function KLL_QUANTILES.INIT_DOUBLE. Supported signature: KLL_QUANTILES.INIT_DOUBLE(DOUBLE, [INT64]) [at 1:8] +select kll_quantiles.init_double(@test_param_double, 100, weight => @test_par... + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for aggregate function KLL_QUANTILES.INIT_DOUBLE + Argument types: DOUBLE, INT64, weight => INT64 + Signature: KLL_QUANTILES.INIT_DOUBLE(DOUBLE, [INT64]) + Named argument `weight` does not exist in signature [at 1:8] +select kll_quantiles.init_double(@test_param_double, 100, weight => @test_par... + ^ +-- +ALTERNATION GROUPS: + external + NAMED_ARGUMENTS,external + NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,external + NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +ERROR: Function not found: kll_quantiles.init_double [at 1:8] +select kll_quantiles.init_double(@test_param_double, 100, weight => @test_par... + ^ +-- +ALTERNATION GROUP: NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,internal +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_double(DOUBLE, optional(1) INT64, optional(1) INT64 weight) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") + +-Literal(type=INT64, value=100) + +-Parameter(type=INT64, name="test_param_int64") +-- +ALTERNATION GROUP: NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(1) INT64, optional(1) INT64 weight) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") + +-Literal(type=INT64, value=100) + +-Parameter(type=INT64, name="test_param_int64") +== + +[language_features={{|NAMED_ARGUMENTS|NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS|NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.init_float64(@test_param_double, 100, weight => @test_param_int64 ) from SimpleTypes +-- +ALTERNATION GROUPS: + internal + external + NAMED_ARGUMENTS,internal + NAMED_ARGUMENTS,external + NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,internal + NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,external +-- +ERROR: Function not found: kll_quantiles.init_float64; Did you mean kll_quantiles.init_int64? [at 1:8] +select kll_quantiles.init_float64(@test_param_double, 100, weight => @test_pa... + ^ +-- +ALTERNATION GROUPS: + NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + NAMED_ARGUMENTS,V_1_3_KLL_WEIGHTS,V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.init_float64(DOUBLE, optional(1) INT64, optional(1) INT64 weight) -> BYTES) + +-Parameter(type=DOUBLE, name="test_param_double") + +-Literal(type=INT64, value=100) + +-Parameter(type=INT64, name="test_param_int64") +== + # INT32 widens to INT64 select kll_quantiles.init_int64(@test_param_int32) from SimpleTypes -- @@ -2244,12 +2519,11 @@ select kll_quantiles.merge_uint64(@test_param_bytes, 2) from SimpleTypes ^ == -[product_mode={{|internal|external}}] +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] select kll_quantiles.merge_double(@test_param_bytes, 50) from SimpleTypes -- -ALTERNATION GROUPS: - - internal +ALTERNATION GROUP: internal -- QueryStmt +-output_column_list= @@ -2268,11 +2542,66 @@ QueryStmt +-Parameter(type=BYTES, name="test_param_bytes") +-Literal(type=INT64, value=50) -- -ALTERNATION GROUP: external +ALTERNATION GROUPS: + external + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external -- ERROR: Function not found: kll_quantiles.merge_double [at 1:8] select kll_quantiles.merge_double(@test_param_bytes, 50) from SimpleTypes ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.merge_float64(BYTES, INT64) -> ARRAY) + +-Parameter(type=BYTES, name="test_param_bytes") + +-Literal(type=INT64, value=50) +== + +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.merge_float64(@test_param_bytes, 50) from SimpleTypes +-- +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.merge_float64; Did you mean kll_quantiles.merge_int64? [at 1:8] +select kll_quantiles.merge_float64(@test_param_bytes, 50) from SimpleTypes + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.merge_float64(BYTES, INT64) -> ARRAY) + +-Parameter(type=BYTES, name="test_param_bytes") + +-Literal(type=INT64, value=50) == select kll_quantiles.merge_int64(@test_param_bytes) from SimpleTypes @@ -2295,15 +2624,36 @@ select kll_quantiles.merge_int64(@test_param_bytes, key) from KeyValue ERROR: Argument 2 to KLL_QUANTILES.MERGE_INT64 must be a literal or query parameter [at 1:53] select kll_quantiles.merge_int64(@test_param_bytes, key) from KeyValue ^ + == -select kll_quantiles.merge_int64(@test_param_bytes, 1) from SimpleTypes +select kll_quantiles.merge_int64(@test_param_bytes, 0) from SimpleTypes -- -ERROR: Argument 2 to KLL_QUANTILES.MERGE_INT64 must be at least 2 [at 1:53] -select kll_quantiles.merge_int64(@test_param_bytes, 1) from SimpleTypes +ERROR: Argument 2 to KLL_QUANTILES.MERGE_INT64 must be at least 1 [at 1:53] +select kll_quantiles.merge_int64(@test_param_bytes, 0) from SimpleTypes ^ == +select kll_quantiles.merge_int64(@test_param_bytes, 1) from SimpleTypes +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.merge_int64(BYTES, INT64) -> ARRAY) + +-Parameter(type=BYTES, name="test_param_bytes") + +-Literal(type=INT64, value=1) +== + [product_mode={{|internal|external}}] select kll_quantiles.extract_int64(@test_param_bytes, 10) from SimpleTypes -- @@ -2350,12 +2700,11 @@ select kll_quantiles.extract_uint64(@test_param_bytes, 20) from SimpleTypes ^ == -[product_mode={{|internal|external}}] +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] select kll_quantiles.extract_double(@test_param_bytes, 5) from SimpleTypes -- -ALTERNATION GROUPS: - - internal +ALTERNATION GROUP: internal -- QueryStmt +-output_column_list= @@ -2371,14 +2720,67 @@ QueryStmt +-input_scan= +-TableScan(table=SimpleTypes) -- -ALTERNATION GROUP: external +ALTERNATION GROUPS: + external + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external -- ERROR: Function not found: kll_quantiles.extract_double [at 1:8] select kll_quantiles.extract_double(@test_param_bytes, 5) from SimpleTypes ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#20] + +-expr_list= + | +-$col1#20 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_float64(BYTES, INT64) -> ARRAY) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Literal(type=INT64, value=5) + +-input_scan= + +-TableScan(table=SimpleTypes) +== + +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.extract_float64(@test_param_bytes, 5) from SimpleTypes +-- +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.extract_float64; Did you mean kll_quantiles.extract_int64? [at 1:8] +select kll_quantiles.extract_float64(@test_param_bytes, 5) from SimpleTypes + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#20] + +-expr_list= + | +-$col1#20 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_float64(BYTES, INT64) -> ARRAY) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Literal(type=INT64, value=5) + +-input_scan= + +-TableScan(table=SimpleTypes) == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.extract_double(@test_param_bytes, key) from KeyValue +-- +ALTERNATION GROUP: + -- QueryStmt +-output_column_list= @@ -2393,15 +2795,76 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-input_scan= +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#3] + +-expr_list= + | +-$col1#3 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_float64(BYTES, INT64) -> ARRAY) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-input_scan= + +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +== +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +select kll_quantiles.extract_double(@test_param_bytes, 0) from SimpleTypes +-- + +ALTERNATION GROUP: +-- +ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_DOUBLE must be at least 1 [at 1:56] +select kll_quantiles.extract_double(@test_param_bytes, 0) from SimpleTypes + ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_FLOAT64 must be at least 1 [at 1:56] +select kll_quantiles.extract_double(@test_param_bytes, 0) from SimpleTypes + ^ == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.extract_double(@test_param_bytes, 1) from SimpleTypes -- -ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_DOUBLE must be at least 2 [at 1:56] -select kll_quantiles.extract_double(@test_param_bytes, 1) from SimpleTypes - ^ +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#20] + +-expr_list= + | +-$col1#20 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_double(BYTES, INT64) -> ARRAY) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Literal(type=INT64, value=1) + +-input_scan= + +-TableScan(table=SimpleTypes) +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#20 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#20] + +-expr_list= + | +-$col1#20 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_float64(BYTES, INT64) -> ARRAY) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Literal(type=INT64, value=1) + +-input_scan= + +-TableScan(table=SimpleTypes) == select kll_quantiles.extract_double(@test_param_bytes, NULL) from SimpleTypes @@ -2411,6 +2874,27 @@ select kll_quantiles.extract_double(@test_param_bytes, NULL) from SimpleTypes ^ == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.extract_float64(@test_param_bytes, NULL) from SimpleTypes +-- +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.extract_float64; Did you mean kll_quantiles.extract_int64? [at 1:8] +select kll_quantiles.extract_float64(@test_param_bytes, NULL) from SimpleTypes + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_FLOAT64 must be non-NULL [at 1:57] +select kll_quantiles.extract_float64(@test_param_bytes, NULL) from SimpleTypes + ^ +== + [product_mode={{|internal|external}}] select kll_quantiles.merge_point_int64(@test_param_bytes, 0.99) from SimpleTypes @@ -2465,13 +2949,12 @@ select kll_quantiles.merge_point_uint64(@test_param_bytes, 0.5) ^ == -[product_mode={{|internal|external}}] +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] select kll_quantiles.merge_point_double(@test_param_bytes, 1) from SimpleTypes -- -ALTERNATION GROUPS: - - internal +ALTERNATION GROUP: internal -- QueryStmt +-output_column_list= @@ -2490,11 +2973,67 @@ QueryStmt +-Parameter(type=BYTES, name="test_param_bytes") +-Literal(type=DOUBLE, value=1) -- -ALTERNATION GROUP: external +ALTERNATION GROUPS: + external + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external -- ERROR: Function not found: kll_quantiles.merge_point_double [at 1:8] select kll_quantiles.merge_point_double(@test_param_bytes, 1) from SimpleTypes ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.merge_point_float64(BYTES, DOUBLE) -> DOUBLE) + +-Parameter(type=BYTES, name="test_param_bytes") + +-Literal(type=DOUBLE, value=1) +== + +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.merge_point_float64(@test_param_bytes, 1) from SimpleTypes +-- + +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.merge_point_float64; Did you mean kll_quantiles.merge_point_int64? [at 1:8] +select kll_quantiles.merge_point_float64(@test_param_bytes, 1) from SimpleTypes + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#20 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#20] + +-input_scan= + | +-TableScan(table=SimpleTypes) + +-aggregate_list= + +-$agg1#20 := + +-AggregateFunctionCall(ZetaSQL:kll_quantiles.merge_point_float64(BYTES, DOUBLE) -> DOUBLE) + +-Parameter(type=BYTES, name="test_param_bytes") + +-Literal(type=DOUBLE, value=1) == select kll_quantiles.merge_point_uint64(@test_param_bytes, 0.5+0.1) from SimpleTypes @@ -2577,13 +3116,12 @@ select kll_quantiles.extract_point_uint64(@test_param_bytes, 0.75) ^ == -[product_mode={{|internal|external}}] +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] select kll_quantiles.extract_point_double(@test_param_bytes, 0.995) from SimpleTypes -- -ALTERNATION GROUPS: - - internal +ALTERNATION GROUP: internal -- QueryStmt +-output_column_list= @@ -2602,12 +3140,37 @@ QueryStmt ALTERNATION GROUP: external -- ERROR: Function not found: kll_quantiles.extract_point_double; Did you mean kll_quantiles.extract_point_int64? [at 1:8] +select kll_quantiles.extract_point_double(@test_param_bytes, 0.995) + ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#20 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#20] + +-expr_list= + | +-$col1#20 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_point_float64(BYTES, DOUBLE) -> DOUBLE) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Literal(type=DOUBLE, value=0.995) + +-input_scan= + +-TableScan(table=SimpleTypes) +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +ERROR: Function not found: kll_quantiles.extract_point_double; Did you mean kll_quantiles.extract_point_float64? [at 1:8] select kll_quantiles.extract_point_double(@test_param_bytes, 0.995) ^ == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.extract_point_double(@test_param_bytes, 0.5+0.1) from SimpleTypes -- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$query.$col1#20 AS `$col1` [DOUBLE] @@ -2623,10 +3186,31 @@ QueryStmt | +-Literal(type=DOUBLE, value=0.1) +-input_scan= +-TableScan(table=SimpleTypes) +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#20 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#20] + +-expr_list= + | +-$col1#20 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_point_float64(BYTES, DOUBLE) -> DOUBLE) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-FunctionCall(ZetaSQL:$add(DOUBLE, DOUBLE) -> DOUBLE) + | +-Literal(type=DOUBLE, value=0.5) + | +-Literal(type=DOUBLE, value=0.1) + +-input_scan= + +-TableScan(table=SimpleTypes) == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.extract_point_double(@test_param_bytes, key) from KeyValue -- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$query.$col1#3 AS `$col1` [DOUBLE] @@ -2641,19 +3225,86 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-input_scan= +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#3] + +-expr_list= + | +-$col1#3 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_point_float64(BYTES, DOUBLE) -> DOUBLE) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Cast(INT64 -> DOUBLE) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-input_scan= + +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +== + +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] +[product_mode={{internal|external}}] +select kll_quantiles.extract_point_float64(@test_param_bytes, key) from KeyValue +-- +ALTERNATION GROUPS: + internal + external +-- +ERROR: Function not found: kll_quantiles.extract_point_float64; Did you mean kll_quantiles.extract_point_int64? [at 1:8] +select kll_quantiles.extract_point_float64(@test_param_bytes, key) from KeyValue + ^ +-- +ALTERNATION GROUPS: + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,internal + V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS,external +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#3] + +-expr_list= + | +-$col1#3 := + | +-FunctionCall(ZetaSQL:kll_quantiles.extract_point_float64(BYTES, DOUBLE) -> DOUBLE) + | +-Parameter(type=BYTES, name="test_param_bytes") + | +-Cast(INT64 -> DOUBLE) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-input_scan= + +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.extract_point_double(@test_param_bytes, 1.1) from SimpleTypes -- +ALTERNATION GROUP: +-- ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_POINT_DOUBLE must be between 0 and 1 [at 1:62] +...extract_point_double(@test_param_bytes, 1.1) from SimpleTypes + ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_POINT_FLOAT64 must be between 0 and 1 [at 1:62] ...extract_point_double(@test_param_bytes, 1.1) from SimpleTypes ^ == +[language_features={{|V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS}}] select kll_quantiles.extract_point_double(@test_param_bytes, -1) from SimpleTypes -- +ALTERNATION GROUP: +-- ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_POINT_DOUBLE must be between 0 and 1 [at 1:62] +...extract_point_double(@test_param_bytes, -1) from SimpleTypes + ^ +-- +ALTERNATION GROUP: V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS +-- +ERROR: Argument 2 to KLL_QUANTILES.EXTRACT_POINT_FLOAT64 must be between 0 and 1 [at 1:62] ...extract_point_double(@test_param_bytes, -1) from SimpleTypes ^ == diff --git a/zetasql/analyzer/testdata/array_aggregate.test b/zetasql/analyzer/testdata/array_aggregate.test new file mode 100644 index 000000000..ca8c39511 --- /dev/null +++ b/zetasql/analyzer/testdata/array_aggregate.test @@ -0,0 +1,1964 @@ +[default language_features=V_1_1_ORDER_BY_IN_AGGREGATE] +[default enabled_ast_rewrites=DEFAULTS,+ORDER_BY_AND_LIMIT_IN_AGGREGATE] +[default show_unparsed] + +[no_enable_literal_replacement] +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_HAVING_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE,V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE] +# Simple ARRAY_AGG with ORDER BY with all the features. +# no_enable_literal_replacement: Distinct and ORDER BY must match exactly, we +# cannot replace just one literal with parameter +SELECT ARRAY_AGG(DISTINCT a + 1 IGNORE NULLS HAVING MAX 3 ORDER BY a + 1 LIMIT 10) +FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a, TestTable.KitchenSink.repeated_uint64_val as b +GROUP BY CAST(a AS UINT64) - b +-- + +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_uint64_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-group_by_list= + | +-$groupbycol1#8 := + | +-FunctionCall(ZetaSQL:$subtract(UINT64, UINT64) -> INT64) + | +-Cast(INT64 -> UINT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-aggregate_list= + +-$agg1#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +-distinct=TRUE + +-null_handling_modifier=IGNORE_NULLS + +-having_modifier= + | +-AggregateHavingModifier + | +-kind=MAX + | +-having_expr= + | +-Literal(type=INT64, value=3) + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=250-255 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +-limit= + +-Literal(type=INT64, value=10) + +[UNPARSED_SQL] +SELECT + ARRAY_AGG(DISTINCT projectscan_7.a_6 IGNORE NULLS + HAVING MAX 3 + ORDER BY projectscan_7.a_6 + LIMIT 10) AS a_8 +FROM + ( + SELECT + arrayscan_4.a_1 AS a_1, + arrayscan_4.a_3 AS a_3, + a_5 AS a_5, + (arrayscan_4.a_3) + 1 AS a_6 + FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS arrayscan_4 + JOIN + UNNEST(arrayscan_4.a_1.repeated_uint64_val) AS a_5 + ) AS projectscan_7 +GROUP BY CAST(projectscan_7.a_3 AS UINT64) - (projectscan_7.a_5); + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-expr_list= + | +-$agg1#7 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#14] + | +-expr_list= + | | +-injected#14 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#13) + | | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#13) + | | +-Literal(type=ARRAY, value=NULL) + | +-input_scan= + | +-ProjectScan + | +-column_list=[null_if_empty_array.$out#13] + | +-expr_list= + | | +-$out#13 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | +-subquery= + | | +-LimitOffsetScan + | | +-column_list=[$agg_rewriter.distinct.arg#12] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.distinct.arg#12] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-AggregateScan + | | | | +-column_list=[$agg_rewriter.distinct.arg#12] + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=[$agg_rewriter.$orderbycol1#9] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=[$agg_rewriter.$orderbycol1#9] + | | | | | | +-expr_list= + | | | | | | | +-$orderbycol1#9 := + | | | | | | | +-GetStructField + | | | | | | | +-type=INT64 + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT<`$orderbycol1_0` INT64>, column=$agg_rewriter.$struct#11) + | | | | | | | +-field_idx=0 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#11] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#11] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(INT64) -> BOOL) + | | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#9) + | | | | +-group_by_list= + | | | | +-distinct.arg#12 := ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#9) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=250-255 + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$agg_rewriter.distinct.arg#12) + | | +-limit= + | | | +-Literal(type=INT64, value=10) + | | +-offset= + | | +-Literal(type=INT64, value=0) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=[$agg_rewriter.$array#10] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_uint64_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-group_by_list= + | +-$groupbycol1#8 := + | +-FunctionCall(ZetaSQL:$subtract(UINT64, UINT64) -> INT64) + | +-Cast(INT64 -> UINT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-aggregate_list= + +-$array#10 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT<`$orderbycol1_0` INT64>) -> ARRAY>) + +-MakeStruct + +-type=STRUCT<`$orderbycol1_0` INT64> + +-field_list= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +-having_modifier= + +-AggregateHavingModifier + +-kind=MAX + +-having_expr= + +-Literal(type=INT64, value=3) +[UNPARSED_SQL] +SELECT + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_17.a_11)) >= 1, projectscan_17.a_11, CAST(NULL AS ARRAY< INT64 >)) AS a_18 + FROM + ( + SELECT + ARRAY( + SELECT + aggregatescan_16.a_15 AS a_15 + FROM + ( + SELECT + projectscan_14.a_13 AS a_15 + FROM + ( + SELECT + a_12.`$orderbycol1_0` AS a_13 + FROM + UNNEST(aggregatescan_9.a_8) AS a_12 + ) AS projectscan_14 + WHERE + NOT((projectscan_14.a_13) IS NULL) + GROUP BY 1 + ) AS aggregatescan_16 + ORDER BY aggregatescan_16.a_15 + LIMIT 10 OFFSET 0) AS a_11 + ) AS projectscan_17 + ) AS a_10 +FROM + ( + SELECT + ARRAY_AGG(STRUCT< `$orderbycol1_0` INT64 > (projectscan_7.a_6) + HAVING MAX 3) AS a_8 + FROM + ( + SELECT + arrayscan_4.a_1 AS a_1, + arrayscan_4.a_3 AS a_3, + a_5 AS a_5, + (arrayscan_4.a_3) + 1 AS a_6 + FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS arrayscan_4 + JOIN + UNNEST(arrayscan_4.a_1.repeated_uint64_val) AS a_5 + ) AS projectscan_7 + GROUP BY CAST(projectscan_7.a_3 AS UINT64) - (projectscan_7.a_5) + ) AS aggregatescan_9; +== + +# Duplicate ARRAY_AGGs in the same AggregateScan +SELECT ARRAY_AGG(a ORDER BY a), ARRAY_AGG(a ORDER BY a) +FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#6 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_int64_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=$array.a#4) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=28-29 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$array.a#4) + +-$agg2#6 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-ColumnRef(type=INT64, column=$array.a#4) + +-order_by_item_list= + +-OrderByItem + +-parse_location=53-54 + +-column_ref= + +-ColumnRef(type=INT64, column=$array.a#4) + +[UNPARSED_SQL] +SELECT + ARRAY_AGG(a_3 + ORDER BY a_3) AS a_4, + ARRAY_AGG(a_3 + ORDER BY a_3) AS a_5 +FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3; + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#6 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-expr_list= + | +-$agg1#5 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#8) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#15] + | | +-expr_list= + | | | +-injected#15 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#10) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#10) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#10] + | | +-expr_list= + | | | +-$out#10 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#8, is_correlated=TRUE) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.a#7] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.a#7] + | | | | +-expr_list= + | | | | | +-a#7 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#9) + | | | | | +-field_idx=0 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$agg_rewriter.$struct#9] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#8, is_correlated=TRUE) + | | | | +-element_column_list=[$agg_rewriter.$struct#9] + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=28-29 + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$agg_rewriter.a#7) + | | +-input_scan= + | | +-SingleRowScan + | +-$agg2#6 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#16] + | +-expr_list= + | | +-injected#16 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | +-Literal(type=ARRAY, value=NULL) + | +-input_scan= + | +-ProjectScan + | +-column_list=[null_if_empty_array.$out#14] + | +-expr_list= + | | +-$out#14 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$agg_rewriter.a#11] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[$agg_rewriter.a#11] + | | | +-expr_list= + | | | | +-a#11 := + | | | | +-GetStructField + | | | | +-type=INT64 + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#13) + | | | | +-field_idx=0 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$agg_rewriter.$struct#13] + | | | +-array_expr_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | +-element_column_list=[$agg_rewriter.$struct#13] + | | +-order_by_item_list= + | | +-OrderByItem + | | +-parse_location=53-54 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$agg_rewriter.a#11) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#8, $array#12] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_int64_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$array#8 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$array.a#4) + +-$array#12 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=INT64, column=$array.a#4) +[UNPARSED_SQL] +SELECT + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_12.a_8)) >= 1, projectscan_12.a_8, CAST(NULL AS ARRAY< INT64 >)) AS a_13 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_11.a_10 AS a_10 + FROM + ( + SELECT + a_9.a_0 AS a_10 + FROM + UNNEST(aggregatescan_6.a_4) AS a_9 + ) AS projectscan_11 + ORDER BY projectscan_11.a_10) AS a_8 + ) AS projectscan_12 + ) AS a_7, + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_19.a_15)) >= 1, projectscan_19.a_15, CAST(NULL AS ARRAY< INT64 >)) AS a_20 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_18.a_17 AS a_17 + FROM + ( + SELECT + a_16.a_0 AS a_17 + FROM + UNNEST(aggregatescan_6.a_5) AS a_16 + ) AS projectscan_18 + ORDER BY projectscan_18.a_17) AS a_15 + ) AS projectscan_19 + ) AS a_14 +FROM + ( + SELECT + ARRAY_AGG(STRUCT< a_0 INT64 > (a_3)) AS a_4, + ARRAY_AGG(STRUCT< a_0 INT64 > (a_3)) AS a_5 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS aggregatescan_6; + +== + +# Slight different ARRAY_AGGs in the same AggregateScan +SELECT ARRAY_AGG(a ORDER BY b), ARRAY_AGG(b ORDER BY a) +FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a, TestTable.KitchenSink.repeated_uint64_val as b +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#6 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_uint64_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-aggregate_list= + +-$agg1#6 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=$array.a#4) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=28-29 + | +-column_ref= + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(UINT64) -> ARRAY) + +-ColumnRef(type=UINT64, column=$array.b#5) + +-order_by_item_list= + +-OrderByItem + +-parse_location=53-54 + +-column_ref= + +-ColumnRef(type=INT64, column=$array.a#4) + +[UNPARSED_SQL] +SELECT + ARRAY_AGG(arrayscan_4.a_3 + ORDER BY a_5) AS a_6, + ARRAY_AGG(a_5 + ORDER BY arrayscan_4.a_3) AS a_7 +FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS arrayscan_4 + JOIN + UNNEST(arrayscan_4.a_1.repeated_uint64_val) AS a_5; + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#6 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-expr_list= + | +-$agg1#6 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#18] + | | +-expr_list= + | | | +-injected#18 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#12) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#12) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#12] + | | +-expr_list= + | | | +-$out#12 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.a#8] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=$agg_rewriter.[a#8, b#9] + | | | | +-expr_list= + | | | | | +-a#8 := + | | | | | | +-GetStructField + | | | | | | +-type=INT64 + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#11) + | | | | | | +-field_idx=0 + | | | | | +-b#9 := + | | | | | +-GetStructField + | | | | | +-type=UINT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#11) + | | | | | +-field_idx=1 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$agg_rewriter.$struct#11] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | | +-element_column_list=[$agg_rewriter.$struct#11] + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=28-29 + | | | +-column_ref= + | | | +-ColumnRef(type=UINT64, column=$agg_rewriter.b#9) + | | +-input_scan= + | | +-SingleRowScan + | +-$agg2#7 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#15) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#19] + | +-expr_list= + | | +-injected#19 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#17) + | | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#17) + | | +-Literal(type=ARRAY, value=NULL) + | +-input_scan= + | +-ProjectScan + | +-column_list=[null_if_empty_array.$out#17] + | +-expr_list= + | | +-$out#17 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#15, is_correlated=TRUE) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$agg_rewriter.b#14] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$agg_rewriter.[a#13, b#14] + | | | +-expr_list= + | | | | +-a#13 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#16) + | | | | | +-field_idx=0 + | | | | +-b#14 := + | | | | +-GetStructField + | | | | +-type=UINT64 + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#16) + | | | | +-field_idx=1 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$agg_rewriter.$struct#16] + | | | +-array_expr_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#15, is_correlated=TRUE) + | | | +-element_column_list=[$agg_rewriter.$struct#16] + | | +-order_by_item_list= + | | +-OrderByItem + | | +-parse_location=53-54 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$agg_rewriter.a#13) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#10, $array#15] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_uint64_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-aggregate_list= + +-$array#10 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$array.a#4) + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-$array#15 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=INT64, column=$array.a#4) + +-ColumnRef(type=UINT64, column=$array.b#5) +[UNPARSED_SQL] +SELECT + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_15.a_10)) >= 1, projectscan_15.a_10, CAST(NULL AS ARRAY< INT64 >)) AS a_16 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_14.a_12 AS a_12 + FROM + ( + SELECT + a_11.a_0 AS a_12, + a_11.b_1 AS a_13 + FROM + UNNEST(aggregatescan_8.a_6) AS a_11 + ) AS projectscan_14 + ORDER BY projectscan_14.a_13) AS a_10 + ) AS projectscan_15 + ) AS a_9, + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_23.a_18)) >= 1, projectscan_23.a_18, CAST(NULL AS ARRAY< UINT64 >)) AS a_24 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_22.a_21 AS a_21 + FROM + ( + SELECT + a_19.a_0 AS a_20, + a_19.b_1 AS a_21 + FROM + UNNEST(aggregatescan_8.a_7) AS a_19 + ) AS projectscan_22 + ORDER BY projectscan_22.a_20) AS a_18 + ) AS projectscan_23 + ) AS a_17 +FROM + ( + SELECT + ARRAY_AGG(STRUCT< a_0 INT64, b_1 UINT64 > (arrayscan_4.a_3, a_5)) AS a_6, + ARRAY_AGG(STRUCT< a_0 INT64, b_1 UINT64 > (arrayscan_4.a_3, a_5)) AS a_7 + FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS arrayscan_4 + JOIN + UNNEST(arrayscan_4.a_1.repeated_uint64_val) AS a_5 + ) AS aggregatescan_8; + +== + +[language_features=V_1_1_HAVING_IN_AGGREGATE,V_1_1_ORDER_BY_IN_AGGREGATE] +# Two ARRAY_AGGs in the same AggregateScan along with other aggregates +SELECT ARRAY_AGG(a HAVING MAX CAST(a AS UINT64) - b ORDER BY b), SUM(a), COUNT(b), ARRAY_AGG(b HAVING MIN 3 ORDER BY a) +FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a, TestTable.KitchenSink.repeated_uint64_val as b +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#6 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [INT64] +| +-$aggregate.$agg3#8 AS `$col3` [INT64] +| +-$aggregate.$agg4#9 AS `$col4` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7, $agg3#8, $agg4#9] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#6, $agg2#7, $agg3#8, $agg4#9] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_uint64_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-aggregate_list= + +-$agg1#6 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=$array.a#4) + | +-having_modifier= + | | +-AggregateHavingModifier + | | +-kind=MAX + | | +-having_expr= + | | +-FunctionCall(ZetaSQL:$subtract(UINT64, UINT64) -> INT64) + | | +-Cast(INT64 -> UINT64) + | | | +-ColumnRef(type=INT64, column=$array.a#4) + | | +-ColumnRef(type=UINT64, column=$array.b#5) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=132-133 + | +-column_ref= + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-$agg2#7 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$array.a#4) + +-$agg3#8 := + | +-AggregateFunctionCall(ZetaSQL:count(UINT64) -> INT64) + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-$agg4#9 := + +-AggregateFunctionCall(ZetaSQL:array_agg(UINT64) -> ARRAY) + +-ColumnRef(type=UINT64, column=$array.b#5) + +-having_modifier= + | +-AggregateHavingModifier + | +-kind=MIN + | +-having_expr= + | +-Literal(type=INT64, value=3) + +-order_by_item_list= + +-OrderByItem + +-parse_location=188-189 + +-column_ref= + +-ColumnRef(type=INT64, column=$array.a#4) + +[UNPARSED_SQL] +SELECT + ARRAY_AGG(arrayscan_4.a_3 + HAVING MAX CAST(arrayscan_4.a_3 AS UINT64) - a_5 + ORDER BY a_5) AS a_6, + SUM(arrayscan_4.a_3) AS a_7, + COUNT(a_5) AS a_8, + ARRAY_AGG(a_5 + HAVING MIN 3 + ORDER BY arrayscan_4.a_3) AS a_9 +FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS arrayscan_4 + JOIN + UNNEST(arrayscan_4.a_1.repeated_uint64_val) AS a_5; + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#6 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [INT64] +| +-$aggregate.$agg3#8 AS `$col3` [INT64] +| +-$aggregate.$agg4#9 AS `$col4` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7, $agg3#8, $agg4#9] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7, $agg3#8, $agg4#9] + +-expr_list= + | +-$agg1#6 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#20] + | | +-expr_list= + | | | +-injected#20 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#14] + | | +-expr_list= + | | | +-$out#14 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.a#10] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=$agg_rewriter.[a#10, b#11] + | | | | +-expr_list= + | | | | | +-a#10 := + | | | | | | +-GetStructField + | | | | | | +-type=INT64 + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#13) + | | | | | | +-field_idx=0 + | | | | | +-b#11 := + | | | | | +-GetStructField + | | | | | +-type=UINT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#13) + | | | | | +-field_idx=1 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$agg_rewriter.$struct#13] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | | +-element_column_list=[$agg_rewriter.$struct#13] + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=132-133 + | | | +-column_ref= + | | | +-ColumnRef(type=UINT64, column=$agg_rewriter.b#11) + | | +-input_scan= + | | +-SingleRowScan + | +-$agg4#9 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#17) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#21] + | +-expr_list= + | | +-injected#21 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#19) + | | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#19) + | | +-Literal(type=ARRAY, value=NULL) + | +-input_scan= + | +-ProjectScan + | +-column_list=[null_if_empty_array.$out#19] + | +-expr_list= + | | +-$out#19 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#17, is_correlated=TRUE) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$agg_rewriter.b#16] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$agg_rewriter.[a#15, b#16] + | | | +-expr_list= + | | | | +-a#15 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#18) + | | | | | +-field_idx=0 + | | | | +-b#16 := + | | | | +-GetStructField + | | | | +-type=UINT64 + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#18) + | | | | +-field_idx=1 + | | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$agg_rewriter.$struct#18] + | | | +-array_expr_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#17, is_correlated=TRUE) + | | | +-element_column_list=[$agg_rewriter.$struct#18] + | | +-order_by_item_list= + | | +-OrderByItem + | | +-parse_location=188-189 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$agg_rewriter.a#15) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=[$agg_rewriter.$array#12, $aggregate.$agg2#7, $aggregate.$agg3#8, $agg_rewriter.$array#17] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_uint64_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-aggregate_list= + +-$array#12 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$array.a#4) + | +-ColumnRef(type=UINT64, column=$array.b#5) + | +-having_modifier= + | +-AggregateHavingModifier + | +-kind=MAX + | +-having_expr= + | +-FunctionCall(ZetaSQL:$subtract(UINT64, UINT64) -> INT64) + | +-Cast(INT64 -> UINT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-$agg2#7 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$array.a#4) + +-$agg3#8 := + | +-AggregateFunctionCall(ZetaSQL:count(UINT64) -> INT64) + | +-ColumnRef(type=UINT64, column=$array.b#5) + +-$array#17 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=INT64, column=$array.a#4) + +-ColumnRef(type=UINT64, column=$array.b#5) + +-having_modifier= + +-AggregateHavingModifier + +-kind=MIN + +-having_expr= + +-Literal(type=INT64, value=3) +[UNPARSED_SQL] +SELECT + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_17.a_12)) >= 1, projectscan_17.a_12, CAST(NULL AS ARRAY< INT64 >)) AS a_18 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_16.a_14 AS a_14 + FROM + ( + SELECT + a_13.a_0 AS a_14, + a_13.b_1 AS a_15 + FROM + UNNEST(aggregatescan_10.a_6) AS a_13 + ) AS projectscan_16 + ORDER BY projectscan_16.a_15) AS a_12 + ) AS projectscan_17 + ) AS a_11, + aggregatescan_10.a_7 AS a_7, + aggregatescan_10.a_8 AS a_8, + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_25.a_20)) >= 1, projectscan_25.a_20, CAST(NULL AS ARRAY< UINT64 >)) AS a_26 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_24.a_23 AS a_23 + FROM + ( + SELECT + a_21.a_0 AS a_22, + a_21.b_1 AS a_23 + FROM + UNNEST(aggregatescan_10.a_9) AS a_21 + ) AS projectscan_24 + ORDER BY projectscan_24.a_22) AS a_20 + ) AS projectscan_25 + ) AS a_19 +FROM + ( + SELECT + ARRAY_AGG(STRUCT< a_0 INT64, b_1 UINT64 > (arrayscan_4.a_3, a_5) + HAVING MAX CAST(arrayscan_4.a_3 AS UINT64) - a_5) AS a_6, + SUM(arrayscan_4.a_3) AS a_7, + COUNT(a_5) AS a_8, + ARRAY_AGG(STRUCT< a_0 INT64, b_1 UINT64 > (arrayscan_4.a_3, a_5) + HAVING MIN 3) AS a_9 + FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS arrayscan_4 + JOIN + UNNEST(arrayscan_4.a_1.repeated_uint64_val) AS a_5 + ) AS aggregatescan_10; + +== + +# No ORDER BYs, so the ARRAY_AGG rewriter shouldn't mess up the tree +SELECT ARRAY_AGG(a), ARRAY_AGG(a + 2) +FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#6 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_int64_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=$array.a#4) + +-$agg2#6 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-ColumnRef(type=INT64, column=$array.a#4) + +-Literal(type=INT64, value=2) + +[UNPARSED_SQL] +SELECT + ARRAY_AGG(a_3) AS a_4, + ARRAY_AGG(a_3 + 2) AS a_5 +FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3; + +== + +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE] + +# ARRAY_AGG with limits +SELECT ARRAY_AGG(a LIMIT 2), ARRAY_AGG(a ORDER BY a+1 LIMIT 3) +FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_int64_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=$array.a#4) + | +-limit= + | +-Literal(type=INT64, value=2) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-ColumnRef(type=INT64, column=$array.a#4) + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=74-77 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +-limit= + +-Literal(type=INT64, value=3) + +[UNPARSED_SQL] +SELECT + ARRAY_AGG(projectscan_5.a_3 + LIMIT 2) AS a_6, + ARRAY_AGG(projectscan_5.a_3 + ORDER BY projectscan_5.a_4 + LIMIT 3) AS a_7 +FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3, + a_3 + 1 AS a_4 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS projectscan_5; + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-expr_list= + | +-$agg1#5 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#17] + | | +-expr_list= + | | | +-injected#17 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#11] + | | +-expr_list= + | | | +-$out#11 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | +-subquery= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.a#8] + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.a#8] + | | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.a#8] + | | | | +-expr_list= + | | | | | +-a#8 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#10) + | | | | | +-field_idx=0 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$agg_rewriter.$struct#10] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | +-element_column_list=[$agg_rewriter.$struct#10] + | | | +-limit= + | | | | +-Literal(type=INT64, value=2) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-$agg2#7 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#18] + | +-expr_list= + | | +-injected#18 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#16) + | | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#16) + | | +-Literal(type=ARRAY, value=NULL) + | +-input_scan= + | +-ProjectScan + | +-column_list=[null_if_empty_array.$out#16] + | +-expr_list= + | | +-$out#16 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14, is_correlated=TRUE) + | | +-subquery= + | | +-LimitOffsetScan + | | +-column_list=[$agg_rewriter.a#12] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.a#12] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=$agg_rewriter.[a#12, $orderbycol1#13] + | | | | +-expr_list= + | | | | | +-a#12 := + | | | | | | +-GetStructField + | | | | | | +-type=INT64 + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#15) + | | | | | | +-field_idx=0 + | | | | | +-$orderbycol1#13 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#15) + | | | | | +-field_idx=1 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$agg_rewriter.$struct#15] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14, is_correlated=TRUE) + | | | | +-element_column_list=[$agg_rewriter.$struct#15] + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=74-77 + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#13) + | | +-limit= + | | | +-Literal(type=INT64, value=3) + | | +-offset= + | | +-Literal(type=INT64, value=0) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#9, $array#14] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_int64_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$array#9 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$array.a#4) + +-$array#14 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=INT64, column=$array.a#4) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +[UNPARSED_SQL] +SELECT + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_13.a_10)) >= 1, projectscan_13.a_10, CAST(NULL AS ARRAY< INT64 >)) AS a_14 + FROM + ( + SELECT + ARRAY( + SELECT + a_11.a_0 AS a_12 + FROM + UNNEST(aggregatescan_8.a_6) AS a_11 + LIMIT 2 OFFSET 0) AS a_10 + ) AS projectscan_13 + ) AS a_9, + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_21.a_16)) >= 1, projectscan_21.a_16, CAST(NULL AS ARRAY< INT64 >)) AS a_22 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_20.a_18 AS a_18 + FROM + ( + SELECT + a_17.a_0 AS a_18, + a_17.`$orderbycol1_1` AS a_19 + FROM + UNNEST(aggregatescan_8.a_7) AS a_17 + ) AS projectscan_20 + ORDER BY projectscan_20.a_19 + LIMIT 3 OFFSET 0) AS a_16 + ) AS projectscan_21 + ) AS a_15 +FROM + ( + SELECT + ARRAY_AGG(STRUCT< a_0 INT64 > (projectscan_5.a_3)) AS a_6, + ARRAY_AGG(STRUCT< a_0 INT64, `$orderbycol1_1` INT64 > (projectscan_5.a_3, projectscan_5.a_4)) AS a_7 + FROM + ( + SELECT + testtable_2.a_1 AS a_1, + a_3 AS a_3, + a_3 + 1 AS a_4 + FROM + ( + SELECT + TestTable.KitchenSink AS a_1 + FROM + TestTable + ) AS testtable_2 + JOIN + UNNEST(testtable_2.a_1.repeated_int64_val) AS a_3 + ) AS projectscan_5 + ) AS aggregatescan_8; + +== + +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE,V_1_1_HAVING_IN_AGGREGATE] + +# Correlated variable only in HAVING clause +WITH t AS (SELECT 1 AS correlated) +SELECT (SELECT ARRAY_AGG(a HAVING MAX t.correlated ORDER BY a+1 LIMIT 3) FROM TestTable, TestTable.KitchenSink.repeated_int64_val as a) +FROM t +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#9 AS `$col1` [ARRAY] ++-query= + +-WithScan + +-column_list=[$query.$col1#9] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="t" + | +-with_subquery= + | +-ProjectScan + | +-column_list=[t.correlated#1] + | +-expr_list= + | | +-correlated#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-query= + +-ProjectScan + +-column_list=[$query.$col1#9] + +-expr_list= + | +-$col1#9 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=t.correlated#2) + | +-subquery= + | +-ProjectScan + | +-column_list=[$aggregate.$agg1#8] + | +-input_scan= + | +-AggregateScan + | +-column_list=[$aggregate.$agg1#8] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestTable.KitchenSink#5, $array.a#6, $orderby.$orderbycol1#7] + | | +-expr_list= + | | | +-$orderbycol1#7 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$array.a#6) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#5, $array.a#6] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#5], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#5) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#6] + | +-aggregate_list= + | +-$agg1#8 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + | +-ColumnRef(type=INT64, column=$array.a#6) + | +-having_modifier= + | | +-AggregateHavingModifier + | | +-kind=MAX + | | +-having_expr= + | | +-ColumnRef(type=INT64, column=t.correlated#2, is_correlated=TRUE) + | +-order_by_item_list= + | | +-OrderByItem + | | +-parse_location=139-142 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#7) + | +-limit= + | +-Literal(type=INT64, value=3) + +-input_scan= + +-WithRefScan(column_list=[t.correlated#2], with_query_name="t") + +[UNPARSED_SQL] +WITH + t AS ( + SELECT + 1 AS a_1 + ) +SELECT + ( + SELECT + ARRAY_AGG(projectscan_8.a_6 + HAVING MAX withrefscan_2.a_1 + ORDER BY projectscan_8.a_7 + LIMIT 3) AS a_9 + FROM + ( + SELECT + testtable_5.a_4 AS a_4, + a_6 AS a_6, + a_6 + 1 AS a_7 + FROM + ( + SELECT + TestTable.KitchenSink AS a_4 + FROM + TestTable + ) AS testtable_5 + JOIN + UNNEST(testtable_5.a_4.repeated_int64_val) AS a_6 + ) AS projectscan_8 + ) AS a_3 +FROM + t AS withrefscan_2; + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$query.$col1#9 AS `$col1` [ARRAY] ++-query= + +-WithScan + +-column_list=[$query.$col1#9] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="t" + | +-with_subquery= + | +-ProjectScan + | +-column_list=[t.correlated#1] + | +-expr_list= + | | +-correlated#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-query= + +-ProjectScan + +-column_list=[$query.$col1#9] + +-expr_list= + | +-$col1#9 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=t.correlated#2) + | +-subquery= + | +-ProjectScan + | +-column_list=[$aggregate.$agg1#8] + | +-input_scan= + | +-ProjectScan + | +-column_list=[$aggregate.$agg1#8] + | +-expr_list= + | | +-$agg1#8 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#15] + | | +-expr_list= + | | | +-injected#15 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#14] + | | +-expr_list= + | | | +-$out#14 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | +-subquery= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.a#10] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.a#10] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$agg_rewriter.[a#10, $orderbycol1#11] + | | | | | +-expr_list= + | | | | | | +-a#10 := + | | | | | | | +-GetStructField + | | | | | | | +-type=INT64 + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#13) + | | | | | | | +-field_idx=0 + | | | | | | +-$orderbycol1#11 := + | | | | | | +-GetStructField + | | | | | | +-type=INT64 + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#13) + | | | | | | +-field_idx=1 + | | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$agg_rewriter.$struct#13] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | | | +-element_column_list=[$agg_rewriter.$struct#13] + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=139-142 + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#11) + | | | +-limit= + | | | | +-Literal(type=INT64, value=3) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-AggregateScan + | +-column_list=[$agg_rewriter.$array#12] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestTable.KitchenSink#5, $array.a#6, $orderby.$orderbycol1#7] + | | +-expr_list= + | | | +-$orderbycol1#7 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$array.a#6) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#5, $array.a#6] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#5], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#5) + | | | +-field_descriptor=repeated_int64_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#6] + | +-aggregate_list= + | +-$array#12 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$array.a#6) + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#7) + | +-having_modifier= + | +-AggregateHavingModifier + | +-kind=MAX + | +-having_expr= + | +-ColumnRef(type=INT64, column=t.correlated#2, is_correlated=TRUE) + +-input_scan= + +-WithRefScan(column_list=[t.correlated#2], with_query_name="t") +[UNPARSED_SQL] +WITH + t AS ( + SELECT + 1 AS a_1 + ) +SELECT + ( + SELECT + ( + SELECT + `IF`((ARRAY_LENGTH(projectscan_17.a_12)) >= 1, projectscan_17.a_12, CAST(NULL AS ARRAY< INT64 >)) AS a_18 + FROM + ( + SELECT + ARRAY( + SELECT + projectscan_16.a_14 AS a_14 + FROM + ( + SELECT + a_13.a_0 AS a_14, + a_13.`$orderbycol1_1` AS a_15 + FROM + UNNEST(aggregatescan_10.a_9) AS a_13 + ) AS projectscan_16 + ORDER BY projectscan_16.a_15 + LIMIT 3 OFFSET 0) AS a_12 + ) AS projectscan_17 + ) AS a_11 + FROM + ( + SELECT + ARRAY_AGG(STRUCT< a_0 INT64, `$orderbycol1_1` INT64 > (projectscan_8.a_6, projectscan_8.a_7) + HAVING MAX withrefscan_2.a_1) AS a_9 + FROM + ( + SELECT + testtable_5.a_4 AS a_4, + a_6 AS a_6, + a_6 + 1 AS a_7 + FROM + ( + SELECT + TestTable.KitchenSink AS a_4 + FROM + TestTable + ) AS testtable_5 + JOIN + UNNEST(testtable_5.a_4.repeated_int64_val) AS a_6 + ) AS projectscan_8 + ) AS aggregatescan_10 + ) AS a_3 +FROM + t AS withrefscan_2; diff --git a/zetasql/analyzer/testdata/array_concat_aggregate.test b/zetasql/analyzer/testdata/array_concat_aggregate.test new file mode 100644 index 000000000..88a11cb96 --- /dev/null +++ b/zetasql/analyzer/testdata/array_concat_aggregate.test @@ -0,0 +1,1475 @@ +[default language_features=V_1_1_ORDER_BY_IN_AGGREGATE] +[default enabled_ast_rewrites=DEFAULTS,+ORDER_BY_AND_LIMIT_IN_AGGREGATE] + +[no_enable_literal_replacement] +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_HAVING_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE,V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE,V_1_2_GROUP_BY_ARRAY,V_1_3_ARRAY_ORDERING] +# Simple ARRAY_AGG with ORDER BY with all the features. +# no_enable_literal_replacement: Distinct and ORDER BY must match exactly, we +# cannot replace just one literal with parameter +SELECT ARRAY_CONCAT_AGG(KitchenSink.repeated_string_val || ["foo"] HAVING MAX 3 ORDER BY KitchenSink.repeated_string_val || ["foo"]LIMIT 10) +FROM TestTable + +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#5] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#5] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:array_concat(ARRAY, repeated(1) ARRAY) -> ARRAY) + | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-Literal(type=ARRAY, value=["foo"]) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$agg1#5 := + +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + +-ColumnRef(type=ARRAY, column=$orderby.$orderbycol1#4) + +-having_modifier= + | +-AggregateHavingModifier + | +-kind=MAX + | +-having_expr= + | +-Literal(type=INT64, value=3) + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=272-314 + | +-column_ref= + | +-ColumnRef(type=ARRAY, column=$orderby.$orderbycol1#4) + +-limit= + +-Literal(type=INT64, value=10) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#5] + +-input_scan= + +-ProjectScan + +-column_list=[$aggregate.$agg1#5] + +-expr_list= + | +-$agg1#5 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#7) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#17] + | +-expr_list= + | | +-injected#17 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#11) + | | +-Literal(type=ARRAY, value=NULL) + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#11) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$flatten.injected#14] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$flatten.injected#12, $offset.injected#13, $flatten.injected#14, $offset.injected#15] + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#12, $offset.injected#13] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#11, is_correlated=TRUE) + | | | | +-element_column_list=[$flatten.injected#12] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#13) + | | | +-array_expr_list= + | | | | +-GetStructField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#12) + | | | | +-field_idx=0 + | | | +-element_column_list=[$flatten.injected#14] + | | | +-array_offset_column= + | | | +-ColumnHolder(column=$offset.injected#15) + | | +-order_by_item_list= + | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#13) + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$offset.injected#15) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$flatten_input.injected#11] + | +-expr_list= + | | +-injected#11 := + | | +-SubqueryExpr + | | +-type=ARRAY>> + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#7, is_correlated=TRUE) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#16] + | | +-expr_list= + | | | +-injected#16 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#10) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#10) + | | | +-Literal(type=ARRAY>>, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#10] + | | +-expr_list= + | | | +-$out#10 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#7, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$agg_rewriter.$struct#9] + | | | +-is_ordered=TRUE + | | | +-expr_list= + | | | | +-$struct#9 := + | | | | +-MakeStruct + | | | | +-type=STRUCT<`array` ARRAY> + | | | | +-field_list= + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$orderbycol1#6) + | | | +-input_scan= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.$orderbycol1#6] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.$orderbycol1#6] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=[$agg_rewriter.$orderbycol1#6] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=[$agg_rewriter.$orderbycol1#6] + | | | | | | +-expr_list= + | | | | | | | +-$orderbycol1#6 := + | | | | | | | +-GetStructField + | | | | | | | +-type=ARRAY + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT<`$orderbycol1_0` ARRAY>, column=$agg_rewriter.$struct#8) + | | | | | | | +-field_idx=0 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#8] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#7, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#8] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$orderbycol1#6) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=272-314 + | | | | +-column_ref= + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$orderbycol1#6) + | | | +-limit= + | | | | +-Literal(type=INT64, value=10) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=[$agg_rewriter.$array#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:array_concat(ARRAY, repeated(1) ARRAY) -> ARRAY) + | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-Literal(type=ARRAY, value=["foo"]) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$array#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT<`$orderbycol1_0` ARRAY>) -> ARRAY>>) + +-MakeStruct + +-type=STRUCT<`$orderbycol1_0` ARRAY> + +-field_list= + +-ColumnRef(type=ARRAY, column=$orderby.$orderbycol1#4) + +-having_modifier= + +-AggregateHavingModifier + +-kind=MAX + +-having_expr= + +-Literal(type=INT64, value=3) +== + +# Duplicate ARRAY_CONCAT_AGGs in the same AggregateScan +SELECT ARRAY_CONCAT_AGG(KitchenSink.repeated_string_val ORDER BY ARRAY_LENGTH(KitchenSink.repeated_string_val)) + , ARRAY_CONCAT_AGG(KitchenSink.repeated_string_val ORDER BY ARRAY_LENGTH(KitchenSink.repeated_string_val)) +FROM TestTable +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#4 := + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-field_descriptor=repeated_string_val + | +-default_value=[] + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=65-110 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + +-GetProtoField + +-type=ARRAY + +-expr= + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-field_descriptor=repeated_string_val + +-default_value=[] + +-order_by_item_list= + +-OrderByItem + +-parse_location=177-222 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-expr_list= + | +-$agg1#5 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#33] + | | +-expr_list= + | | | +-injected#33 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#22) + | | | +-Literal(type=ARRAY, value=NULL) + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#22) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$flatten.injected#25] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#23, $offset.injected#24, $flatten.injected#25, $offset.injected#26] + | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$flatten.injected#23, $offset.injected#24] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#22, is_correlated=TRUE) + | | | | | +-element_column_list=[$flatten.injected#23] + | | | | | +-array_offset_column= + | | | | | +-ColumnHolder(column=$offset.injected#24) + | | | | +-array_expr_list= + | | | | | +-GetStructField + | | | | | +-type=ARRAY + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#23) + | | | | | +-field_idx=0 + | | | | +-element_column_list=[$flatten.injected#25] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#26) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$offset.injected#24) + | | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#26) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$flatten_input.injected#22] + | | +-expr_list= + | | | +-injected#22 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#32] + | | | +-expr_list= + | | | | +-injected#32 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#14) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#14) + | | | | +-Literal(type=ARRAY>>, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#14] + | | | +-expr_list= + | | | | +-$out#14 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY>> + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.$struct#13] + | | | | +-is_ordered=TRUE + | | | | +-expr_list= + | | | | | +-$struct#13 := + | | | | | +-MakeStruct + | | | | | +-type=STRUCT<`array` ARRAY> + | | | | | +-field_list= + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#8) + | | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.$arg#8] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#9, $orderbycol1#10, $arg#8] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[KitchenSink#9, $orderbycol1#10, $arg#8] + | | | | | | +-expr_list= + | | | | | | | +-$arg#8 := + | | | | | | | +-GetProtoField + | | | | | | | +-type=ARRAY + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=PROTO, column=$agg_rewriter.KitchenSink#9) + | | | | | | | +-field_descriptor=repeated_string_val + | | | | | | | +-default_value=[] + | | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[KitchenSink#9, $orderbycol1#10] + | | | | | | +-expr_list= + | | | | | | | +-KitchenSink#9 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=PROTO + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#12) + | | | | | | | | +-field_idx=0 + | | | | | | | +-$orderbycol1#10 := + | | | | | | | +-GetStructField + | | | | | | | +-type=INT64 + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#12) + | | | | | | | +-field_idx=1 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#12] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#12] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#8) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=65-110 + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#10) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-input_scan= + | | +-SingleRowScan + | +-$agg2#7 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#35] + | +-expr_list= + | | +-injected#35 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#27) + | | +-Literal(type=ARRAY, value=NULL) + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#27) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$flatten.injected#30] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$flatten.injected#28, $offset.injected#29, $flatten.injected#30, $offset.injected#31] + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#28, $offset.injected#29] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#27, is_correlated=TRUE) + | | | | +-element_column_list=[$flatten.injected#28] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#29) + | | | +-array_expr_list= + | | | | +-GetStructField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#28) + | | | | +-field_idx=0 + | | | +-element_column_list=[$flatten.injected#30] + | | | +-array_offset_column= + | | | +-ColumnHolder(column=$offset.injected#31) + | | +-order_by_item_list= + | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#29) + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$offset.injected#31) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$flatten_input.injected#27] + | +-expr_list= + | | +-injected#27 := + | | +-SubqueryExpr + | | +-type=ARRAY>> + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18, is_correlated=TRUE) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#34] + | | +-expr_list= + | | | +-injected#34 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#21) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#21) + | | | +-Literal(type=ARRAY>>, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#21] + | | +-expr_list= + | | | +-$out#21 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$agg_rewriter.$struct#20] + | | | +-is_ordered=TRUE + | | | +-expr_list= + | | | | +-$struct#20 := + | | | | +-MakeStruct + | | | | +-type=STRUCT<`array` ARRAY> + | | | | +-field_list= + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#15) + | | | +-input_scan= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.$arg#15] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-FilterScan + | | | | +-column_list=$agg_rewriter.[KitchenSink#16, $orderbycol1#17, $arg#15] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#16, $orderbycol1#17, $arg#15] + | | | | | +-expr_list= + | | | | | | +-$arg#15 := + | | | | | | +-GetProtoField + | | | | | | +-type=ARRAY + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=PROTO, column=$agg_rewriter.KitchenSink#16) + | | | | | | +-field_descriptor=repeated_string_val + | | | | | | +-default_value=[] + | | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#16, $orderbycol1#17] + | | | | | +-expr_list= + | | | | | | +-KitchenSink#16 := + | | | | | | | +-GetStructField + | | | | | | | +-type=PROTO + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#19) + | | | | | | | +-field_idx=0 + | | | | | | +-$orderbycol1#17 := + | | | | | | +-GetStructField + | | | | | | +-type=INT64 + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#19) + | | | | | | +-field_idx=1 + | | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$agg_rewriter.$struct#19] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18, is_correlated=TRUE) + | | | | | +-element_column_list=[$agg_rewriter.$struct#19] + | | | | +-filter_expr= + | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#15) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=177-222 + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#17) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#11, $array#18] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#4 := + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$array#11 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT, `$orderbycol1_1` INT64>) -> ARRAY, `$orderbycol1_1` INT64>>) + | +-MakeStruct + | +-type=STRUCT, `$orderbycol1_1` INT64> + | +-field_list= + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) + +-$array#18 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT, `$orderbycol1_1` INT64>) -> ARRAY, `$orderbycol1_1` INT64>>) + +-MakeStruct + +-type=STRUCT, `$orderbycol1_1` INT64> + +-field_list= + +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +== + +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE] + +# Slight different ARRAY_CONCAT_AGGs in the same AggregateScan +SELECT ARRAY_CONCAT_AGG(KitchenSink.repeated_string_val ORDER BY ARRAY_LENGTH(KitchenSink.repeated_string_val) LIMIT 2), + ARRAY_CONCAT_AGG(KitchenSink.repeated_bytes_val ORDER BY 3 - ASCII(KitchenSink.repeated_bytes_val[SAFE_OFFSET(0)])) +FROM TestTable +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#4 := + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$subtract(INT64, INT64) -> INT64) + | | +-Literal(type=INT64, value=3) + | | +-FunctionCall(ZetaSQL:ascii(BYTES) -> INT64) + | | +-FunctionCall(ZetaSQL:$safe_array_at_offset(ARRAY, INT64) -> BYTES) + | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_bytes_val + | | | +-default_value=[] + | | +-Literal(type=INT64, value=0) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-field_descriptor=repeated_string_val + | +-default_value=[] + | +-order_by_item_list= + | | +-OrderByItem + | | +-parse_location=128-173 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) + | +-limit= + | +-Literal(type=INT64, value=2) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + +-GetProtoField + +-type=ARRAY + +-expr= + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-field_descriptor=repeated_bytes_val + +-default_value=[] + +-order_by_item_list= + +-OrderByItem + +-parse_location=248-305 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#7 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-expr_list= + | +-$agg1#5 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#33] + | | +-expr_list= + | | | +-injected#33 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#22) + | | | +-Literal(type=ARRAY, value=NULL) + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#22) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$flatten.injected#25] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#23, $offset.injected#24, $flatten.injected#25, $offset.injected#26] + | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$flatten.injected#23, $offset.injected#24] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#22, is_correlated=TRUE) + | | | | | +-element_column_list=[$flatten.injected#23] + | | | | | +-array_offset_column= + | | | | | +-ColumnHolder(column=$offset.injected#24) + | | | | +-array_expr_list= + | | | | | +-GetStructField + | | | | | +-type=ARRAY + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#23) + | | | | | +-field_idx=0 + | | | | +-element_column_list=[$flatten.injected#25] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#26) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$offset.injected#24) + | | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#26) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$flatten_input.injected#22] + | | +-expr_list= + | | | +-injected#22 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#32] + | | | +-expr_list= + | | | | +-injected#32 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#14) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#14) + | | | | +-Literal(type=ARRAY>>, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#14] + | | | +-expr_list= + | | | | +-$out#14 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY>> + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.$struct#13] + | | | | +-is_ordered=TRUE + | | | | +-expr_list= + | | | | | +-$struct#13 := + | | | | | +-MakeStruct + | | | | | +-type=STRUCT<`array` ARRAY> + | | | | | +-field_list= + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#8) + | | | | +-input_scan= + | | | | +-LimitOffsetScan + | | | | +-column_list=[$agg_rewriter.$arg#8] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-OrderByScan + | | | | | +-column_list=[$agg_rewriter.$arg#8] + | | | | | +-is_ordered=TRUE + | | | | | +-input_scan= + | | | | | | +-FilterScan + | | | | | | +-column_list=$agg_rewriter.[KitchenSink#9, $orderbycol1#10, $arg#8] + | | | | | | +-input_scan= + | | | | | | | +-ProjectScan + | | | | | | | +-column_list=$agg_rewriter.[KitchenSink#9, $orderbycol1#10, $arg#8] + | | | | | | | +-expr_list= + | | | | | | | | +-$arg#8 := + | | | | | | | | +-GetProtoField + | | | | | | | | +-type=ARRAY + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=PROTO, column=$agg_rewriter.KitchenSink#9) + | | | | | | | | +-field_descriptor=repeated_string_val + | | | | | | | | +-default_value=[] + | | | | | | | +-input_scan= + | | | | | | | +-ProjectScan + | | | | | | | +-column_list=$agg_rewriter.[KitchenSink#9, $orderbycol1#10] + | | | | | | | +-expr_list= + | | | | | | | | +-KitchenSink#9 := + | | | | | | | | | +-GetStructField + | | | | | | | | | +-type=PROTO + | | | | | | | | | +-expr= + | | | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#12) + | | | | | | | | | +-field_idx=0 + | | | | | | | | +-$orderbycol1#10 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=INT64 + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#12) + | | | | | | | | +-field_idx=1 + | | | | | | | +-input_scan= + | | | | | | | +-ArrayScan + | | | | | | | +-column_list=[$agg_rewriter.$struct#12] + | | | | | | | +-array_expr_list= + | | | | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#11, is_correlated=TRUE) + | | | | | | | +-element_column_list=[$agg_rewriter.$struct#12] + | | | | | | +-filter_expr= + | | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#8) + | | | | | +-order_by_item_list= + | | | | | +-OrderByItem + | | | | | +-parse_location=128-173 + | | | | | +-column_ref= + | | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#10) + | | | | +-limit= + | | | | | +-Literal(type=INT64, value=2) + | | | | +-offset= + | | | | +-Literal(type=INT64, value=0) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-input_scan= + | | +-SingleRowScan + | +-$agg2#7 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#35] + | +-expr_list= + | | +-injected#35 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#27) + | | +-Literal(type=ARRAY, value=NULL) + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#27) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$flatten.injected#30] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$flatten.injected#28, $offset.injected#29, $flatten.injected#30, $offset.injected#31] + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#28, $offset.injected#29] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#27, is_correlated=TRUE) + | | | | +-element_column_list=[$flatten.injected#28] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#29) + | | | +-array_expr_list= + | | | | +-GetStructField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#28) + | | | | +-field_idx=0 + | | | +-element_column_list=[$flatten.injected#30] + | | | +-array_offset_column= + | | | +-ColumnHolder(column=$offset.injected#31) + | | +-order_by_item_list= + | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#29) + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$offset.injected#31) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$flatten_input.injected#27] + | +-expr_list= + | | +-injected#27 := + | | +-SubqueryExpr + | | +-type=ARRAY>> + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18, is_correlated=TRUE) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#34] + | | +-expr_list= + | | | +-injected#34 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#21) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#21) + | | | +-Literal(type=ARRAY>>, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#21] + | | +-expr_list= + | | | +-$out#21 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$agg_rewriter.$struct#20] + | | | +-is_ordered=TRUE + | | | +-expr_list= + | | | | +-$struct#20 := + | | | | +-MakeStruct + | | | | +-type=STRUCT<`array` ARRAY> + | | | | +-field_list= + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#15) + | | | +-input_scan= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.$arg#15] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-FilterScan + | | | | +-column_list=$agg_rewriter.[KitchenSink#16, $orderbycol1#17, $arg#15] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#16, $orderbycol1#17, $arg#15] + | | | | | +-expr_list= + | | | | | | +-$arg#15 := + | | | | | | +-GetProtoField + | | | | | | +-type=ARRAY + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=PROTO, column=$agg_rewriter.KitchenSink#16) + | | | | | | +-field_descriptor=repeated_bytes_val + | | | | | | +-default_value=[] + | | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#16, $orderbycol1#17] + | | | | | +-expr_list= + | | | | | | +-KitchenSink#16 := + | | | | | | | +-GetStructField + | | | | | | | +-type=PROTO + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#19) + | | | | | | | +-field_idx=0 + | | | | | | +-$orderbycol1#17 := + | | | | | | +-GetStructField + | | | | | | +-type=INT64 + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#19) + | | | | | | +-field_idx=1 + | | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$agg_rewriter.$struct#19] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#18, is_correlated=TRUE) + | | | | | +-element_column_list=[$agg_rewriter.$struct#19] + | | | | +-filter_expr= + | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#15) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=248-305 + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#17) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#11, $array#18] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#4 := + | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$subtract(INT64, INT64) -> INT64) + | | +-Literal(type=INT64, value=3) + | | +-FunctionCall(ZetaSQL:ascii(BYTES) -> INT64) + | | +-FunctionCall(ZetaSQL:$safe_array_at_offset(ARRAY, INT64) -> BYTES) + | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_bytes_val + | | | +-default_value=[] + | | +-Literal(type=INT64, value=0) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$array#11 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT, `$orderbycol1_1` INT64>) -> ARRAY, `$orderbycol1_1` INT64>>) + | +-MakeStruct + | +-type=STRUCT, `$orderbycol1_1` INT64> + | +-field_list= + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) + +-$array#18 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT, `$orderbycol1_1` INT64>) -> ARRAY, `$orderbycol1_1` INT64>>) + +-MakeStruct + +-type=STRUCT, `$orderbycol1_1` INT64> + +-field_list= + +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) +== + +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE] + +# ARRAY_CONCAT_AGG with limits +SELECT ARRAY_CONCAT_AGG(KitchenSink.repeated_string_val LIMIT 2), + ARRAY_CONCAT_AGG(KitchenSink.repeated_string_val ORDER BY ARRAY_LENGTH(KitchenSink.repeated_string_val)+1 LIMIT 3) +FROM TestTable +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#6 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#4, $agg2#6] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#4, $agg2#6] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#5] + | +-expr_list= + | | +-$orderbycol1#5 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$agg1#4 := + | +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-field_descriptor=repeated_string_val + | +-default_value=[] + | +-limit= + | +-Literal(type=INT64, value=2) + +-$agg2#6 := + +-AggregateFunctionCall(ZetaSQL:array_concat_agg(ARRAY) -> ARRAY) + +-GetProtoField + +-type=ARRAY + +-expr= + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-field_descriptor=repeated_string_val + +-default_value=[] + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=162-209 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) + +-limit= + +-Literal(type=INT64, value=3) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [ARRAY] +| +-$aggregate.$agg2#6 AS `$col2` [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#4, $agg2#6] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#4, $agg2#6] + +-expr_list= + | +-$agg1#4 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#9) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#31] + | | +-expr_list= + | | | +-injected#31 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#20) + | | | +-Literal(type=ARRAY, value=NULL) + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#20) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$flatten.injected#23] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#21, $offset.injected#22, $flatten.injected#23, $offset.injected#24] + | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$flatten.injected#21, $offset.injected#22] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#20, is_correlated=TRUE) + | | | | | +-element_column_list=[$flatten.injected#21] + | | | | | +-array_offset_column= + | | | | | +-ColumnHolder(column=$offset.injected#22) + | | | | +-array_expr_list= + | | | | | +-GetStructField + | | | | | +-type=ARRAY + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#21) + | | | | | +-field_idx=0 + | | | | +-element_column_list=[$flatten.injected#23] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#24) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$offset.injected#22) + | | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#24) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$flatten_input.injected#20] + | | +-expr_list= + | | | +-injected#20 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#30] + | | | +-expr_list= + | | | | +-injected#30 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#12) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#12) + | | | | +-Literal(type=ARRAY>>, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#12] + | | | +-expr_list= + | | | | +-$out#12 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY>> + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.$struct#11] + | | | | +-expr_list= + | | | | | +-$struct#11 := + | | | | | +-MakeStruct + | | | | | +-type=STRUCT<`array` ARRAY> + | | | | | +-field_list= + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#7) + | | | | +-input_scan= + | | | | +-LimitOffsetScan + | | | | +-column_list=[$agg_rewriter.$arg#7] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=[$agg_rewriter.$arg#7] + | | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#8, $arg#7] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[KitchenSink#8, $arg#7] + | | | | | | +-expr_list= + | | | | | | | +-$arg#7 := + | | | | | | | +-GetProtoField + | | | | | | | +-type=ARRAY + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=PROTO, column=$agg_rewriter.KitchenSink#8) + | | | | | | | +-field_descriptor=repeated_string_val + | | | | | | | +-default_value=[] + | | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=[$agg_rewriter.KitchenSink#8] + | | | | | | +-expr_list= + | | | | | | | +-KitchenSink#8 := + | | | | | | | +-GetStructField + | | | | | | | +-type=PROTO + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT>, column=$agg_rewriter.$struct#10) + | | | | | | | +-field_idx=0 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#10] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#10] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#7) + | | | | +-limit= + | | | | | +-Literal(type=INT64, value=2) + | | | | +-offset= + | | | | +-Literal(type=INT64, value=0) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-input_scan= + | | +-SingleRowScan + | +-$agg2#6 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#16) + | +-subquery= + | +-ProjectScan + | +-column_list=[$with_expr.injected#33] + | +-expr_list= + | | +-injected#33 := + | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | +-FunctionCall(ZetaSQL:$is_null(ARRAY>>) -> BOOL) + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#25) + | | +-Literal(type=ARRAY, value=NULL) + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=ARRAY + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#25) + | | +-subquery= + | | +-OrderByScan + | | +-column_list=[$flatten.injected#28] + | | +-is_ordered=TRUE + | | +-input_scan= + | | | +-ArrayScan + | | | +-column_list=[$flatten.injected#26, $offset.injected#27, $flatten.injected#28, $offset.injected#29] + | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$flatten.injected#26, $offset.injected#27] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>>, column=$flatten_input.injected#25, is_correlated=TRUE) + | | | | +-element_column_list=[$flatten.injected#26] + | | | | +-array_offset_column= + | | | | +-ColumnHolder(column=$offset.injected#27) + | | | +-array_expr_list= + | | | | +-GetStructField + | | | | +-type=ARRAY + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT<`array` ARRAY>, column=$flatten.injected#26) + | | | | +-field_idx=0 + | | | +-element_column_list=[$flatten.injected#28] + | | | +-array_offset_column= + | | | +-ColumnHolder(column=$offset.injected#29) + | | +-order_by_item_list= + | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$offset.injected#27) + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$offset.injected#29) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$flatten_input.injected#25] + | +-expr_list= + | | +-injected#25 := + | | +-SubqueryExpr + | | +-type=ARRAY>> + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#16, is_correlated=TRUE) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#32] + | | +-expr_list= + | | | +-injected#32 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY>>, ARRAY>>) -> ARRAY>>) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY>>) -> INT64) + | | | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#19) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY>>, column=null_if_empty_array.$out#19) + | | | +-Literal(type=ARRAY>>, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#19] + | | +-expr_list= + | | | +-$out#19 := + | | | +-SubqueryExpr + | | | +-type=ARRAY>> + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#16, is_correlated=TRUE) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$agg_rewriter.$struct#18] + | | | +-is_ordered=TRUE + | | | +-expr_list= + | | | | +-$struct#18 := + | | | | +-MakeStruct + | | | | +-type=STRUCT<`array` ARRAY> + | | | | +-field_list= + | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#13) + | | | +-input_scan= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.$arg#13] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.$arg#13] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=$agg_rewriter.[KitchenSink#14, $orderbycol1#15, $arg#13] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[KitchenSink#14, $orderbycol1#15, $arg#13] + | | | | | | +-expr_list= + | | | | | | | +-$arg#13 := + | | | | | | | +-GetProtoField + | | | | | | | +-type=ARRAY + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=PROTO, column=$agg_rewriter.KitchenSink#14) + | | | | | | | +-field_descriptor=repeated_string_val + | | | | | | | +-default_value=[] + | | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[KitchenSink#14, $orderbycol1#15] + | | | | | | +-expr_list= + | | | | | | | +-KitchenSink#14 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=PROTO + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#17) + | | | | | | | | +-field_idx=0 + | | | | | | | +-$orderbycol1#15 := + | | | | | | | +-GetStructField + | | | | | | | +-type=INT64 + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, `$orderbycol1_1` INT64>, column=$agg_rewriter.$struct#17) + | | | | | | | +-field_idx=1 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#17] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY, `$orderbycol1_1` INT64>>, column=$agg_rewriter.$array#16, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#17] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | | | +-ColumnRef(type=ARRAY, column=$agg_rewriter.$arg#13) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=162-209 + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#15) + | | | +-limit= + | | | | +-Literal(type=INT64, value=3) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#9, $array#16] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $orderby.$orderbycol1#5] + | +-expr_list= + | | +-$orderbycol1#5 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-aggregate_list= + +-$array#9 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT>) -> ARRAY>>) + | +-MakeStruct + | +-type=STRUCT> + | +-field_list= + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-$array#16 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT, `$orderbycol1_1` INT64>) -> ARRAY, `$orderbycol1_1` INT64>>) + +-MakeStruct + +-type=STRUCT, `$orderbycol1_1` INT64> + +-field_list= + +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) diff --git a/zetasql/analyzer/testdata/array_element.test b/zetasql/analyzer/testdata/array_element.test index 42329649c..d7dc2d148 100644 --- a/zetasql/analyzer/testdata/array_element.test +++ b/zetasql/analyzer/testdata/array_element.test @@ -839,6 +839,7 @@ QueryStmt | | | | +-ColumnRef(type=STRING, column=$subquery1.k#4, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=192-203 | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#7) | | | +-is_descending=TRUE @@ -961,6 +962,7 @@ QueryStmt | | | | | +-ColumnRef(type=INT64, column=$subquery1.k#4, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=328-339 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#7) | | | | +-is_descending=TRUE @@ -1004,7 +1006,7 @@ select MessageWithMapField.int32_int32_map[SAFE_KEY(key + 1)] select PROTO_MODIFY_MAP(MessageWithMapField.int32_int32_map, key + 1, key + 2) from MapFieldTable -- -ERROR: No matching signature for function PROTO_MODIFY_MAP(ARRAY, INT64, INT64); some key or value did not match the map's key or value type. Supported signature: PROTO_MODIFY_MAP(PROTO_MAP, [PROTO_MAP_KEY, ...], [PROTO_MAP_VALUE, ...]) [at 1:8] +ERROR: No matching signature for function PROTO_MODIFY_MAP(ARRAY, INT64, INT64); some key or value did not match the map's key or value type. Supported signature: PROTO_MODIFY_MAP(PROTO_MAP, [[PROTO_MAP_KEY, PROTO_MAP_VALUE], ...]) [at 1:8] select PROTO_MODIFY_MAP(MessageWithMapField.int32_int32_map, key + 1, key + 2) ^ == diff --git a/zetasql/analyzer/testdata/array_filter.test b/zetasql/analyzer/testdata/array_filter.test index 3009df7a0..f953ba369 100644 --- a/zetasql/analyzer/testdata/array_filter.test +++ b/zetasql/analyzer/testdata/array_filter.test @@ -73,6 +73,7 @@ QueryStmt | | | +-Literal(type=INT64, value=0) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#5) | +-input_scan= @@ -161,6 +162,7 @@ QueryStmt | | | +-Literal(type=INT64, value=0) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#5) | +-input_scan= @@ -247,6 +249,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=236-239 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | +-input_scan= @@ -338,6 +341,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= @@ -433,6 +437,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=236-239 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | +-input_scan= @@ -523,6 +528,7 @@ QueryStmt | | | +-Literal(type=INT64, value=0) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= @@ -620,6 +626,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= @@ -726,6 +733,7 @@ QueryStmt | | | +-Literal(type=INT64, value=1) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#12) | +-input_scan= @@ -773,6 +781,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=0) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=231-234 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | | +-input_scan= @@ -903,6 +912,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=1) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=231-234 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#13) | | +-input_scan= @@ -950,6 +960,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=0) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=231-234 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.off#9) | | | +-input_scan= @@ -1102,6 +1113,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=0) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=231-234 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.off#17) | | | +-input_scan= @@ -1117,6 +1129,7 @@ QueryStmt | | | +-SingleRowScan | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#12) | +-input_scan= @@ -1283,6 +1296,7 @@ QueryStmt | | | | | | +-Literal(type=INT64, value=0) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=231-234 | | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.off#18) | | | | +-input_scan= @@ -1298,6 +1312,7 @@ QueryStmt | | | | +-SingleRowScan | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=231-234 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#13) | | +-input_scan= @@ -1414,6 +1429,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=1) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=231-234 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | | +-input_scan= @@ -1537,6 +1553,7 @@ QueryStmt | | | | +-ColumnRef(type=INT64, column=$array.element#7) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=231-234 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | | +-input_scan= @@ -1640,6 +1657,7 @@ QueryStmt | | | +-Literal(type=INT64, value=0) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#11) | +-input_scan= @@ -1686,6 +1704,7 @@ QueryStmt | | | | +-ColumnHolder(column=$array_offset.off#6) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=216-219 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | | +-input_scan= @@ -1815,6 +1834,7 @@ QueryStmt | | | | | | +-Literal(type=INT64, value=1) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=231-234 | | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.off#16) | | | | +-input_scan= @@ -1837,6 +1857,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#10) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#10) | +-input_scan= @@ -2005,6 +2026,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=1) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=231-234 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#10) | | +-input_scan= @@ -2105,6 +2127,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=0) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=243-246 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#5) | | +-Literal(type=ARRAY, value=NULL, has_explicit_type=TRUE) diff --git a/zetasql/analyzer/testdata/array_functions.test b/zetasql/analyzer/testdata/array_functions.test index 94d3f8a9b..01af851c3 100644 --- a/zetasql/analyzer/testdata/array_functions.test +++ b/zetasql/analyzer/testdata/array_functions.test @@ -350,6 +350,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=4) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=289-295 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | +-limit= @@ -384,6 +385,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=4) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=499-510 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | +-is_descending=TRUE @@ -502,6 +504,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=5) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=289-295 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | +-limit= @@ -540,6 +543,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=5) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=499-510 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | +-is_descending=TRUE @@ -662,6 +666,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=7) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=284-290 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | +-limit= @@ -696,6 +701,7 @@ QueryStmt | | | | | +-Literal(type=INT64, value=7) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=489-500 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | +-is_descending=TRUE @@ -802,6 +808,7 @@ QueryStmt | | | +-Literal(type=INT64, value=2) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=188-194 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | +-input_scan= @@ -915,6 +922,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=$subquery1.n#3, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=351-357 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | +-input_scan= @@ -1024,6 +1032,7 @@ QueryStmt | | | | +-ColumnRef(type=INT64, column=$with_expr.start_offset#4, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=436-442 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | +-input_scan= @@ -1133,6 +1142,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=$subquery1.n#3, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=360-366 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | +-input_scan= @@ -1242,6 +1252,7 @@ QueryStmt | | | | +-ColumnRef(type=INT64, column=$with_expr.end_offset#4, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=451-457 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | +-input_scan= @@ -1374,6 +1385,7 @@ QueryStmt | | | | | +-SingleRowScan | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=284-290 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#7) | | | +-limit= @@ -1415,6 +1427,7 @@ QueryStmt | | | | | +-SingleRowScan | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=489-500 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#9) | | | | +-is_descending=TRUE @@ -1549,6 +1562,7 @@ QueryStmt | | | | | +-SingleRowScan | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=284-290 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#9) | | | +-limit= @@ -1593,6 +1607,7 @@ QueryStmt | | | | | +-SingleRowScan | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=489-500 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#11) | | | | +-is_descending=TRUE diff --git a/zetasql/analyzer/testdata/array_path.test b/zetasql/analyzer/testdata/array_path.test index 57620c9f0..3559ab472 100644 --- a/zetasql/analyzer/testdata/array_path.test +++ b/zetasql/analyzer/testdata/array_path.test @@ -2333,6 +2333,7 @@ QueryStmt | +-ColumnHolder(column=$array_offset.o#22) +-order_by_item_list= +-OrderByItem + +-parse_location=93-94 +-column_ref= +-ColumnRef(type=INT64, column=$array_offset.o#22) @@ -2389,6 +2390,7 @@ QueryStmt | +-ColumnHolder(column=$array_offset.o#22) +-order_by_item_list= +-OrderByItem + +-parse_location=93-94 +-column_ref= +-ColumnRef(type=INT64, column=$array_offset.o#22) == @@ -2628,6 +2630,7 @@ QueryStmt | +-ColumnHolder(column=$array_offset.o#3) +-order_by_item_list= +-OrderByItem + +-parse_location=203-204 +-column_ref= +-ColumnRef(type=INT64, column=$array_offset.o#3) @@ -2743,6 +2746,7 @@ QueryStmt | +-ColumnHolder(column=$array_offset.o#3) +-order_by_item_list= +-OrderByItem + +-parse_location=203-204 +-column_ref= +-ColumnRef(type=INT64, column=$array_offset.o#3) == @@ -4375,6 +4379,7 @@ QueryStmt | | | | | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) | | | | | | +-order_by_item_list= | | | | | | +-OrderByItem + | | | | | | +-parse_location=231-234 | | | | | | +-column_ref= | | | | | | +-ColumnRef(type=INT64, column=$array_offset.off#13) | | | | | +-input_scan= diff --git a/zetasql/analyzer/testdata/array_transform.test b/zetasql/analyzer/testdata/array_transform.test index 11a1bcfc7..5aefbdd6a 100644 --- a/zetasql/analyzer/testdata/array_transform.test +++ b/zetasql/analyzer/testdata/array_transform.test @@ -74,6 +74,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#5) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#5) | +-input_scan= @@ -161,6 +162,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=221-224 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | +-input_scan= @@ -253,6 +255,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#7) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= @@ -349,6 +352,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#8) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=221-224 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | +-input_scan= @@ -441,6 +445,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#7) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= @@ -539,6 +544,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#7) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= @@ -647,6 +653,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#13) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#13) | +-input_scan= @@ -695,6 +702,7 @@ QueryStmt | | | | +-ColumnHolder(column=$array_offset.off#8) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=216-219 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | | +-input_scan= @@ -827,6 +835,7 @@ QueryStmt | | | | +-ColumnHolder(column=$array_offset.off#14) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=216-219 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#14) | | +-input_scan= @@ -875,6 +884,7 @@ QueryStmt | | | | | +-ColumnHolder(column=$array_offset.off#9) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=216-219 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.off#9) | | | +-input_scan= @@ -1022,6 +1032,7 @@ QueryStmt | | | | | | +-ColumnHolder(column=$array_offset.off#20) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=216-219 | | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.off#20) | | | | +-input_scan= @@ -1045,6 +1056,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#13) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#13) | +-input_scan= @@ -1205,6 +1217,7 @@ QueryStmt | | | | | | | +-ColumnHolder(column=$array_offset.off#21) | | | | | | +-order_by_item_list= | | | | | | +-OrderByItem + | | | | | | +-parse_location=216-219 | | | | | | +-column_ref= | | | | | | +-ColumnRef(type=INT64, column=$array_offset.off#21) | | | | | +-input_scan= @@ -1228,6 +1241,7 @@ QueryStmt | | | | +-ColumnHolder(column=$array_offset.off#14) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=216-219 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#14) | | +-input_scan= @@ -1345,6 +1359,7 @@ QueryStmt | | | | +-ColumnHolder(column=$array_offset.off#8) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=216-219 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | | +-input_scan= @@ -1469,6 +1484,7 @@ QueryStmt | | | | +-ColumnHolder(column=$array_offset.off#8) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=216-219 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.off#8) | | +-input_scan= diff --git a/zetasql/analyzer/testdata/binary_rewriter_functions.test b/zetasql/analyzer/testdata/binary_rewriter_functions.test index 58153b884..86b8118fc 100644 --- a/zetasql/analyzer/testdata/binary_rewriter_functions.test +++ b/zetasql/analyzer/testdata/binary_rewriter_functions.test @@ -69,6 +69,7 @@ QueryStmt | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#3, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=225-231 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | +-input_scan= @@ -155,6 +156,7 @@ QueryStmt | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#3, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=237-243 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | | +-Literal(type=ARRAY, value=NULL, has_explicit_type=TRUE) @@ -261,6 +263,7 @@ QueryStmt | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#3, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=220-226 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | +-input_scan= @@ -342,6 +345,7 @@ QueryStmt | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#3, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=232-238 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.offset#5) | | +-Literal(type=ARRAY, value=NULL, has_explicit_type=TRUE) diff --git a/zetasql/analyzer/testdata/casts.test b/zetasql/analyzer/testdata/casts.test index ec471ed13..92a869537 100644 --- a/zetasql/analyzer/testdata/casts.test +++ b/zetasql/analyzer/testdata/casts.test @@ -5656,3 +5656,12 @@ SELECT CAST(123 AS DATETIME) ERROR: Invalid cast from INT64 to DATETIME [at 1:13] SELECT CAST(123 AS DATETIME) ^ +== + +[language_features=PARAMETERIZED_TYPES] +# Test very large type parameter +select CAST("" as float(18446744073709551615)); +-- +ERROR: Integer type parameters must fall in the domain of INT64. Supplied value '18446744073709551615' is outside that range. Specific types typically have tighter bounds specific to that type. [at 2:25] +select CAST("" as float(18446744073709551615)); + ^ diff --git a/zetasql/analyzer/testdata/collation.test b/zetasql/analyzer/testdata/collation.test index 320ac5e56..29df5bca7 100644 --- a/zetasql/analyzer/testdata/collation.test +++ b/zetasql/analyzer/testdata/collation.test @@ -1147,6 +1147,7 @@ QueryStmt | | | +-TableScan(column_list=[CollatedTable.string_ci#5{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=150-159 | | +-column_ref= | | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#5{Collation:"und:ci"}) | | +-collation=und:ci @@ -3371,14 +3372,17 @@ QueryStmt | +-TableScan(column_list=CollatedTable.[string_ci#1, string_binary#2, array_with_string_ci#4], table=CollatedTable, column_index_list=[0, 1, 3]) +-order_by_item_list= +-OrderByItem + | +-parse_location=60-69 | +-column_ref= | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) | +-collation=und:ci +-OrderByItem + | +-parse_location=71-84 | +-column_ref= | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"binary"}, column=CollatedTable.string_binary#2{Collation:"binary"}) | +-collation=binary +-OrderByItem + +-parse_location=86-106 +-column_ref= | +-ColumnRef(type=ARRAY, type_annotation_map=[{Collation:"und:ci"}], column=CollatedTable.array_with_string_ci#4[{Collation:"und:ci"}]) +-collation=[und:ci] @@ -3416,10 +3420,12 @@ QueryStmt | +-TableScan(column_list=CollatedTable.[string_ci#1, struct_with_string_ci#3], table=CollatedTable, column_index_list=[0, 2]) +-order_by_item_list= +-OrderByItem + | +-parse_location=57-81 | +-column_ref= | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=$orderby.$orderbycol1#5{Collation:"und:ci"}) | +-collation=und:ci +-OrderByItem + +-parse_location=83-106 +-column_ref= | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=$orderby.$orderbycol2#6{Collation:"und:ci"}) +-collation=und:ci @@ -3443,12 +3449,14 @@ QueryStmt | +-TableScan(column_list=CollatedTable.[string_ci#1, string_binary#2], table=CollatedTable, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + | +-parse_location=60-86 | +-column_ref= | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) | +-collation_name= | | +-Literal(type=STRING, value="binary") | +-collation=binary +-OrderByItem + +-parse_location=97-137 +-column_ref= | +-ColumnRef(type=STRING, type_annotation_map={Collation:"binary"}, column=CollatedTable.string_binary#2{Collation:"binary"}) +-collation_name= @@ -3482,6 +3490,7 @@ QueryStmt | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=37-46 | +-column_ref= | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) | +-collation=und:ci @@ -3491,6 +3500,7 @@ QueryStmt | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=148-173 | +-column_ref= | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) | +-collation_name= @@ -3502,6 +3512,7 @@ QueryStmt +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) +-order_by_item_list= +-OrderByItem + +-parse_location=213-249 +-column_ref= | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) +-collation_name= @@ -7819,6 +7830,7 @@ QueryStmt | | | | +-ColumnRef(type=INT64, column=$with_expr.end_offset#6, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=770-773 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.idx#8) | | +-input_scan= @@ -8019,6 +8031,7 @@ QueryStmt | | | | | +-ColumnRef(type=INT64, column=$with_expr.end_offset#10, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=770-773 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.idx#12) | | | +-input_scan= diff --git a/zetasql/analyzer/testdata/correlated_aggr_subquery.test b/zetasql/analyzer/testdata/correlated_aggr_subquery.test index c3be696eb..cb87860a4 100644 --- a/zetasql/analyzer/testdata/correlated_aggr_subquery.test +++ b/zetasql/analyzer/testdata/correlated_aggr_subquery.test @@ -3600,6 +3600,7 @@ QueryStmt | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=54-149 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#6) @@ -3763,5 +3764,6 @@ QueryStmt | +-TableScan(column_list=SimpleTypesWithStruct.[key#1, TestEnum#2, TestStruct#3], table=SimpleTypesWithStruct, column_index_list=[0, 1, 2], alias="st") +-order_by_item_list= +-OrderByItem + +-parse_location=52-99 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#9) diff --git a/zetasql/analyzer/testdata/correlated_expr_subquery.test b/zetasql/analyzer/testdata/correlated_expr_subquery.test index 6c21fabc4..283235dd6 100644 --- a/zetasql/analyzer/testdata/correlated_expr_subquery.test +++ b/zetasql/analyzer/testdata/correlated_expr_subquery.test @@ -512,6 +512,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=36-52 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -1460,6 +1461,7 @@ QueryStmt | | +-v#5 := ColumnRef(type=INT32, column=$array.v#4) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=110-111 | +-column_ref= | +-ColumnRef(type=INT32, column=$distinct.v#5) +-input_scan= @@ -1515,9 +1517,11 @@ QueryStmt | | +-v#5 := ColumnRef(type=INT32, column=$array.v#4) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=110-111 | | +-column_ref= | | +-ColumnRef(type=INT32, column=$distinct.v#5) | +-OrderByItem + | +-parse_location=113-119 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol2#6) +-input_scan= @@ -1574,6 +1578,7 @@ QueryStmt | | +-key#6 := ColumnRef(type=INT32, column=$expr_subquery.key#5) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=115-121 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#7) +-input_scan= @@ -1633,9 +1638,11 @@ QueryStmt | | +-key#7 := ColumnRef(type=INT32, column=$expr_subquery.key#5) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=118-119 | | +-column_ref= | | +-ColumnRef(type=INT32, column=$distinct.v#6) | +-OrderByItem + | +-parse_location=121-127 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol2#8) +-input_scan= @@ -1689,9 +1696,11 @@ QueryStmt | | +-key#7 := ColumnRef(type=INT32, column=$expr_subquery.key#5) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=118-119 | | +-column_ref= | | +-ColumnRef(type=INT32, column=$distinct.v#6) | +-OrderByItem + | +-parse_location=121-122 | +-column_ref= | +-ColumnRef(type=INT32, column=$distinct.key#7) +-input_scan= @@ -1759,6 +1768,7 @@ QueryStmt | | | +-ex#4 := ColumnRef(type=STRING, column=$array.ex#1, is_correlated=TRUE) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=100-102 | | +-column_ref= | | +-ColumnRef(type=STRING, column=$groupby.ex#4) | +-Literal(type=STRING, value="a") @@ -2085,6 +2095,7 @@ QueryStmt | | +-m#5 := ColumnRef(type=INT32, column=Int32ValueTable.value#1, is_correlated=TRUE) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=121-126 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#7) +-input_scan= @@ -2278,6 +2289,7 @@ QueryStmt | | +-ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=136-139 | +-column_ref= | +-ColumnRef(type=INT64, column=$expr_subquery.key#7) +-input_scan= diff --git a/zetasql/analyzer/testdata/correlated_subquery_outer_aggr.test b/zetasql/analyzer/testdata/correlated_subquery_outer_aggr.test index 4db3c6573..0b995def1 100644 --- a/zetasql/analyzer/testdata/correlated_subquery_outer_aggr.test +++ b/zetasql/analyzer/testdata/correlated_subquery_outer_aggr.test @@ -1114,6 +1114,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=50-62 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) == @@ -1160,6 +1161,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=56-73 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) == @@ -1927,6 +1929,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=50-71 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) == @@ -1984,6 +1987,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=56-82 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) == @@ -2350,6 +2354,7 @@ QueryStmt | | | +-TestEnum#7 := ColumnRef(type=ENUM, column=TestTable.TestEnum#5) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=145-212 | | +-column_ref= | | +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#9) | +-input_scan= @@ -2361,6 +2366,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=47-213 +-column_ref= +-ColumnRef(type=ENUM, column=$orderby.$orderbycol1#10) == @@ -2613,9 +2619,11 @@ QueryStmt | | +-Literal(type=DOUBLE, value=14) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=608-617 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$groupby.field#10) | +-OrderByItem + | +-parse_location=619-628 | +-column_ref= | +-ColumnRef(type=DOUBLE, column=$query.value#11) +-limit= @@ -2936,6 +2944,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=50-69 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#5) == @@ -3232,6 +3241,7 @@ QueryStmt | | +-ColumnRef(type=INT64, column=KeyValue.Key#9) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=116-122 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#13) +-input_scan= @@ -3550,6 +3560,7 @@ QueryStmt | | +-ColumnRef(type=INT64, column=KeyValue.Key#3) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=140-146 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#7) +-group_by_list= @@ -3920,6 +3931,7 @@ QueryStmt | | +-ColumnRef(type=INT64, column=KeyValue.Key#4) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=157-163 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) +-aggregate_list= @@ -4260,6 +4272,7 @@ QueryStmt | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=132-138 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#9) +-Literal(type=INT64, value=0) @@ -4319,6 +4332,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=57-124 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#9) == @@ -4371,6 +4385,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=57-119 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) == @@ -4435,6 +4450,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=57-150 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#9) == @@ -4492,6 +4508,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=57-129 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#10) == @@ -4556,6 +4573,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=57-149 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#9) == @@ -4607,6 +4625,7 @@ QueryStmt | | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=140-146 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#9) | +-input_scan= @@ -4622,6 +4641,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=57-147 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#10) == @@ -4834,6 +4854,58 @@ from KitchenSinkValueTable t0 group by 1 order by (select {{t0.|}}has_int32_val) -- +ALTERNATION GROUP: t0. +-- +QueryStmt ++-output_column_list= +| +-$groupby.has_int32_val#3 AS has_int32_val [BOOL] ++-query= + +-OrderByScan + +-column_list=[$groupby.has_int32_val#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.has_int32_val#3, $orderby.$orderbycol1#5] + | +-expr_list= + | | +-$orderbycol1#5 := + | | +-SubqueryExpr + | | +-type=BOOL + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=BOOL, column=$groupby.has_int32_val#3) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.has_int32_val#4] + | | +-expr_list= + | | | +-has_int32_val#4 := ColumnRef(type=BOOL, column=$groupby.has_int32_val#3, is_correlated=TRUE) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.has_int32_val#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[KitchenSinkValueTable.value#1, $pre_groupby.has_int32_val#2] + | | +-expr_list= + | | | +-has_int32_val#2 := + | | | +-GetProtoField + | | | +-type=BOOL + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | | +-field_descriptor=int32_val + | | | +-get_has_bit=TRUE + | | +-input_scan= + | | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0], alias="t0") + | +-group_by_list= + | +-has_int32_val#3 := ColumnRef(type=BOOL, column=$pre_groupby.has_int32_val#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=74-99 + +-column_ref= + +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#5) +-- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$groupby.has_int32_val#3 AS has_int32_val [BOOL] @@ -4878,6 +4950,7 @@ QueryStmt | +-has_int32_val#3 := ColumnRef(type=BOOL, column=$pre_groupby.has_int32_val#2) +-order_by_item_list= +-OrderByItem + +-parse_location=74-96 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#5) == @@ -4888,6 +4961,58 @@ select distinct t0.has_int32_val from KitchenSinkValueTable t0 order by (select {{t0.|}}has_int32_val) -- +ALTERNATION GROUP: t0. +-- +QueryStmt ++-output_column_list= +| +-$distinct.has_int32_val#3 AS has_int32_val [BOOL] ++-query= + +-OrderByScan + +-column_list=[$distinct.has_int32_val#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$distinct.has_int32_val#3, $orderby.$orderbycol1#5] + | +-expr_list= + | | +-$orderbycol1#5 := + | | +-SubqueryExpr + | | +-type=BOOL + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=BOOL, column=$distinct.has_int32_val#3) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.has_int32_val#4] + | | +-expr_list= + | | | +-has_int32_val#4 := ColumnRef(type=BOOL, column=$distinct.has_int32_val#3, is_correlated=TRUE) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.has_int32_val#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[KitchenSinkValueTable.value#1, $query.has_int32_val#2] + | | +-expr_list= + | | | +-has_int32_val#2 := + | | | +-GetProtoField + | | | +-type=BOOL + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | | +-field_descriptor=int32_val + | | | +-get_has_bit=TRUE + | | +-input_scan= + | | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0], alias="t0") + | +-group_by_list= + | +-has_int32_val#3 := ColumnRef(type=BOOL, column=$query.has_int32_val#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=72-97 + +-column_ref= + +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#5) +-- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$distinct.has_int32_val#3 AS has_int32_val [BOOL] @@ -4932,6 +5057,7 @@ QueryStmt | +-has_int32_val#3 := ColumnRef(type=BOOL, column=$query.has_int32_val#2) +-order_by_item_list= +-OrderByItem + +-parse_location=72-94 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#5) == diff --git a/zetasql/analyzer/testdata/correlated_subquery_outer_aggr_struct.test b/zetasql/analyzer/testdata/correlated_subquery_outer_aggr_struct.test index ed30ed6db..67ca269c8 100644 --- a/zetasql/analyzer/testdata/correlated_subquery_outer_aggr_struct.test +++ b/zetasql/analyzer/testdata/correlated_subquery_outer_aggr_struct.test @@ -945,6 +945,8 @@ FROM ComplexTypes ct GROUP BY TestStruct.d ORDER BY (SELECT {{ct.|}}TestStruct.d.a); -- +ALTERNATION GROUP: ct. +-- QueryStmt +-output_column_list= | +-$groupby.d#8 AS d [STRUCT] @@ -993,6 +995,61 @@ QueryStmt | +-d#8 := ColumnRef(type=STRUCT, column=$pre_groupby.d#7) +-order_by_item_list= +-OrderByItem + +-parse_location=72-98 + +-column_ref= + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#10) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.d#8 AS d [STRUCT] ++-query= + +-OrderByScan + +-column_list=[$groupby.d#8] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.d#8, $orderby.$orderbycol1#10] + | +-expr_list= + | | +-$orderbycol1#10 := + | | +-SubqueryExpr + | | +-type=INT32 + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=STRUCT, column=$groupby.d#8) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.a#9] + | | +-expr_list= + | | | +-a#9 := + | | | +-GetStructField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT, column=$groupby.d#8, is_correlated=TRUE) + | | | +-field_idx=0 + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.d#8] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[ComplexTypes.TestStruct#5, $pre_groupby.d#7] + | | +-expr_list= + | | | +-d#7 := + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | | | +-field_idx=1 + | | +-input_scan= + | | +-TableScan(column_list=[ComplexTypes.TestStruct#5], table=ComplexTypes, column_index_list=[4], alias="ct") + | +-group_by_list= + | +-d#8 := ColumnRef(type=STRUCT, column=$pre_groupby.d#7) + +-order_by_item_list= + +-OrderByItem + +-parse_location=72-95 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#10) == @@ -1044,6 +1101,7 @@ QueryStmt | +-TestStruct#7 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=74-97 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#9) == @@ -1760,6 +1818,7 @@ QueryStmt | +-TestStruct#7 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=65-112 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#10) == @@ -2266,6 +2325,7 @@ QueryStmt | | | +-field_idx=1 | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=164-205 | | +-column_ref= | | +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#18) | +-input_scan= @@ -2277,6 +2337,7 @@ QueryStmt | +-TestStruct#7 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=68-206 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#19) == @@ -2370,6 +2431,7 @@ QueryStmt | | | +-field_idx=1 | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=155-181 | | +-column_ref= | | +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#20) | +-input_scan= @@ -2401,6 +2463,7 @@ QueryStmt | +-TestStruct#8 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=70-182 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#21) == @@ -2814,6 +2877,7 @@ QueryStmt | +-TestStruct#8 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=69-108 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#11) == @@ -3149,6 +3213,7 @@ QueryStmt | +-TestStruct#8 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=72-119 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#17) == @@ -3418,6 +3483,8 @@ FROM TestStructValueTable GROUP BY TestStructValueTable ORDER BY (SELECT TestStructValueTable.a) -- +ALTERNATION GROUP: TestStructValueTable. +-- QueryStmt +-output_column_list= | +-$groupby.TestStructValueTable#3 AS TestStructValueTable [STRUCT] @@ -3477,6 +3544,72 @@ QueryStmt | +-TestStructValueTable#3 := ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) +-order_by_item_list= +-OrderByItem + +-parse_location=117-148 + +-column_ref= + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#6) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.TestStructValueTable#3 AS TestStructValueTable [STRUCT] +| +-$query.a#4 AS a [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.TestStructValueTable#3, $query.a#4] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.TestStructValueTable#3, $query.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-SubqueryExpr + | | +-type=INT32 + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=STRUCT, column=$groupby.TestStructValueTable#3) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.a#5] + | | +-expr_list= + | | | +-a#5 := + | | | +-GetStructField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT, column=$groupby.TestStructValueTable#3, is_correlated=TRUE) + | | | +-field_idx=0 + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.TestStructValueTable#3, $query.a#4] + | +-expr_list= + | | +-a#4 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$groupby.TestStructValueTable#3) + | | +-field_idx=0 + | +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.TestStructValueTable#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestStructValueTable.value#1, $pre_groupby.a#2] + | | +-expr_list= + | | | +-a#2 := + | | | +-GetStructField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + | | | +-field_idx=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestStructValueTable.value#1], table=TestStructValueTable, column_index_list=[0]) + | +-group_by_list= + | +-TestStructValueTable#3 := ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=96-127 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#6) == @@ -3487,6 +3620,73 @@ FROM TestStructValueTable vt GROUP BY vt ORDER BY (SELECT vt.a) -- +ALTERNATION GROUP: vt. +-- +QueryStmt ++-output_column_list= +| +-$groupby.vt#3 AS vt [STRUCT] +| +-$query.a#4 AS a [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.vt#3, $query.a#4] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.vt#3, $query.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-SubqueryExpr + | | +-type=INT32 + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=STRUCT, column=$groupby.vt#3) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.a#5] + | | +-expr_list= + | | | +-a#5 := + | | | +-GetStructField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT, column=$groupby.vt#3, is_correlated=TRUE) + | | | +-field_idx=0 + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.vt#3, $query.a#4] + | +-expr_list= + | | +-a#4 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$groupby.vt#3) + | | +-field_idx=0 + | +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.vt#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestStructValueTable.value#1, $pre_groupby.a#2] + | | +-expr_list= + | | | +-a#2 := + | | | +-GetStructField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + | | | +-field_idx=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestStructValueTable.value#1], table=TestStructValueTable, column_index_list=[0], alias="vt") + | +-group_by_list= + | +-vt#3 := ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-79 + +-column_ref= + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#6) +-- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$groupby.vt#3 AS vt [STRUCT] @@ -3546,6 +3746,7 @@ QueryStmt | +-vt#3 := ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) +-order_by_item_list= +-OrderByItem + +-parse_location=63-76 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#6) == diff --git a/zetasql/analyzer/testdata/create_connection.test b/zetasql/analyzer/testdata/create_connection.test new file mode 100644 index 000000000..c996a9493 --- /dev/null +++ b/zetasql/analyzer/testdata/create_connection.test @@ -0,0 +1,103 @@ +CREATE CONNECTION foo +-- +CreateConnectionStmt(name_path=foo) +== + +CREATE CONNECTION foo OPTIONS() +-- +CreateConnectionStmt(name_path=foo) +== + +[disallow_duplicate_options] +CREATE CONNECTION foo OPTIONS(x=1, x=5) +-- +ERROR: Duplicate option specified for 'x' [at 1:36] +CREATE CONNECTION foo OPTIONS(x=1, x=5) + ^ +== + +CREATE TEMP CONNECTION foo OPTIONS(x=1) +-- +ERROR: Syntax error: Expected keyword FUNCTION but got keyword CONNECTION [at 1:13] +CREATE TEMP CONNECTION foo OPTIONS(x=1) + ^ +== + +CREATE {{|OR REPLACE }} CONNECTION {{|IF NOT EXISTS}} foo.bar.baz OPTIONS (a=1, b=2) +-- +ALTERNATION GROUP: +-- +CreateConnectionStmt ++-name_path=foo.bar.baz ++-option_list= + +-a := Literal(type=INT64, value=1) + +-b := Literal(type=INT64, value=2) +-- +ALTERNATION GROUP: IF NOT EXISTS +-- +CreateConnectionStmt ++-name_path=foo.bar.baz ++-create_mode=CREATE_IF_NOT_EXISTS ++-option_list= + +-a := Literal(type=INT64, value=1) + +-b := Literal(type=INT64, value=2) +-- +ALTERNATION GROUP: OR REPLACE , +-- +CreateConnectionStmt ++-name_path=foo.bar.baz ++-create_mode=CREATE_OR_REPLACE ++-option_list= + +-a := Literal(type=INT64, value=1) + +-b := Literal(type=INT64, value=2) +-- +ALTERNATION GROUP: OR REPLACE ,IF NOT EXISTS +-- +ERROR: CREATE CONNECTION cannot have both OR REPLACE and IF NOT EXISTS [at 1:1] +CREATE OR REPLACE CONNECTION IF NOT EXISTS foo.bar.baz OPTIONS (a=1, b=2) +^ +== + +CREATE TEMP CONNECTION foo OPTIONS(x=1) +-- +ERROR: Syntax error: Expected keyword FUNCTION but got keyword CONNECTION [at 1:13] +CREATE TEMP CONNECTION foo OPTIONS(x=1) + ^ +== + +[show_unparsed] +CREATE CONNECTION foo OPTIONS(x=1) +-- +CreateConnectionStmt ++-name_path=foo ++-option_list= + +-x := Literal(type=INT64, value=1) + +[UNPARSED_SQL] +CREATE CONNECTION foo OPTIONS(x = 1); +== + +[show_unparsed] +CREATE OR REPLACE CONNECTION foo OPTIONS(x=1) +-- +CreateConnectionStmt ++-name_path=foo ++-create_mode=CREATE_OR_REPLACE ++-option_list= + +-x := Literal(type=INT64, value=1) + +[UNPARSED_SQL] +CREATE OR REPLACE CONNECTION foo OPTIONS(x = 1); +== + +[show_unparsed] +CREATE CONNECTION IF NOT EXISTS foo OPTIONS(x=1) +-- +CreateConnectionStmt ++-name_path=foo ++-create_mode=CREATE_IF_NOT_EXISTS ++-option_list= + +-x := Literal(type=INT64, value=1) + +[UNPARSED_SQL] +CREATE CONNECTION IF NOT EXISTS foo OPTIONS(x = 1); diff --git a/zetasql/analyzer/testdata/create_external_table.test b/zetasql/analyzer/testdata/create_external_table.test index 943aaa286..75fd732a0 100644 --- a/zetasql/analyzer/testdata/create_external_table.test +++ b/zetasql/analyzer/testdata/create_external_table.test @@ -506,7 +506,24 @@ with connection default options (x=foo) -- -ERROR: CONNECTION DEFAULT is not supported [at 2:17] +CreateExternalTableStmt ++-name_path=projectid.datasetid.tablename ++-option_list= +| +-x := Literal(type=STRING, value="foo") ++-column_definition_list= +| +-ColumnDefinition(name="x", type=INT64, column=projectid.datasetid.tablename.x#1) ++-connection= + +-Connection(connection=$connection_default) +== + +[no_java] +[use_database=SpecialCatalog] +[language_features=CREATE_EXTERNAL_TABLE_WITH_CONNECTION,CREATE_EXTERNAL_TABLE_WITH_TABLE_ELEMENT_LIST,CREATE_EXTERNAL_TABLE_WITH_PARTITION_COLUMNS] +create external table projectid.datasetid.tablename(x int64) +with connection default +options (x=foo) +-- +ERROR: Default connection not found [at 2:17] with connection default ^ == diff --git a/zetasql/analyzer/testdata/create_index.test b/zetasql/analyzer/testdata/create_index.test index f4a06f10b..b85ea2d99 100644 --- a/zetasql/analyzer/testdata/create_index.test +++ b/zetasql/analyzer/testdata/create_index.test @@ -1386,3 +1386,56 @@ CREATE index i1 on TestTable UNNEST(KitchenSink.repeated_int32_val, mode => "PAD ERROR: UNNEST expression used with CREATE INDEX does not allow named arguments [at 1:69] ...on TestTable UNNEST(KitchenSink.repeated_int32_val, mode => "PAD") (repeat... ^ +== + +# The language feature FEATURE_CREATE_INDEX_PARTITION_BY is not supported. +CREATE VECTOR INDEX index0 ON KeyValue(key) PARTITION BY value options() +-- +ERROR: CREATE INDEX with PARTITION BY is not supported. [at 1:45] +CREATE VECTOR INDEX index0 ON KeyValue(key) PARTITION BY value options() + ^ +== + +[language_features=CREATE_INDEX_PARTITION_BY] +CREATE INDEX index0 ON KeyValue(key) PARTITION BY value options() +-- +ERROR: PARTITION BY is not supported for CREATE INDEX. [at 1:38] +CREATE INDEX index0 ON KeyValue(key) PARTITION BY value options() + ^ + +== + +[language_features=CREATE_INDEX_PARTITION_BY] +CREATE SEARCH INDEX index0 ON KeyValue(key) PARTITION BY value options() +-- +ERROR: PARTITION BY is not supported for CREATE SEARCH INDEX. [at 1:45] +CREATE SEARCH INDEX index0 ON KeyValue(key) PARTITION BY value options() + ^ + +== + +[language_features=CREATE_INDEX_PARTITION_BY] +CREATE VECTOR INDEX index0 ON KeyValue(key) PARTITION BY value options() +-- +CreateIndexStmt ++-name_path=index0 ++-table_name_path=KeyValue ++-table_scan= +| +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) ++-is_unique=FALSE ++-is_vector=TRUE ++-index_item_list= +| +-IndexItem +| +-column_ref= +| | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +| +-descending=FALSE ++-partition_by_list= + +-ColumnRef(type=STRING, column=KeyValue.Value#2) +== + +[language_features=CREATE_INDEX_PARTITION_BY] +CREATE VECTOR INDEX index0 ON KeyValue(key) PARTITION BY col_not_exist options() +-- +ERROR: Unrecognized name: col_not_exist [at 1:58] +CREATE VECTOR INDEX index0 ON KeyValue(key) PARTITION BY col_not_exist options() + ^ diff --git a/zetasql/analyzer/testdata/create_model.test b/zetasql/analyzer/testdata/create_model.test index 47cf25b54..6e525c834 100644 --- a/zetasql/analyzer/testdata/create_model.test +++ b/zetasql/analyzer/testdata/create_model.test @@ -967,12 +967,23 @@ remote with connection `` create model t1 remote with connection default -- -ERROR: CONNECTION DEFAULT is not supported [at 2:24] +CreateModelStmt ++-name_path=t1 ++-is_remote=TRUE ++-connection= + +-Connection(connection=$connection_default) +== + +[no_java] +[use_database=SpecialCatalog] +create model t1 +remote with connection default +-- +ERROR: Default connection not found [at 2:24] remote with connection default ^ == - # All remote model clauses create model t1 input (i1 INT64, i2 FLOAT64) diff --git a/zetasql/analyzer/testdata/create_row_access_policy.test b/zetasql/analyzer/testdata/create_row_access_policy.test index 30fb2e129..e45050911 100644 --- a/zetasql/analyzer/testdata/create_row_access_policy.test +++ b/zetasql/analyzer/testdata/create_row_access_policy.test @@ -478,9 +478,9 @@ CreateRowAccessPolicyStmt -- ALTERNATION GROUP: access,grant to ("foo@google.com"), -- -ERROR: Expected keyword FILTER before USING [at 1:68] -create row access policy p1 on KeyValue grant to ("foo@google.com") - ^ +ERROR: Expected keyword FILTER before USING [at 2:2] + using (key = 321); + ^ -- ALTERNATION GROUPS: access,to "foo@google.com",filter diff --git a/zetasql/analyzer/testdata/create_table.test b/zetasql/analyzer/testdata/create_table.test index 8cc0e033d..12e08dfd0 100644 --- a/zetasql/analyzer/testdata/create_table.test +++ b/zetasql/analyzer/testdata/create_table.test @@ -5552,7 +5552,21 @@ create table t (a int64) with connection ``; [language_features=CREATE_TABLE_WITH_CONNECTION] create table t (a int64) with connection default; -- -ERROR: CONNECTION DEFAULT is not supported [at 1:42] +CreateTableStmt ++-name_path=t ++-column_definition_list= +| +-ColumnDefinition(name="a", type=INT64, column=t.a#1) ++-connection= + +-Connection(connection=$connection_default) + +== + +[no_java] +[use_database=SpecialCatalog] +[language_features=CREATE_TABLE_WITH_CONNECTION] +create table t (a int64) with connection default; +-- +ERROR: Default connection not found [at 1:42] create table t (a int64) with connection default; ^ diff --git a/zetasql/analyzer/testdata/create_table_as_select.test b/zetasql/analyzer/testdata/create_table_as_select.test index 761f4549d..e72e6fab7 100644 --- a/zetasql/analyzer/testdata/create_table_as_select.test +++ b/zetasql/analyzer/testdata/create_table_as_select.test @@ -1462,7 +1462,35 @@ create table tt with connection default as select 1 a, 2 b; -- -ERROR: CONNECTION DEFAULT is not supported [at 2:17] +CreateTableAsSelectStmt ++-name_path=tt ++-column_definition_list= +| +-ColumnDefinition(name="a", type=INT64, column=tt.a#3) +| +-ColumnDefinition(name="b", type=INT64, column=tt.b#4) ++-connection= +| +-Connection(connection=$connection_default) ++-output_column_list= +| +-$create_as.a#1 AS a [INT64] +| +-$create_as.b#2 AS b [INT64] ++-query= + +-ProjectScan + +-column_list=$create_as.[a#1, b#2] + +-expr_list= + | +-a#1 := Literal(type=INT64, value=1) + | +-b#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + + +[no_java] +[use_database=SpecialCatalog] +[language_features=CREATE_TABLE_WITH_CONNECTION] +create table tt +with connection default +as select 1 a, 2 b; +-- +ERROR: Default connection not found [at 2:17] with connection default ^ == diff --git a/zetasql/analyzer/testdata/differential_privacy.test b/zetasql/analyzer/testdata/differential_privacy.test index f6627b706..e37f2c9a1 100644 --- a/zetasql/analyzer/testdata/differential_privacy.test +++ b/zetasql/analyzer/testdata/differential_privacy.test @@ -1962,6 +1962,7 @@ QueryStmt | | | +-Literal(type=INT64, value=0) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=283-290 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$aggregate.int64#13) | +-limit= @@ -2032,6 +2033,7 @@ QueryStmt | | | | +-Literal(type=INT64, value=0) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=283-290 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$aggregate.int64#13) | | +-limit= diff --git a/zetasql/analyzer/testdata/differential_privacy_subquery.test b/zetasql/analyzer/testdata/differential_privacy_subquery.test index 13d697e52..6d07e96eb 100644 --- a/zetasql/analyzer/testdata/differential_privacy_subquery.test +++ b/zetasql/analyzer/testdata/differential_privacy_subquery.test @@ -514,6 +514,7 @@ QueryStmt | +-max_groups_contributed := Literal(type=INT64, value=100) +-order_by_item_list= +-OrderByItem + +-parse_location=349-360 +-column_ref= | +-ColumnRef(type=INT64, column=$aggregate.weight#6) +-is_descending=TRUE @@ -604,6 +605,7 @@ QueryStmt | +-max_groups_contributed := Literal(type=INT64, value=100) +-order_by_item_list= +-OrderByItem + +-parse_location=349-360 +-column_ref= | +-ColumnRef(type=INT64, column=$aggregate.weight#6) +-is_descending=TRUE diff --git a/zetasql/analyzer/testdata/differential_privacy_with.test b/zetasql/analyzer/testdata/differential_privacy_with.test index 49803c6eb..f8fd462cf 100644 --- a/zetasql/analyzer/testdata/differential_privacy_with.test +++ b/zetasql/analyzer/testdata/differential_privacy_with.test @@ -2407,6 +2407,7 @@ QueryStmt | | +-TableScan(parse_location=36-67, column_list=[SimpleTypesWithAnonymizationUid.int64#2], table=SimpleTypesWithAnonymizationUid, column_index_list=[1]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=81-87 | +-column_ref= | +-ColumnRef(type=DOUBLE, column=$orderby.$orderbycol1#13) +-query= diff --git a/zetasql/analyzer/testdata/dml_insert.test b/zetasql/analyzer/testdata/dml_insert.test index 784dbbf4c..90fbbe52b 100644 --- a/zetasql/analyzer/testdata/dml_insert.test +++ b/zetasql/analyzer/testdata/dml_insert.test @@ -683,6 +683,7 @@ InsertStmt | | | +-output_column_list=KeyValue.[Key#5, Value#6] | | +-order_by_item_list= | | +-OrderByItem +| | +-parse_location=98-99 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$union_all.Key#7) | +-limit= diff --git a/zetasql/analyzer/testdata/drop.test b/zetasql/analyzer/testdata/drop.test index f1419778d..faceced08 100644 --- a/zetasql/analyzer/testdata/drop.test +++ b/zetasql/analyzer/testdata/drop.test @@ -18,6 +18,21 @@ DROP EXTERNAL TABLE namespace.foo; DropStmt(object_type="EXTERNAL TABLE", is_if_exists=FALSE, name_path=namespace.foo) == +DROP CONNECTION foo; +-- +DropStmt(object_type="CONNECTION", is_if_exists=FALSE, name_path=foo) +== + +DROP CONNECTION foo.bar.baz; +-- +DropStmt(object_type="CONNECTION", is_if_exists=FALSE, name_path=foo.bar.baz) +== + +DROP CONNECTION IF EXISTS foo.bar.baz; +-- +DropStmt(object_type="CONNECTION", is_if_exists=TRUE, name_path=foo.bar.baz) +== + DROP VIEW bar; -- diff --git a/zetasql/analyzer/testdata/expr_subquery.test b/zetasql/analyzer/testdata/expr_subquery.test index 1ddf0541c..4ed4cfc64 100644 --- a/zetasql/analyzer/testdata/expr_subquery.test +++ b/zetasql/analyzer/testdata/expr_subquery.test @@ -1143,9 +1143,11 @@ QueryStmt | | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2]) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=51-58 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-OrderByItem + | +-parse_location=60-81 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol2#5) +-input_scan= @@ -1213,6 +1215,7 @@ QueryStmt | | +-output_column_list=[$union_all2_cast.$col1#6] | +-order_by_item_list= | +-OrderByItem + | +-parse_location=104-125 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#7) +-input_scan= @@ -1260,9 +1263,11 @@ QueryStmt | | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2]) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=84-91 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-OrderByItem + | +-parse_location=93-114 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol2#5) +-input_scan= @@ -1326,6 +1331,7 @@ QueryStmt | | +-output_column_list=[$union_all2.$col1#6] | +-order_by_item_list= | +-OrderByItem + | +-parse_location=80-103 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#8) +-input_scan= @@ -1401,6 +1407,7 @@ QueryStmt | | +-output_column_list=[$union_all2.$col1#7, $union_all2_cast.$col2#11] | +-order_by_item_list= | +-OrderByItem + | +-parse_location=130-153 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#12) +-input_scan= diff --git a/zetasql/analyzer/testdata/hints.test b/zetasql/analyzer/testdata/hints.test index 1ea6e0f6a..e17409bee 100644 --- a/zetasql/analyzer/testdata/hints.test +++ b/zetasql/analyzer/testdata/hints.test @@ -966,6 +966,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=43-44 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -986,6 +987,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=39-40 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -1007,6 +1009,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=46-47 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -1055,6 +1058,7 @@ QueryStmt | +-output_column_list=[KeyValue.Key#3] +-order_by_item_list= +-OrderByItem + +-parse_location=78-79 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.key#5) == diff --git a/zetasql/analyzer/testdata/json.test b/zetasql/analyzer/testdata/json.test index 68a7ef8d5..d377ecb08 100644 --- a/zetasql/analyzer/testdata/json.test +++ b/zetasql/analyzer/testdata/json.test @@ -315,7 +315,7 @@ SELECT (JSON '[{"a": "foo"}, {"a": "bar"}]')[SAFE_ORDINAL(1)]["a"] ^ == -[language_features=JSON_TYPE,NAMED_ARGUMENTS] +[language_features=JSON_TYPE,NAMED_ARGUMENTS,TO_JSON_UNSUPPORTED_FIELDS] SELECT TO_JSON(key), TO_JSON(TestEnum, stringify_wide_numbers=>true), TO_JSON(KitchenSink, stringify_wide_numbers=>false) FROM TestTable; -- QueryStmt @@ -328,22 +328,25 @@ QueryStmt +-column_list=$query.[$col1#4, $col2#5, $col3#6] +-expr_list= | +-$col1#4 := - | | +-FunctionCall(ZetaSQL:to_json(INT32, optional(1) BOOL stringify_wide_numbers) -> JSON) + | | +-FunctionCall(ZetaSQL:to_json(INT32, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) | | +-ColumnRef(type=INT32, column=TestTable.key#1) | | +-Literal(type=BOOL, value=false) + | | +-Literal(type=ENUM, value=FAIL) | +-$col2#5 := - | | +-FunctionCall(ZetaSQL:to_json(ENUM, optional(1) BOOL stringify_wide_numbers) -> JSON) + | | +-FunctionCall(ZetaSQL:to_json(ENUM, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) | | +-ColumnRef(type=ENUM, column=TestTable.TestEnum#2) | | +-Literal(type=BOOL, value=true) + | | +-Literal(type=ENUM, value=FAIL) | +-$col3#6 := - | +-FunctionCall(ZetaSQL:to_json(PROTO, optional(1) BOOL stringify_wide_numbers) -> JSON) + | +-FunctionCall(ZetaSQL:to_json(PROTO, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) | +-Literal(type=BOOL, value=false) + | +-Literal(type=ENUM, value=FAIL) +-input_scan= +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) == -[language_features=JSON_TYPE,NAMED_ARGUMENTS] +[language_features=JSON_TYPE,NAMED_ARGUMENTS,TO_JSON_UNSUPPORTED_FIELDS] SELECT TO_JSON(1, false); -- ERROR: Positional argument is invalid because this function restricts that this argument is referred to by name "stringify_wide_numbers" only [at 1:19] @@ -577,3 +580,178 @@ FROM JsonTable; ERROR: Argument 'mode' to JSON_KEYS must be a constant expression [at 1:30] SELECT JSON_KEYS(JSON '999', mode=>TO_JSON_STRING(json_col)) ^ +== + +[language_features=JSON_TYPE,NAMED_ARGUMENTS,RANGE_TYPE,TO_JSON_UNSUPPORTED_FIELDS] +[enabled_ast_rewrites=DEFAULTS] +[run_unparser] +[show_unparsed] + +# TO_JSON with unsupported_fields defauls to 'FAIL' +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL)) +-- + +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [JSON] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:to_json(RANGE, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) + | +-FunctionCall(ZetaSQL:range(DATE, DATE) -> RANGE) + | | +-Literal(type=DATE, value=2022-10-01, has_explicit_type=TRUE) + | | +-Literal(type=DATE, value=NULL) + | +-Literal(type=BOOL, value=false) + | +-Literal(type=ENUM, value=FAIL) + +-input_scan= + +-SingleRowScan + +[UNPARSED_SQL] +SELECT + TO_JSON(`RANGE`(DATE "2022-10-01", CAST(NULL AS DATE)), stringify_wide_numbers => false, unsupported_fields => "FAIL") AS a_1; +== + +[language_features=JSON_TYPE,NAMED_ARGUMENTS,RANGE_TYPE,TO_JSON_UNSUPPORTED_FIELDS] +[enabled_ast_rewrites=DEFAULTS] +[run_unparser] +[show_unparsed] + +# TO_JSON with unsupported_fields set to 'IGNORE' or 'PLACEHOLDER' +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>{{'IGNORE'|'PLACEHOLDER'}}) +-- + +ALTERNATION GROUP: 'IGNORE' +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [JSON] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:to_json(RANGE, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) + | +-FunctionCall(ZetaSQL:range(DATE, DATE) -> RANGE) + | | +-Literal(type=DATE, value=2022-10-01, has_explicit_type=TRUE) + | | +-Literal(type=DATE, value=NULL) + | +-Literal(type=BOOL, value=false) + | +-Literal(type=ENUM, value=IGNORE) + +-input_scan= + +-SingleRowScan + +[UNPARSED_SQL] +SELECT + TO_JSON(`RANGE`(DATE "2022-10-01", CAST(NULL AS DATE)), stringify_wide_numbers => false, unsupported_fields => "IGNORE") AS a_1; +-- +ALTERNATION GROUP: 'PLACEHOLDER' +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [JSON] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:to_json(RANGE, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) + | +-FunctionCall(ZetaSQL:range(DATE, DATE) -> RANGE) + | | +-Literal(type=DATE, value=2022-10-01, has_explicit_type=TRUE) + | | +-Literal(type=DATE, value=NULL) + | +-Literal(type=BOOL, value=false) + | +-Literal(type=ENUM, value=PLACEHOLDER) + +-input_scan= + +-SingleRowScan + +[UNPARSED_SQL] +SELECT + TO_JSON(`RANGE`(DATE "2022-10-01", CAST(NULL AS DATE)), stringify_wide_numbers => false, unsupported_fields => "PLACEHOLDER") AS a_1; +== + +[language_features=JSON_TYPE,NAMED_ARGUMENTS,RANGE_TYPE,TO_JSON_UNSUPPORTED_FIELDS] +[enabled_ast_rewrites=DEFAULTS] +[no_run_unparser] + +# TO_JSON with unsupported_fields set to invalid value 'blah' +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>'blah') +-- + +ERROR: Could not cast literal "blah" to type zetasql.functions.UnsupportedFields [at 2:48] +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>'blah') + ^ +== + +# Case sensitive: setting unsupported_fields to `iGnore` triggers hint +[language_features=JSON_TYPE,NAMED_ARGUMENTS,RANGE_TYPE,TO_JSON_UNSUPPORTED_FIELDS] +[enabled_ast_rewrites=DEFAULTS] +[run_unparser] +[show_unparsed] + +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>'iGnore') +-- + +ERROR: Could not cast literal "iGnore" to type zetasql.functions.UnsupportedFields; Did you mean 'IGNORE'? (Note: ENUM values are case sensitive) [at 1:48] +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>'iGnore') + ^ +== + +# Case sensitive: setting unsupported_fields to `iGnore` triggers hint +[language_features=JSON_TYPE,NAMED_ARGUMENTS,RANGE_TYPE,TO_JSON_UNSUPPORTED_FIELDS] +[enabled_ast_rewrites=DEFAULTS] +[run_unparser] +[show_unparsed] + +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>'iGnore') +-- + +ERROR: Could not cast literal "iGnore" to type zetasql.functions.UnsupportedFields; Did you mean 'IGNORE'? (Note: ENUM values are case sensitive) [at 1:48] +SELECT TO_JSON(RANGE(DATE '2022-10-01', NULL), unsupported_fields=>'iGnore') + ^ +== + +# Setting unsupported_fields to a named parameter +[language_features=JSON_TYPE,NAMED_ARGUMENTS,TO_JSON_UNSUPPORTED_FIELDS] +[allow_undeclared_parameters] +SELECT TO_JSON(1, unsupported_fields=>@p); +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [JSON] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:to_json(INT64, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) + | +-Literal(type=INT64, value=1) + | +-Literal(type=BOOL, value=false) + | +-Parameter(parse_location=38-40, type=ENUM, name="p") + +-input_scan= + +-SingleRowScan +[UNDECLARED_PARAMETERS] +p: ENUM +== + +# Setting unsupported_fields to a positional parameter +[language_features=JSON_TYPE,NAMED_ARGUMENTS,TO_JSON_UNSUPPORTED_FIELDS] +[allow_undeclared_parameters] +[parameter_mode=positional] +SELECT TO_JSON(1, unsupported_fields=>?); +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [JSON] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:to_json(INT64, optional(1) BOOL stringify_wide_numbers, optional(1) ENUM unsupported_fields) -> JSON) + | +-Literal(type=INT64, value=1) + | +-Literal(type=BOOL, value=false) + | +-Parameter(parse_location=38-39, type=ENUM, position=1) + +-input_scan= + +-SingleRowScan +[UNDECLARED_PARAMETERS] +ENUM diff --git a/zetasql/analyzer/testdata/lambda.test b/zetasql/analyzer/testdata/lambda.test index 88cadb1df..5294e4a04 100644 --- a/zetasql/analyzer/testdata/lambda.test +++ b/zetasql/analyzer/testdata/lambda.test @@ -1338,6 +1338,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=221-224 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | +-input_scan= @@ -1436,6 +1437,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=221-224 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | +-input_scan= @@ -1535,6 +1537,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=221-224 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | +-input_scan= @@ -1617,3 +1620,81 @@ ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_ARRAY_FILTER Argument 2: lambda requires 2 arguments but 1 is provided [at 1:8] SELECT fn_array_filter([1], e -> LENGTH(fn_array_filter([e], e->e > "")) > 0); ^ +== + +[language_features=V_1_4_MAP_TYPE,V_1_3_INLINE_LAMBDA_ARGUMENT] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + MAP_FROM_ARRAY([('a', 1)]), e -> 1); +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#2 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#2] + +-expr_list= + | +-$col1#2 := + | +-FunctionCall(sample_functions:fn_map_type_any_1_2_lambda_any_1_any_2_return_bool(MAP, FUNCTIONINT64>) -> BOOL) + | +-FunctionArgument + | | +-expr= + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | +-FunctionArgument + | +-inline_lambda= + | +-InlineLambda + | +-argument_list=[$lambda_arg.e#1] + | +-body= + | +-Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +[language_features=V_1_4_MAP_TYPE,V_1_3_INLINE_LAMBDA_ARGUMENT] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + MAP_FROM_ARRAY([(1, "a")]), e -> 1); +-- +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL for argument types: MAP, LAMBDA. Supported signature: FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL(MAP, FUNCTIONANY>) [at 1:8] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL + Argument types: MAP, LAMBDA + Signature: FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL(MAP, FUNCTIONT2>) + Argument 2: failed to resolve lambda body, error: Lambda should return type STRING, but returns INT64 [at 1:8] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + ^ +== + +[language_features=V_1_4_MAP_TYPE,V_1_3_INLINE_LAMBDA_ARGUMENT] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + MAP_FROM_ARRAY([('a', 1)]), e -> CONCAT(e, 'a')); +-- +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL for argument types: MAP, LAMBDA. Supported signature: FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL(MAP, FUNCTIONANY>) [at 1:8] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL + Argument types: MAP, LAMBDA + Signature: FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL(MAP, FUNCTIONT2>) + Argument 2: failed to resolve lambda body, error: Lambda should return type INT64, but returns STRING [at 1:8] +SELECT fn_map_type_any_1_2_lambda_any_1_any_2_return_bool( + ^ +== + +[language_features=V_1_4_MAP_TYPE,V_1_3_INLINE_LAMBDA_ARGUMENT] +SELECT + fn_map_type_any_1_2_lambda_any_1_any_2_return_bool(NULL, e -> CONCAT(e, 'a')); +-- +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL for argument types: NULL, LAMBDA. Supported signature: FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL(MAP, FUNCTIONANY>) [at 2:3] + fn_map_type_any_1_2_lambda_any_1_any_2_return_bool(NULL, e -> CONCAT(e, 'a')); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL + Argument types: NULL, LAMBDA + Signature: FN_MAP_TYPE_ANY_1_2_LAMBDA_ANY_1_ANY_2_RETURN_BOOL(MAP, FUNCTIONT2>) + Failed to infer type [at 2:3] + fn_map_type_any_1_2_lambda_any_1_any_2_return_bool(NULL, e -> CONCAT(e, 'a')); + ^ diff --git a/zetasql/analyzer/testdata/limit.test b/zetasql/analyzer/testdata/limit.test index eb300f596..9baa61fbc 100644 --- a/zetasql/analyzer/testdata/limit.test +++ b/zetasql/analyzer/testdata/limit.test @@ -777,6 +777,7 @@ QueryStmt | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=34-35 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= @@ -800,6 +801,7 @@ QueryStmt | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=34-35 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= diff --git a/zetasql/analyzer/testdata/literals.test b/zetasql/analyzer/testdata/literals.test index f5e8ea345..3d2e1476d 100644 --- a/zetasql/analyzer/testdata/literals.test +++ b/zetasql/analyzer/testdata/literals.test @@ -1223,6 +1223,7 @@ QueryStmt | +-$agg2#21 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) +-order_by_item_list= +-OrderByItem + +-parse_location=74-77 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#20) == diff --git a/zetasql/analyzer/testdata/map_functions.test b/zetasql/analyzer/testdata/map_functions.test index 6ff32fa70..86afe5142 100644 --- a/zetasql/analyzer/testdata/map_functions.test +++ b/zetasql/analyzer/testdata/map_functions.test @@ -1593,13 +1593,29 @@ SELECT MAP_{{VALUES_SORTED|VALUES_UNSORTED}}( -- ALTERNATION GROUP: VALUES_SORTED -- -ERROR: Array of array types are not supported [at 2:8] +ERROR: No matching signature for function MAP_VALUES_SORTED for argument types: MAP>. Supported signature: MAP_VALUES_SORTED(MAP) [at 2:8] +SELECT MAP_VALUES_SORTED( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_VALUES_SORTED + Argument types: MAP> + Signature: MAP_VALUES_SORTED(MAP) + > is inferred to be array of array, which is not supported [at 2:8] SELECT MAP_VALUES_SORTED( ^ -- ALTERNATION GROUP: VALUES_UNSORTED -- -ERROR: Array of array types are not supported [at 2:8] +ERROR: No matching signature for function MAP_VALUES_UNSORTED for argument types: MAP>. Supported signature: MAP_VALUES_UNSORTED(MAP) [at 2:8] +SELECT MAP_VALUES_UNSORTED( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_VALUES_UNSORTED + Argument types: MAP> + Signature: MAP_VALUES_UNSORTED(MAP) + > is inferred to be array of array, which is not supported [at 2:8] SELECT MAP_VALUES_UNSORTED( ^ == @@ -1610,13 +1626,29 @@ SELECT MAP_{{KEYS_SORTED|KEYS_UNSORTED}}(MAP_FROM_ARRAY([([1, 2, 3], 'value')])) -- ALTERNATION GROUP: KEYS_SORTED -- -ERROR: Array of array types are not supported [at 2:8] +ERROR: No matching signature for function MAP_KEYS_SORTED for argument types: MAP, STRING>. Supported signature: MAP_KEYS_SORTED(MAP) [at 2:8] +SELECT MAP_KEYS_SORTED(MAP_FROM_ARRAY([([1, 2, 3], 'value')])); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_KEYS_SORTED + Argument types: MAP, STRING> + Signature: MAP_KEYS_SORTED(MAP) + > is inferred to be array of array, which is not supported [at 2:8] SELECT MAP_KEYS_SORTED(MAP_FROM_ARRAY([([1, 2, 3], 'value')])); ^ -- ALTERNATION GROUP: KEYS_UNSORTED -- -ERROR: Array of array types are not supported [at 2:8] +ERROR: No matching signature for function MAP_KEYS_UNSORTED for argument types: MAP, STRING>. Supported signature: MAP_KEYS_UNSORTED(MAP) [at 2:8] +SELECT MAP_KEYS_UNSORTED(MAP_FROM_ARRAY([([1, 2, 3], 'value')])); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_KEYS_UNSORTED + Argument types: MAP, STRING> + Signature: MAP_KEYS_UNSORTED(MAP) + > is inferred to be array of array, which is not supported [at 2:8] SELECT MAP_KEYS_UNSORTED(MAP_FROM_ARRAY([([1, 2, 3], 'value')])); ^ == @@ -1644,7 +1676,7 @@ SELECT MAP_VALUES_SORTED_BY_KEY( -- ALTERNATION GROUP: STRUCT('a'), 1 -- -ERROR: MAP_VALUES_SORTED: MAP element type STRUCT is not orderable [at 1:8] +ERROR: MAP_VALUES_SORTED_BY_KEY: MAP element type STRUCT is not orderable [at 1:8] SELECT MAP_VALUES_SORTED_BY_KEY( ^ -- @@ -1664,3 +1696,640 @@ QueryStmt +-input_scan= +-SingleRowScan == + +SELECT MAP_EMPTY( + MAP_FROM_ARRAY({{[('a', 1)]|CAST([] AS ARRAY>)|CAST(NULL AS ARRAY>)}})); +-- +ALTERNATION GROUP: [('a', 1)] +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_empty(MAP input_map) -> BOOL) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | +-Literal(type=ARRAY>, value=[{"a", 1}]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: CAST([] AS ARRAY>) +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_empty(MAP input_map) -> BOOL) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | +-Literal(type=ARRAY>, value=[], has_explicit_type=TRUE) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: CAST(NULL AS ARRAY>) +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_empty(MAP input_map) -> BOOL) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | +-Literal(type=ARRAY>, value=NULL, has_explicit_type=TRUE) + +-input_scan= + +-SingleRowScan +== + +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', 2), + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, 'c', 3), + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', 2), + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, 'c', 3); +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] +| +-$query.$col2#2 AS `$col2` [MAP] +| +-$query.$col3#3 AS `$col3` [MAP] +| +-$query.$col4#4 AS `$col4` [MAP] ++-query= + +-ProjectScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3, $col4#4] + +-expr_list= + | +-$col1#1 := + | | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(0) STRING, repeated(0) INT64) -> MAP) + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | +-$col2#2 := + | | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | | +-Literal(type=STRING, value="c") + | | +-Literal(type=INT64, value=3) + | +-$col3#3 := + | | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(0) STRING, repeated(0) INT64) -> MAP) + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | +-$col4#4 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | +-Literal(type=STRING, value="b") + | +-Literal(type=INT64, value=2) + | +-Literal(type=STRING, value="c") + | +-Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +== + +SELECT MAP_INSERT{{|_OR_REPLACE}}( + MAP_FROM_ARRAY([('a', 1)]), 'b', 2, {{'c', 'x'|1, 3|'c'}}); +-- +ALTERNATION GROUP: 'c', 'x' +-- +ERROR: No matching signature for function MAP_INSERT for argument types: MAP, STRING, INT64, STRING, STRING. Supported signature: MAP_INSERT(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT + Argument types: MAP, STRING, INT64, STRING, STRING + Signature: MAP_INSERT(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, INT64, STRING} [at 1:8] +SELECT MAP_INSERT( + ^ +-- +ALTERNATION GROUP: 1, 3 +-- +ERROR: No matching signature for function MAP_INSERT for argument types: MAP, STRING, INT64, INT64, INT64. Supported signature: MAP_INSERT(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT + Argument types: MAP, STRING, INT64, INT64, INT64 + Signature: MAP_INSERT(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, STRING, STRING} [at 1:8] +SELECT MAP_INSERT( + ^ +-- +ALTERNATION GROUP: 'c' +-- +ERROR: Number of arguments does not match for function MAP_INSERT. Supported signature: MAP_INSERT(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT + Argument types: MAP, STRING, INT64, STRING + Signature: MAP_INSERT(MAP, T1, T2, [[T1, T2], ...]) + Wrong number of repeated arguments provided. Expected a multiple of 2 but got 1 repeated argument [at 1:8] +SELECT MAP_INSERT( + ^ +-- +ALTERNATION GROUP: _OR_REPLACE,'c', 'x' +-- +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE for argument types: MAP, STRING, INT64, STRING, STRING. Supported signature: MAP_INSERT_OR_REPLACE(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT_OR_REPLACE( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE + Argument types: MAP, STRING, INT64, STRING, STRING + Signature: MAP_INSERT_OR_REPLACE(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, INT64, STRING} [at 1:8] +SELECT MAP_INSERT_OR_REPLACE( + ^ +-- +ALTERNATION GROUP: _OR_REPLACE,1, 3 +-- +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE for argument types: MAP, STRING, INT64, INT64, INT64. Supported signature: MAP_INSERT_OR_REPLACE(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT_OR_REPLACE( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE + Argument types: MAP, STRING, INT64, INT64, INT64 + Signature: MAP_INSERT_OR_REPLACE(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, STRING, STRING} [at 1:8] +SELECT MAP_INSERT_OR_REPLACE( + ^ +-- +ALTERNATION GROUP: _OR_REPLACE,'c' +-- +ERROR: Number of arguments does not match for function MAP_INSERT_OR_REPLACE. Supported signature: MAP_INSERT_OR_REPLACE(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT_OR_REPLACE( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE + Argument types: MAP, STRING, INT64, STRING + Signature: MAP_INSERT_OR_REPLACE(MAP, T1, T2, [[T1, T2], ...]) + Wrong number of repeated arguments provided. Expected a multiple of 2 but got 1 repeated argument [at 1:8] +SELECT MAP_INSERT_OR_REPLACE( + ^ +== + +SELECT MAP_INSERT{{|_OR_REPLACE}}(MAP_FROM_ARRAY([('a', 1)])) +-- +ALTERNATION GROUP: +-- +ERROR: Number of arguments does not match for function MAP_INSERT. Supported signature: MAP_INSERT(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT(MAP_FROM_ARRAY([('a', 1)])) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT + Argument types: MAP + Signature: MAP_INSERT(MAP, T1, T2, [[T1, T2], ...]) + Signature requires at least 3 arguments, found 1 argument [at 1:8] +SELECT MAP_INSERT(MAP_FROM_ARRAY([('a', 1)])) + ^ +-- +ALTERNATION GROUP: _OR_REPLACE +-- +ERROR: Number of arguments does not match for function MAP_INSERT_OR_REPLACE. Supported signature: MAP_INSERT_OR_REPLACE(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 1:8] +SELECT MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)])) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE + Argument types: MAP + Signature: MAP_INSERT_OR_REPLACE(MAP, T1, T2, [[T1, T2], ...]) + Signature requires at least 3 arguments, found 1 argument [at 1:8] +SELECT MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)])) + ^ +== + +[no_java] +# TODO: Implement Java support for MAP value serialization. +SELECT + MAP_INSERT(NULL, 'b', 2), + MAP_INSERT(NULL, 'b', 2, 'c', 3), + MAP_INSERT_OR_REPLACE(NULL, 'b', 2), + MAP_INSERT_OR_REPLACE(NULL, 'b', 2, 'c', 3); +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] +| +-$query.$col2#2 AS `$col2` [MAP] +| +-$query.$col3#3 AS `$col3` [MAP] +| +-$query.$col4#4 AS `$col4` [MAP] ++-query= + +-ProjectScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3, $col4#4] + +-expr_list= + | +-$col1#1 := + | | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(0) STRING, repeated(0) INT64) -> MAP) + | | +-Literal(type=MAP, value=NULL) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | +-$col2#2 := + | | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | | +-Literal(type=MAP, value=NULL) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | | +-Literal(type=STRING, value="c") + | | +-Literal(type=INT64, value=3) + | +-$col3#3 := + | | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(0) STRING, repeated(0) INT64) -> MAP) + | | +-Literal(type=MAP, value=NULL) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | +-$col4#4 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=STRING, value="b") + | +-Literal(type=INT64, value=2) + | +-Literal(type=STRING, value="c") + | +-Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +== + +[no_java] +# TODO: Implement Java support for MAP value serialization. +SELECT MAP_INSERT{{|_OR_REPLACE}}(NULL, 'b', 2, {{1, 3|'c', 'x'}}); +-- +ALTERNATION GROUP: 1, 3 +-- +ERROR: No matching signature for function MAP_INSERT for argument types: NULL, STRING, INT64, INT64, INT64. Supported signature: MAP_INSERT(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 2:8] +SELECT MAP_INSERT(NULL, 'b', 2, 1, 3); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT + Argument types: NULL, STRING, INT64, INT64, INT64 + Signature: MAP_INSERT(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, STRING} [at 2:8] +SELECT MAP_INSERT(NULL, 'b', 2, 1, 3); + ^ +-- +ALTERNATION GROUP: 'c', 'x' +-- +ERROR: No matching signature for function MAP_INSERT for argument types: NULL, STRING, INT64, STRING, STRING. Supported signature: MAP_INSERT(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 2:8] +SELECT MAP_INSERT(NULL, 'b', 2, 'c', 'x'); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT + Argument types: NULL, STRING, INT64, STRING, STRING + Signature: MAP_INSERT(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, STRING} [at 2:8] +SELECT MAP_INSERT(NULL, 'b', 2, 'c', 'x'); + ^ +-- +ALTERNATION GROUP: _OR_REPLACE,1, 3 +-- +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE for argument types: NULL, STRING, INT64, INT64, INT64. Supported signature: MAP_INSERT_OR_REPLACE(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 2:8] +SELECT MAP_INSERT_OR_REPLACE(NULL, 'b', 2, 1, 3); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE + Argument types: NULL, STRING, INT64, INT64, INT64 + Signature: MAP_INSERT_OR_REPLACE(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, STRING} [at 2:8] +SELECT MAP_INSERT_OR_REPLACE(NULL, 'b', 2, 1, 3); + ^ +-- +ALTERNATION GROUP: _OR_REPLACE,'c', 'x' +-- +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE for argument types: NULL, STRING, INT64, STRING, STRING. Supported signature: MAP_INSERT_OR_REPLACE(MAP, ANY, ANY, [[ANY, ANY], ...]) [at 2:8] +SELECT MAP_INSERT_OR_REPLACE(NULL, 'b', 2, 'c', 'x'); + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for function MAP_INSERT_OR_REPLACE + Argument types: NULL, STRING, INT64, STRING, STRING + Signature: MAP_INSERT_OR_REPLACE(MAP, T1, T2, [[T1, T2], ...]) + Unable to find common supertype for templated argument + Input types for : {INT64, STRING} [at 2:8] +SELECT MAP_INSERT_OR_REPLACE(NULL, 'b', 2, 'c', 'x'); + ^ +== + +[no_java] +# TODO: Implement Java support for MAP value serialization. +SELECT MAP_INSERT{{|_OR_REPLACE}}( + NULL, {{'a', 1|DATE('2024-01-01'), STRUCT()|NULL, NULL|NULL, NULL, 1.1, true}}) +-- +ALTERNATION GROUP: 'a', 1 +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(0) STRING, repeated(0) INT64) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=STRING, value="a") + | +-Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: DATE('2024-01-01'), STRUCT() +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP>] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert(MAP> input_map, DATE, STRUCT<>, repeated(0) DATE, repeated(0) STRUCT<>) -> MAP>) + | +-Literal(type=MAP>, value=NULL) + | +-FunctionCall(ZetaSQL:date(TIMESTAMP, optional(0) STRING) -> DATE) + | | +-Literal(type=TIMESTAMP, value=2024-01-01 08:00:00+00) + | +-Literal(type=STRUCT<>, value={}) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: NULL, NULL +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, INT64, INT64, repeated(0) INT64, repeated(0) INT64) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=INT64, value=NULL) + | +-Literal(type=INT64, value=NULL) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: NULL, NULL, 1.1, true +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, DOUBLE, BOOL, repeated(1) DOUBLE, repeated(1) BOOL) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=DOUBLE, value=NULL) + | +-Literal(type=BOOL, value=NULL) + | +-Literal(type=DOUBLE, value=1.1) + | +-Literal(type=BOOL, value=true) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: _OR_REPLACE,'a', 1 +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(0) STRING, repeated(0) INT64) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=STRING, value="a") + | +-Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: _OR_REPLACE,DATE('2024-01-01'), STRUCT() +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP>] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP> input_map, DATE, STRUCT<>, repeated(0) DATE, repeated(0) STRUCT<>) -> MAP>) + | +-Literal(type=MAP>, value=NULL) + | +-FunctionCall(ZetaSQL:date(TIMESTAMP, optional(0) STRING) -> DATE) + | | +-Literal(type=TIMESTAMP, value=2024-01-01 08:00:00+00) + | +-Literal(type=STRUCT<>, value={}) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: _OR_REPLACE,NULL, NULL +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, INT64, INT64, repeated(0) INT64, repeated(0) INT64) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=INT64, value=NULL) + | +-Literal(type=INT64, value=NULL) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: _OR_REPLACE,NULL, NULL, 1.1, true +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, DOUBLE, BOOL, repeated(1) DOUBLE, repeated(1) BOOL) -> MAP) + | +-Literal(type=MAP, value=NULL) + | +-Literal(type=DOUBLE, value=NULL) + | +-Literal(type=BOOL, value=NULL) + | +-Literal(type=DOUBLE, value=1.1) + | +-Literal(type=BOOL, value=true) + +-input_scan= + +-SingleRowScan +== + +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), NULL, NULL, 'b', 2), + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, NULL, NULL), + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), NULL, NULL, 'b', 2), + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, NULL, NULL); +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] +| +-$query.$col2#2 AS `$col2` [MAP] +| +-$query.$col3#3 AS `$col3` [MAP] +| +-$query.$col4#4 AS `$col4` [MAP] ++-query= + +-ProjectScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3, $col4#4] + +-expr_list= + | +-$col1#1 := + | | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | | +-Literal(type=STRING, value=NULL) + | | +-Literal(type=INT64, value=NULL) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | +-$col2#2 := + | | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | | +-Literal(type=STRING, value=NULL) + | | +-Literal(type=INT64, value=NULL) + | +-$col3#3 := + | | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | | +-Literal(type=STRING, value=NULL) + | | +-Literal(type=INT64, value=NULL) + | | +-Literal(type=STRING, value="b") + | | +-Literal(type=INT64, value=2) + | +-$col4#4 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, STRING, INT64, repeated(1) STRING, repeated(1) INT64) -> MAP) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-Literal(type=ARRAY>, value=[{"a", 1}]) + | +-Literal(type=STRING, value="b") + | +-Literal(type=INT64, value=2) + | +-Literal(type=STRING, value=NULL) + | +-Literal(type=INT64, value=NULL) + +-input_scan= + +-SingleRowScan +== + +SELECT MAP_INSERT{{|_OR_REPLACE}}(MAP_FROM_ARRAY([(DATE "2020-01-01", 1)]), "2020-01-02", 2); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, DATE, INT64, repeated(0) DATE, repeated(0) INT64) -> MAP) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-FunctionCall(ZetaSQL:$make_array(repeated(1) STRUCT) -> ARRAY>) + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-Literal(type=DATE, value=2020-01-01, has_explicit_type=TRUE) + | | +-Literal(type=INT64, value=1) + | +-Literal(type=DATE, value=2020-01-02) + | +-Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: _OR_REPLACE +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, DATE, INT64, repeated(0) DATE, repeated(0) INT64) -> MAP) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-FunctionCall(ZetaSQL:$make_array(repeated(1) STRUCT) -> ARRAY>) + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-Literal(type=DATE, value=2020-01-01, has_explicit_type=TRUE) + | | +-Literal(type=INT64, value=1) + | +-Literal(type=DATE, value=2020-01-02) + | +-Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +SELECT MAP_INSERT{{|_OR_REPLACE}}(MAP_FROM_ARRAY([(DATE "2020-01-01", 1)]), "2020-01-02", 2, "2020-01-03", 3); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert(MAP input_map, DATE, INT64, repeated(1) DATE, repeated(1) INT64) -> MAP) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-FunctionCall(ZetaSQL:$make_array(repeated(1) STRUCT) -> ARRAY>) + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-Literal(type=DATE, value=2020-01-01, has_explicit_type=TRUE) + | | +-Literal(type=INT64, value=1) + | +-Literal(type=DATE, value=2020-01-02) + | +-Literal(type=INT64, value=2) + | +-Literal(type=DATE, value=2020-01-03) + | +-Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: _OR_REPLACE +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [MAP] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := + | +-FunctionCall(ZetaSQL:map_insert_or_replace(MAP input_map, DATE, INT64, repeated(1) DATE, repeated(1) INT64) -> MAP) + | +-FunctionCall(ZetaSQL:map_from_array(ARRAY>) -> MAP) + | | +-FunctionCall(ZetaSQL:$make_array(repeated(1) STRUCT) -> ARRAY>) + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-Literal(type=DATE, value=2020-01-01, has_explicit_type=TRUE) + | | +-Literal(type=INT64, value=1) + | +-Literal(type=DATE, value=2020-01-02) + | +-Literal(type=INT64, value=2) + | +-Literal(type=DATE, value=2020-01-03) + | +-Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +== + diff --git a/zetasql/analyzer/testdata/multi_level_aggregation.test b/zetasql/analyzer/testdata/multi_level_aggregation.test index 61764c354..36759671e 100644 --- a/zetasql/analyzer/testdata/multi_level_aggregation.test +++ b/zetasql/analyzer/testdata/multi_level_aggregation.test @@ -34,15 +34,12 @@ ERROR: Multi-level aggregation is not yet supported. [at 2:24] ^ == +# TODO: Re-enable this test. # GROUP BY modifier must be specified when invoking a multi-level aggregate expr -SELECT - SUM(ANY_VALUE(int64)) -FROM SimpleTypes; --- -ERROR: Aggregations of aggregations are not allowed [at 2:3] - SUM(ANY_VALUE(int64)) - ^ -== +# SELECT +# SUM(ANY_VALUE(int64)) +# FROM SimpleTypes; +# == # GROUP BY modifier must be a groupable type. SELECT @@ -159,6 +156,39 @@ ERROR: GROUP BY GROUPING SETS is not supported inside an aggregate function. [at ^ == +# GROUP AND ORDER BY is only supported in pipes. +[language_features=V_1_4_MULTILEVEL_AGGREGATION,PIPES] +SELECT + SUM(ANY_VALUE(int64) GROUP AND ORDER BY bool) +FROM SimpleTypes; +-- +ERROR: GROUP AND ORDER BY is not supported outside pipe AGGREGATE. [at 2:24] + SUM(ANY_VALUE(int64) GROUP AND ORDER BY bool) + ^ +== + +# GROUP BY can only support order specification in in pipes. +[language_features=V_1_4_MULTILEVEL_AGGREGATION,PIPES] +SELECT + SUM(ANY_VALUE(int64) GROUP BY double ASC) +FROM SimpleTypes; +-- +ERROR: GROUP BY does not support order specification outside pipe AGGREGATE [at 2:40] + SUM(ANY_VALUE(int64) GROUP BY double ASC) + ^ +== + +# GROUP BY modifiers cannot have aliases +[language_features=V_1_4_MULTILEVEL_AGGREGATION,PIPES] +SELECT + SUM(ANY_VALUE(int64) GROUP BY bool AS some_alias) +FROM SimpleTypes; +-- +ERROR: GROUP BY expressions in an aggregate function cannot have aliases. [at 2:38] + SUM(ANY_VALUE(int64) GROUP BY bool AS some_alias) + ^ +== + # GROUP BY modifiers not supported on analytic functions. SELECT SUM(ANY_VALUE(int64) GROUP BY bool) OVER () diff --git a/zetasql/analyzer/testdata/named_arguments.test b/zetasql/analyzer/testdata/named_arguments.test index 813a5d884..883ea7e01 100644 --- a/zetasql/analyzer/testdata/named_arguments.test +++ b/zetasql/analyzer/testdata/named_arguments.test @@ -1574,7 +1574,7 @@ SELECT fn_rep_opt("a0", "r0", "r1", "r2", o0 => "o0", o1 => "o1"); Signature Mismatch Details: ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REP_OPT Argument types: STRING, STRING, STRING, STRING, o0 => STRING, o1 => STRING - Signature: FN_REP_OPT([a0=>]STRING, [[r0=>]STRING, ...], [[r1=>]STRING, ...], [[r2=>]STRING, ...], [a1=>]STRING, [[o0=>]STRING], [[o1=>]STRING]) + Signature: FN_REP_OPT([a0=>]STRING, [[STRING, STRING, STRING], ...], [a1=>]STRING, [[o0=>]STRING], [[o1=>]STRING]) Required named argument `a1` is not provided [at 1:8] SELECT fn_rep_opt("a0", "r0", "r1", "r2", o0 => "o0", o1 => "o1"); ^ @@ -2435,14 +2435,14 @@ SELECT fn_repeated_t1_t2_with_optional_named_t1(1, "2", o1=>true); -- -ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 for argument types: INT64, STRING, o1 => BOOL. Supported signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([ANY, ...], [ANY, ...], [o1 => ANY]) [at 3:3] +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 for argument types: INT64, STRING, o1 => BOOL. Supported signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([[ANY, ANY], ...], [o1 => ANY]) [at 3:3] fn_repeated_t1_t2_with_optional_named_t1(1, "2", o1=>true); ^ -- Signature Mismatch Details: ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 Argument types: INT64, STRING, o1 => BOOL - Signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([T1, ...], [T2, ...], [o1 => T1]) + Signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([[T1, T2], ...], [o1 => T1]) Unable to find common supertype for templated argument Input types for : {INT64, BOOL} [at 3:3] fn_repeated_t1_t2_with_optional_named_t1(1, "2", o1=>true); @@ -2455,14 +2455,14 @@ SELECT fn_repeated_t1_t2_with_optional_named_t1(1, "2", 3, "4", o1=>true); -- -ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 for argument types: INT64, STRING, INT64, STRING, o1 => BOOL. Supported signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([ANY, ...], [ANY, ...], [o1 => ANY]) [at 3:3] +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 for argument types: INT64, STRING, INT64, STRING, o1 => BOOL. Supported signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([[ANY, ANY], ...], [o1 => ANY]) [at 3:3] fn_repeated_t1_t2_with_optional_named_t1(1, "2", 3, "4", o1=>true); ^ -- Signature Mismatch Details: ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 Argument types: INT64, STRING, INT64, STRING, o1 => BOOL - Signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([T1, ...], [T2, ...], [o1 => T1]) + Signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([[T1, T2], ...], [o1 => T1]) Unable to find common supertype for templated argument Input types for : {INT64, BOOL} [at 3:3] fn_repeated_t1_t2_with_optional_named_t1(1, "2", 3, "4", o1=>true); @@ -2475,14 +2475,14 @@ SELECT fn_repeated_t1_t2_with_optional_named_t1(1, "2", true, "3", o1=>1); -- -ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 for argument types: INT64, STRING, BOOL, STRING, o1 => INT64. Supported signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([ANY, ...], [ANY, ...], [o1 => ANY]) [at 3:3] +ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 for argument types: INT64, STRING, BOOL, STRING, o1 => INT64. Supported signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([[ANY, ANY], ...], [o1 => ANY]) [at 3:3] fn_repeated_t1_t2_with_optional_named_t1(1, "2", true, "3", o1=>1); ^ -- Signature Mismatch Details: ERROR: No matching signature for function SAMPLE_FUNCTIONS:FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1 Argument types: INT64, STRING, BOOL, STRING, o1 => INT64 - Signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([T1, ...], [T2, ...], [o1 => T1]) + Signature: FN_REPEATED_T1_T2_WITH_OPTIONAL_NAMED_T1([[T1, T2], ...], [o1 => T1]) Unable to find common supertype for templated argument Input types for : {INT64, BOOL} [at 3:3] fn_repeated_t1_t2_with_optional_named_t1(1, "2", true, "3", o1=>1); diff --git a/zetasql/analyzer/testdata/order_by_collate.test b/zetasql/analyzer/testdata/order_by_collate.test index 78cd911b6..f10136095 100644 --- a/zetasql/analyzer/testdata/order_by_collate.test +++ b/zetasql/analyzer/testdata/order_by_collate.test @@ -3,9 +3,7 @@ select `string` col from SimpleTypes order by col COLLATE {{"en_US"|@test_param_string|r"en_US"}} -- -ALTERNATION GROUPS: - "en_US" - r"en_US" +ALTERNATION GROUP: "en_US" -- QueryStmt +-output_column_list= @@ -18,6 +16,7 @@ QueryStmt | +-TableScan(column_list=[SimpleTypes.string#5], table=SimpleTypes, column_index_list=[4]) +-order_by_item_list= +-OrderByItem + +-parse_location=46-65 +-column_ref= | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) +-collation_name= @@ -36,10 +35,30 @@ QueryStmt | +-TableScan(column_list=[SimpleTypes.string#5], table=SimpleTypes, column_index_list=[4]) +-order_by_item_list= +-OrderByItem + +-parse_location=46-76 +-column_ref= | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) +-collation_name= +-Parameter(type=STRING, name="test_param_string") +-- +ALTERNATION GROUP: r"en_US" +-- +QueryStmt ++-output_column_list= +| +-SimpleTypes.string#5 AS col [STRING] ++-query= + +-OrderByScan + +-column_list=[SimpleTypes.string#5] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[SimpleTypes.string#5], table=SimpleTypes, column_index_list=[4]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=46-66 + +-column_ref= + | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) + +-collation_name= + +-Literal(type=STRING, value="en_US") == # COLLATE must be followed by a string literal or a string parameter. @@ -113,6 +132,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Value#2], table=KeyValue, column_index_list=[1]) +-order_by_item_list= +-OrderByItem + +-parse_location=36-70 +-column_ref= | +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#3) +-collation_name= @@ -155,22 +175,26 @@ QueryStmt | +-TableScan(column_list=SimpleTypes.[int32#1, string#5], table=SimpleTypes, column_index_list=[0, 4]) +-order_by_item_list= +-OrderByItem + | +-parse_location=75-93 | +-column_ref= | | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) | +-collation_name= | +-Literal(type=STRING, value="s1") +-OrderByItem + | +-parse_location=104-123 | +-column_ref= | | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) | +-collation_name= | | +-Literal(type=STRING, value="s2") | +-is_descending=TRUE +-OrderByItem + | +-parse_location=134-166 | +-column_ref= | | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) | +-collation_name= | +-Parameter(type=STRING, name="test_param_string") +-OrderByItem + +-parse_location=177-183 +-column_ref= | +-ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-is_descending=TRUE @@ -194,16 +218,19 @@ QueryStmt | +-TableScan(column_list=[SimpleTypes.string#5], table=SimpleTypes, column_index_list=[4]) +-order_by_item_list= +-OrderByItem + | +-parse_location=44-56 | +-column_ref= | | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) | +-collation_name= | +-Literal(type=STRING, value="") +-OrderByItem + | +-parse_location=67-83 | +-column_ref= | | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) | +-collation_name= | +-Literal(type=STRING, value="NULL") +-OrderByItem + +-parse_location=94-117 +-column_ref= | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) +-collation_name= @@ -434,6 +461,7 @@ QueryStmt | +-TableScan(column_list=[SimpleTypes.string#5], table=SimpleTypes, column_index_list=[4]) +-order_by_item_list= +-OrderByItem + +-parse_location=42-59 +-column_ref= | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) +-collation_name= diff --git a/zetasql/analyzer/testdata/order_by_in_aggregate.test b/zetasql/analyzer/testdata/order_by_in_aggregate.test index 73a3f5eed..176faef46 100644 --- a/zetasql/analyzer/testdata/order_by_in_aggregate.test +++ b/zetasql/analyzer/testdata/order_by_in_aggregate.test @@ -20,6 +20,7 @@ QueryStmt +-ColumnRef(type=INT32, column=TestTable.key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=30-33 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) @@ -70,9 +71,11 @@ QueryStmt +-ColumnRef(type=INT32, column=TestTable.key#1) +-order_by_item_list= +-OrderByItem + | +-parse_location=30-39 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) +-OrderByItem + +-parse_location=41-50 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol2#5) @@ -105,6 +108,7 @@ QueryStmt +-Literal(type=INT64, value=1) +-order_by_item_list= +-OrderByItem + +-parse_location=52-55 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) @@ -141,10 +145,12 @@ QueryStmt | +-default_value=0 | +-order_by_item_list= | +-OrderByItem + | +-parse_location=53-56 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=95-98 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.Key#5) @@ -184,6 +190,7 @@ QueryStmt +-default_value="default_name" +-order_by_item_list= +-OrderByItem + +-parse_location=50-57 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) @@ -220,6 +227,7 @@ QueryStmt | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=45-50 | | +-column_ref= | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) | +-s2#4 := @@ -228,6 +236,7 @@ QueryStmt | | +-Literal(type=STRING, value=",") | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=97-102 | | +-column_ref= | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) | +-s3#5 := @@ -237,19 +246,24 @@ QueryStmt | +-Literal(type=BYTES, value=b"b") | +-order_by_item_list= | +-OrderByItem + | +-parse_location=165-170 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + | +-parse_location=214-217 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.key#6) +-OrderByItem + | +-parse_location=219-221 | +-column_ref= | +-ColumnRef(type=STRING, column=$aggregate.s1#3) +-OrderByItem + | +-parse_location=223-225 | +-column_ref= | +-ColumnRef(type=STRING, column=$aggregate.s2#4) +-OrderByItem + +-parse_location=227-229 +-column_ref= +-ColumnRef(type=BYTES, column=$aggregate.s3#5) @@ -309,6 +323,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=60-81 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) +-$agg2#7 := @@ -316,6 +331,7 @@ QueryStmt +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) +-order_by_item_list= +-OrderByItem + +-parse_location=134-156 +-column_ref= +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) @@ -363,6 +379,7 @@ QueryStmt +-default_value=0 +-order_by_item_list= +-OrderByItem + +-parse_location=73-76 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) @@ -417,6 +434,7 @@ QueryStmt +-default_value=0 +-order_by_item_list= +-OrderByItem + +-parse_location=71-106 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#7) @@ -459,6 +477,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=69-90 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) @@ -522,10 +541,12 @@ QueryStmt +-default_value=0 +-order_by_item_list= +-OrderByItem + | +-parse_location=71-79 | +-column_ref= | | +-ColumnRef(type=INT32, column=TestTable.key#1) | +-is_descending=TRUE +-OrderByItem + +-parse_location=81-107 +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol2#4) +-is_descending=TRUE @@ -569,6 +590,7 @@ QueryStmt +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-order_by_item_list= +-OrderByItem + +-parse_location=73-116 +-column_ref= | +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-collation_name= @@ -681,13 +703,16 @@ QueryStmt | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=71-92 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-OrderByItem + | | +-parse_location=114-144 | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol2#5) | | +-is_descending=TRUE | +-OrderByItem + | +-parse_location=166-188 | +-column_ref= | +-ColumnRef(type=STRING, column=$orderby.$orderbycol3#6) +-b#9 := @@ -700,12 +725,15 @@ QueryStmt | +-default_value=77 | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=255-276 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-OrderByItem + | | +-parse_location=298-320 | | +-column_ref= | | +-ColumnRef(type=STRING, column=$orderby.$orderbycol3#6) | +-OrderByItem + | +-parse_location=342-372 | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol3#8) | +-is_descending=TRUE @@ -714,13 +742,16 @@ QueryStmt +-ColumnRef(type=STRING, column=$orderby.$orderbycol3#6) +-order_by_item_list= +-OrderByItem + | +-parse_location=441-462 | +-column_ref= | +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#10) +-OrderByItem + | +-parse_location=484-510 | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-is_descending=TRUE +-OrderByItem + +-parse_location=532-575 +-column_ref= | +-ColumnRef(type=STRING, column=$orderby.$orderbycol3#6) +-collation_name= @@ -794,6 +825,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=60-81 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) +-function_group_list= @@ -869,6 +901,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=39-42 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) @@ -876,6 +909,9 @@ QueryStmt select array_agg(distinct {{|TestTable.}}Key order by {{|TestTable.}}Key) from TestTable +-- +ALTERNATION GROUP: + -- QueryStmt +-output_column_list= @@ -895,14 +931,89 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=39-42 + +-column_ref= + +-ColumnRef(type=INT32, column=TestTable.key#1) +-- +ALTERNATION GROUP: TestTable. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#4 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=TestTable.key#1) + +-distinct=TRUE + +-order_by_item_list= + +-OrderByItem + +-parse_location=39-52 + +-column_ref= + +-ColumnRef(type=INT32, column=TestTable.key#1) +-- +ALTERNATION GROUP: TestTable., +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#4 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=TestTable.key#1) + +-distinct=TRUE + +-order_by_item_list= + +-OrderByItem + +-parse_location=49-52 + +-column_ref= + +-ColumnRef(type=INT32, column=TestTable.key#1) +-- +ALTERNATION GROUP: TestTable.,TestTable. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#4 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=TestTable.key#1) + +-distinct=TRUE + +-order_by_item_list= + +-OrderByItem + +-parse_location=49-62 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) - == select array_agg({{|KitchenSinkValueTable.}}int64_key_1 order by {{|KitchenSinkValueTable.}}int64_key_1) from KitchenSinkValueTable +-- +ALTERNATION GROUP: + -- QueryStmt +-output_column_list= @@ -931,9 +1042,108 @@ QueryStmt +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) +-order_by_item_list= +-OrderByItem + +-parse_location=55-66 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) +-- +ALTERNATION GROUP: KitchenSinkValueTable. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=[KitchenSinkValueTable.value#1, $orderby.$orderbycol1#2] + | +-expr_list= + | | +-$orderbycol1#2 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | +-field_descriptor=int64_key_1 + | +-input_scan= + | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=55-88 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) +-- +ALTERNATION GROUP: KitchenSinkValueTable., +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=[KitchenSinkValueTable.value#1, $orderby.$orderbycol1#2] + | +-expr_list= + | | +-$orderbycol1#2 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | +-field_descriptor=int64_key_1 + | +-input_scan= + | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=77-88 + +-column_ref= + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) +-- +ALTERNATION GROUP: KitchenSinkValueTable.,KitchenSinkValueTable. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=[KitchenSinkValueTable.value#1, $orderby.$orderbycol1#2] + | +-expr_list= + | | +-$orderbycol1#2 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | +-field_descriptor=int64_key_1 + | +-input_scan= + | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT64) -> ARRAY) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=77-110 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) - == select array_agg(distinct KitchenSinkValueTable.int64_key_1 @@ -968,6 +1178,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=86-119 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#2) @@ -976,6 +1187,9 @@ QueryStmt select array_agg({{|TestStructValueTable.}}a order by {{|TestStructValueTable.}}a) from TestStructValueTable +-- +ALTERNATION GROUP: + -- QueryStmt +-output_column_list= @@ -1004,9 +1218,108 @@ QueryStmt +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) +-order_by_item_list= +-OrderByItem + +-parse_location=45-46 + +-column_ref= + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) +-- +ALTERNATION GROUP: TestStructValueTable. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestStructValueTable.value#1, $orderby.$orderbycol1#2] + | +-expr_list= + | | +-$orderbycol1#2 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + | | +-field_idx=0 + | +-input_scan= + | +-TableScan(column_list=[TestStructValueTable.value#1], table=TestStructValueTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=45-67 + +-column_ref= + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) +-- +ALTERNATION GROUP: TestStructValueTable., +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestStructValueTable.value#1, $orderby.$orderbycol1#2] + | +-expr_list= + | | +-$orderbycol1#2 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + | | +-field_idx=0 + | +-input_scan= + | +-TableScan(column_list=[TestStructValueTable.value#1], table=TestStructValueTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-67 + +-column_ref= + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) +-- +ALTERNATION GROUP: TestStructValueTable.,TestStructValueTable. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestStructValueTable.value#1, $orderby.$orderbycol1#2] + | +-expr_list= + | | +-$orderbycol1#2 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=TestStructValueTable.value#1) + | | +-field_idx=0 + | +-input_scan= + | +-TableScan(column_list=[TestStructValueTable.value#1], table=TestStructValueTable, column_index_list=[0]) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-88 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) - == select array_agg(distinct TestStructValueTable.a @@ -1041,6 +1354,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=75-97 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#2) @@ -1053,6 +1367,9 @@ from KitchenSink.int32_val as int32_val, KitchenSink.string_val as string_val from TestTable) as T +-- +ALTERNATION GROUP: + -- QueryStmt +-output_column_list= @@ -1097,9 +1414,156 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=39-42 + +-column_ref= + +-ColumnRef(type=INT32, column=TestTable.key#1) +-- +ALTERNATION GROUP: T. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.key#1, T.int64_val#4, T.int32_val#5, T.string_val#6] + | +-expr_list= + | | +-int64_val#4 := + | | | +-GetProtoField + | | | +-type=INT64 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int64_val + | | | +-default_value=0 + | | +-int32_val#5 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int32_val + | | | +-default_value=77 + | | +-string_val#6 := + | | +-GetProtoField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=string_val + | | +-default_value="default_name" + | +-input_scan= + | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2]) + +-aggregate_list= + +-$agg1#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=TestTable.key#1) + +-distinct=TRUE + +-order_by_item_list= + +-OrderByItem + +-parse_location=39-44 + +-column_ref= + +-ColumnRef(type=INT32, column=TestTable.key#1) +-- +ALTERNATION GROUP: T., +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.key#1, T.int64_val#4, T.int32_val#5, T.string_val#6] + | +-expr_list= + | | +-int64_val#4 := + | | | +-GetProtoField + | | | +-type=INT64 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int64_val + | | | +-default_value=0 + | | +-int32_val#5 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int32_val + | | | +-default_value=77 + | | +-string_val#6 := + | | +-GetProtoField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=string_val + | | +-default_value="default_name" + | +-input_scan= + | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2]) + +-aggregate_list= + +-$agg1#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=TestTable.key#1) + +-distinct=TRUE + +-order_by_item_list= + +-OrderByItem + +-parse_location=41-44 + +-column_ref= + +-ColumnRef(type=INT32, column=TestTable.key#1) +-- +ALTERNATION GROUP: T.,T. +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.key#1, T.int64_val#4, T.int32_val#5, T.string_val#6] + | +-expr_list= + | | +-int64_val#4 := + | | | +-GetProtoField + | | | +-type=INT64 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int64_val + | | | +-default_value=0 + | | +-int32_val#5 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int32_val + | | | +-default_value=77 + | | +-string_val#6 := + | | +-GetProtoField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=string_val + | | +-default_value="default_name" + | +-input_scan= + | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2]) + +-aggregate_list= + +-$agg1#7 := + +-AggregateFunctionCall(ZetaSQL:array_agg(INT32) -> ARRAY) + +-ColumnRef(type=INT32, column=TestTable.key#1) + +-distinct=TRUE + +-order_by_item_list= + +-OrderByItem + +-parse_location=41-46 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) - == select Key, @@ -1167,6 +1631,7 @@ QueryStmt | +-distinct=TRUE | +-order_by_item_list= | +-OrderByItem + | +-parse_location=111-120 | +-column_ref= | +-ColumnRef(type=INT32, column=$subquery1.int32_val#5) +-b#8 := @@ -1177,9 +1642,11 @@ QueryStmt | +-distinct=TRUE | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=227-236 | | +-column_ref= | | +-ColumnRef(type=INT32, column=$subquery1.int32_val#5) | +-OrderByItem + | +-parse_location=238-247 | +-column_ref= | +-ColumnRef(type=INT64, column=$subquery1.int64_val#4) +-c#9 := @@ -1190,12 +1657,15 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + | +-parse_location=354-363 | +-column_ref= | +-ColumnRef(type=INT32, column=$subquery1.int32_val#5) +-OrderByItem + | +-parse_location=365-374 | +-column_ref= | +-ColumnRef(type=INT64, column=$subquery1.int64_val#4) +-OrderByItem + +-parse_location=376-386 +-column_ref= +-ColumnRef(type=STRING, column=$subquery1.string_val#6) @@ -1262,13 +1732,16 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + | +-parse_location=111-125 | +-column_ref= | | +-ColumnRef(type=INT32, column=$subquery1.int32_val#5) | +-is_descending=TRUE +-OrderByItem + | +-parse_location=160-173 | +-column_ref= | +-ColumnRef(type=INT64, column=$subquery1.int64_val#4) +-OrderByItem + +-parse_location=208-223 +-column_ref= | +-ColumnRef(type=STRING, column=$subquery1.string_val#6) +-is_descending=TRUE @@ -1334,12 +1807,15 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + | +-parse_location=111-120 | +-column_ref= | +-ColumnRef(type=INT32, column=$subquery1.int32_val#5) +-OrderByItem + | +-parse_location=122-131 | +-column_ref= | +-ColumnRef(type=INT32, column=$subquery1.int32_val#5) +-OrderByItem + +-parse_location=133-142 +-column_ref= +-ColumnRef(type=INT64, column=$subquery1.int64_val#4) @@ -1433,6 +1909,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=40-44 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#4) == @@ -1491,6 +1968,7 @@ QueryStmt +-ColumnRef(type=ARRAY, column=$union_all.x#8) +-order_by_item_list= +-OrderByItem + +-parse_location=35-36 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.w#7) == @@ -1549,6 +2027,7 @@ QueryStmt +-ColumnRef(type=ARRAY, column=$union_all.x#8) +-order_by_item_list= +-OrderByItem + +-parse_location=35-41 +-column_ref= | +-ColumnRef(type=INT64, column=$union_all.w#7) +-is_descending=TRUE @@ -1599,6 +2078,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=60-81 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-limit= @@ -1608,6 +2088,7 @@ QueryStmt +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) +-order_by_item_list= | +-OrderByItem + | +-parse_location=143-165 | +-column_ref= | +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) +-limit= @@ -1679,6 +2160,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= | +-OrderByItem + | +-parse_location=39-42 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-limit= @@ -1727,10 +2209,12 @@ QueryStmt +-default_value=0 +-order_by_item_list= | +-OrderByItem + | | +-parse_location=71-79 | | +-column_ref= | | | +-ColumnRef(type=INT32, column=TestTable.key#1) | | +-is_descending=TRUE | +-OrderByItem + | +-parse_location=81-107 | +-column_ref= | | +-ColumnRef(type=INT32, column=$orderby.$orderbycol2#4) | +-is_descending=TRUE @@ -1773,6 +2257,7 @@ QueryStmt +-ColumnRef(type=ARRAY, column=$subquery1.arr1#1) +-order_by_item_list= +-OrderByItem + +-parse_location=38-42 +-column_ref= +-ColumnRef(type=ARRAY, column=$subquery1.arr2#2) == @@ -1826,6 +2311,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=47-51 +-column_ref= +-ColumnRef(type=ARRAY, column=$subquery1.arr1#1) == @@ -1884,6 +2370,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=43-50 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -1928,6 +2415,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=73-98 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -1981,6 +2469,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=128-206 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -2020,6 +2509,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=84-106 +-column_ref= +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) -- @@ -2066,6 +2556,7 @@ QueryStmt +-distinct=TRUE +-order_by_item_list= +-OrderByItem + +-parse_location=88-114 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#4) == @@ -2113,6 +2604,7 @@ QueryStmt +-ColumnRef(type=INT32, column=TestTable.key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=30-45 +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-null_order=NULLS_FIRST @@ -2136,6 +2628,7 @@ QueryStmt +-ColumnRef(type=INT32, column=TestTable.key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=30-44 +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-null_order=NULLS_LAST diff --git a/zetasql/analyzer/testdata/order_preservation.test b/zetasql/analyzer/testdata/order_preservation.test index 428bb398b..b2ae9b88d 100644 --- a/zetasql/analyzer/testdata/order_preservation.test +++ b/zetasql/analyzer/testdata/order_preservation.test @@ -27,6 +27,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=44-47 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -55,6 +56,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=68-71 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -84,6 +86,7 @@ QueryStmt | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=46-49 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) == @@ -106,6 +109,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=35-38 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-limit= @@ -134,6 +138,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=50-53 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-limit= @@ -178,6 +183,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=60-63 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-limit= @@ -222,6 +228,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=49-52 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-limit= @@ -262,6 +269,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=56-59 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-limit= @@ -296,6 +304,7 @@ QueryStmt | | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=45-48 | | +-column_ref= | | +-ColumnRef(type=INT32, column=TestTable.key#1) | +-$col2#14 := @@ -310,6 +319,7 @@ QueryStmt | | | +-TableScan(column_list=[TestTable.key#4], table=TestTable, column_index_list=[0]) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=94-97 | | +-column_ref= | | +-ColumnRef(type=INT32, column=TestTable.key#4) | +-$col3#15 := @@ -323,6 +333,7 @@ QueryStmt | | | +-TableScan(column_list=[TestTable.key#7], table=TestTable, column_index_list=[0]) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=144-147 | | +-column_ref= | | +-ColumnRef(type=INT32, column=TestTable.key#7) | +-$col4#16 := @@ -338,6 +349,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#10], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=193-196 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#10) +-input_scan= @@ -365,6 +377,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=46-49 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-query= @@ -397,6 +410,7 @@ QueryStmt | | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=47-50 | | +-column_ref= | | +-ColumnRef(type=INT32, column=TestTable.key#1) | +-WithEntry @@ -414,6 +428,7 @@ QueryStmt | +-WithRefScan(column_list=[T2.key#5], with_query_name="T2") +-order_by_item_list= +-OrderByItem + +-parse_location=109-115 +-column_ref= | +-ColumnRef(type=INT32, column=T2.key#5) +-is_descending=TRUE @@ -463,6 +478,7 @@ QueryStmt | | +-ColumnHolder(column=$array_offset.y#2) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=81-82 | +-column_ref= | +-ColumnRef(type=INT64, column=$array_offset.y#2) +-aggregate_list= @@ -496,6 +512,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=62-65 | +-column_ref= | +-ColumnRef(type=INT32, column=TestTable.key#1) +-element_column_list=[$array.$unnest1#4] diff --git a/zetasql/analyzer/testdata/orderby.test b/zetasql/analyzer/testdata/orderby.test index c40886053..ad89c3321 100644 --- a/zetasql/analyzer/testdata/orderby.test +++ b/zetasql/analyzer/testdata/orderby.test @@ -20,13 +20,16 @@ QueryStmt | +-TableScan(table=KeyValue) +-order_by_item_list= +-OrderByItem + | +-parse_location=38-44 | +-column_ref= | | +-ColumnRef(type=INT64, column=$query.$col2#4) | +-is_descending=TRUE +-OrderByItem + | +-parse_location=46-47 | +-column_ref= | +-ColumnRef(type=INT64, column=$query.$col3#5) +-OrderByItem + +-parse_location=49-54 +-column_ref= +-ColumnRef(type=INT64, column=$query.$col1#3) == @@ -44,6 +47,7 @@ QueryStmt | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=35-36 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) == @@ -63,6 +67,7 @@ QueryStmt | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) +-order_by_item_list= +-OrderByItem + +-parse_location=33-34 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) == @@ -85,6 +90,7 @@ QueryStmt | +-key#4 := ColumnRef(type=INT32, column=TestTable.key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=48-49 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.key#4) == @@ -119,6 +125,7 @@ QueryStmt | +-Literal(type=INT64, value=5) +-order_by_item_list= +-OrderByItem + +-parse_location=74-75 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.key#4) == @@ -153,6 +160,7 @@ QueryStmt | +-Literal(type=INT64, value=5) +-order_by_item_list= +-OrderByItem + +-parse_location=74-75 +-column_ref= +-ColumnRef(type=STRING, column=$aggregate.$agg1#3) == @@ -183,6 +191,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=51-52 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -205,6 +214,7 @@ QueryStmt | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2]) +-order_by_item_list= +-OrderByItem + +-parse_location=63-64 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) == @@ -224,9 +234,11 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + | +-parse_location=41-42 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + +-parse_location=44-45 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -246,21 +258,27 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + | +-parse_location=41-42 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + | +-parse_location=44-45 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + | +-parse_location=47-48 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + | +-parse_location=50-51 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + | +-parse_location=53-54 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + +-parse_location=56-57 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -298,46 +316,60 @@ QueryStmt | +-TableScan(column_list=SimpleTypes.[int32#1, int64#2, uint32#3, uint64#4, string#5, bytes#6, bool#7, float#8, double#9, date#10, timestamp_seconds#11, timestamp_millis#12, timestamp_micros#13, timestamp_nanos#14, timestamp#15, numeric#16, bignumeric#17, json#18, uuid#19], table=SimpleTypes, column_index_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]) +-order_by_item_list= +-OrderByItem + | +-parse_location=35-36 | +-column_ref= | +-ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-OrderByItem + | +-parse_location=38-39 | +-column_ref= | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) +-OrderByItem + | +-parse_location=41-46 | +-column_ref= | +-ColumnRef(type=UINT32, column=SimpleTypes.uint32#3) +-OrderByItem + | +-parse_location=48-54 | +-column_ref= | | +-ColumnRef(type=UINT64, column=SimpleTypes.uint64#4) | +-is_descending=TRUE +-OrderByItem + | +-parse_location=56-57 | +-column_ref= | +-ColumnRef(type=STRING, column=SimpleTypes.string#5) +-OrderByItem + | +-parse_location=59-60 | +-column_ref= | +-ColumnRef(type=BYTES, column=SimpleTypes.bytes#6) +-OrderByItem + | +-parse_location=62-63 | +-column_ref= | +-ColumnRef(type=BOOL, column=SimpleTypes.bool#7) +-OrderByItem + | +-parse_location=65-66 | +-column_ref= | +-ColumnRef(type=FLOAT, column=SimpleTypes.float#8) +-OrderByItem + | +-parse_location=68-69 | +-column_ref= | +-ColumnRef(type=DOUBLE, column=SimpleTypes.double#9) +-OrderByItem + | +-parse_location=71-73 | +-column_ref= | +-ColumnRef(type=DATE, column=SimpleTypes.date#10) +-OrderByItem + | +-parse_location=75-77 | +-column_ref= | +-ColumnRef(type=TIMESTAMP, column=SimpleTypes.timestamp_seconds#11) +-OrderByItem + | +-parse_location=79-81 | +-column_ref= | +-ColumnRef(type=TIMESTAMP, column=SimpleTypes.timestamp_millis#12) +-OrderByItem + | +-parse_location=83-85 | +-column_ref= | +-ColumnRef(type=TIMESTAMP, column=SimpleTypes.timestamp_micros#13) +-OrderByItem + +-parse_location=87-89 +-column_ref= +-ColumnRef(type=TIMESTAMP, column=SimpleTypes.timestamp_nanos#14) == @@ -357,6 +389,7 @@ QueryStmt | +-TableScan(column_list=[ComplexTypes.TestEnum#2], table=ComplexTypes, column_index_list=[1]) +-order_by_item_list= +-OrderByItem + +-parse_location=43-44 +-column_ref= +-ColumnRef(type=ENUM, column=ComplexTypes.TestEnum#2) == @@ -396,6 +429,7 @@ QueryStmt | +-output_column_list=KeyValue.[Key#3, Value#4] +-order_by_item_list= +-OrderByItem + +-parse_location=83-84 +-column_ref= +-ColumnRef(type=STRING, column=$union_all.value#6) == @@ -442,6 +476,7 @@ QueryStmt | | | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=50-51 | | | +-column_ref= | | | +-ColumnRef(type=INT32, column=TestTable.key#1) | | +-output_column_list=[TestTable.key#1, $union_all1.$col2#4, $union_all1.$col3#5] @@ -472,6 +507,7 @@ QueryStmt | | | | +-TableScan(column_list=[TestTable.key#6], table=TestTable, column_index_list=[0]) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=113-114 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$union_all2.$col2#9) | | +-limit= @@ -481,6 +517,7 @@ QueryStmt | +-output_column_list=[TestTable.key#6, $union_all2.$col2#9, $union_all2.$col3#10] +-order_by_item_list= +-OrderByItem + +-parse_location=142-143 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.$col3#13) == @@ -569,6 +606,7 @@ QueryStmt | +-TableScan(column_list=[ComplexTypes.Int32Array#4], table=ComplexTypes, column_index_list=[3]) +-order_by_item_list= +-OrderByItem + +-parse_location=45-46 +-column_ref= +-ColumnRef(type=ARRAY, column=ComplexTypes.Int32Array#4) == @@ -684,6 +722,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=32-36 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#3) == @@ -703,6 +742,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-37 +-column_ref= +-ColumnRef(type=INT64, column=KeyValue.Key#1) == @@ -742,6 +782,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=38-45 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -761,6 +802,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-39 +-column_ref= +-ColumnRef(type=STRING, column=KeyValue.Value#2) == @@ -795,36 +837,47 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + | +-parse_location=32-35 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + | +-parse_location=37-42 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + | +-parse_location=44-45 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + | +-parse_location=47-48 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + | +-parse_location=50-55 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + | +-parse_location=57-58 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + | +-parse_location=60-63 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + | +-parse_location=65-66 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-OrderByItem + | +-parse_location=68-75 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol9#3) +-OrderByItem + | +-parse_location=77-78 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + +-parse_location=80-87 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol11#4) == @@ -880,36 +933,47 @@ QueryStmt | +-output_column_list=KeyValue.[Key#3, Value#4] +-order_by_item_list= +-OrderByItem + | +-parse_location=119-123 | +-column_ref= | +-ColumnRef(type=INT64, column=$union_all.key1#5) +-OrderByItem + | +-parse_location=125-131 | +-column_ref= | +-ColumnRef(type=STRING, column=$union_all.value1#6) +-OrderByItem + | +-parse_location=133-134 | +-column_ref= | +-ColumnRef(type=INT64, column=$union_all.key1#5) +-OrderByItem + | +-parse_location=136-137 | +-column_ref= | +-ColumnRef(type=STRING, column=$union_all.value1#6) +-OrderByItem + | +-parse_location=139-145 | +-column_ref= | +-ColumnRef(type=STRING, column=$union_all.value1#6) +-OrderByItem + | +-parse_location=147-148 | +-column_ref= | +-ColumnRef(type=INT64, column=$union_all.key1#5) +-OrderByItem + | +-parse_location=150-154 | +-column_ref= | +-ColumnRef(type=INT64, column=$union_all.key1#5) +-OrderByItem + | +-parse_location=156-157 | +-column_ref= | +-ColumnRef(type=STRING, column=$union_all.value1#6) +-OrderByItem + | +-parse_location=159-167 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol9#7) +-OrderByItem + | +-parse_location=169-170 | +-column_ref= | +-ColumnRef(type=INT64, column=$union_all.key1#5) +-OrderByItem + +-parse_location=172-180 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol11#8) == @@ -938,6 +1002,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +-order_by_item_list= +-OrderByItem + +-parse_location=32-39 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#3) == @@ -963,21 +1028,27 @@ QueryStmt | +-TableScan(column_list=SimpleTypes.[int32#1, int64#2, uint32#3, uint64#4, float#8, double#9], table=SimpleTypes, column_index_list=[0, 1, 2, 3, 7, 8]) +-order_by_item_list= +-OrderByItem + | +-parse_location=77-82 | +-column_ref= | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) +-OrderByItem + | +-parse_location=84-90 | +-column_ref= | +-ColumnRef(type=UINT64, column=SimpleTypes.uint64#4) +-OrderByItem + | +-parse_location=92-98 | +-column_ref= | +-ColumnRef(type=DOUBLE, column=SimpleTypes.double#9) +-OrderByItem + | +-parse_location=100-101 | +-column_ref= | +-ColumnRef(type=INT64, column=SimpleTypes.int64#2) +-OrderByItem + | +-parse_location=103-104 | +-column_ref= | +-ColumnRef(type=UINT64, column=SimpleTypes.uint64#4) +-OrderByItem + +-parse_location=106-107 +-column_ref= +-ColumnRef(type=DOUBLE, column=SimpleTypes.double#9) == @@ -996,6 +1067,7 @@ QueryStmt | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=42-45 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) == @@ -1015,6 +1087,7 @@ QueryStmt | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=42-45 +-column_ref= +-ColumnRef(type=INT32, column=TestTable.key#1) == @@ -1041,6 +1114,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=41-48 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#3) == @@ -1075,6 +1149,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=45-54 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -1099,6 +1174,7 @@ QueryStmt | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=47-50 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.key#3) == @@ -1131,6 +1207,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=61-69 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg2#5) == @@ -1160,6 +1237,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=51-59 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#4) == @@ -1194,36 +1272,47 @@ QueryStmt | +-value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + | +-parse_location=73-76 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + | +-parse_location=78-81 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + | +-parse_location=83-84 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + | +-parse_location=86-87 | +-column_ref= | +-ColumnRef(type=STRING, column=$groupby.value#4) +-OrderByItem + | +-parse_location=89-94 | +-column_ref= | +-ColumnRef(type=STRING, column=$groupby.value#4) +-OrderByItem + | +-parse_location=96-99 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + | +-parse_location=101-104 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + | +-parse_location=106-109 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + | +-parse_location=111-112 | +-column_ref= | +-ColumnRef(type=STRING, column=$groupby.value#4) +-OrderByItem + | +-parse_location=114-126 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.foo#3) +-OrderByItem + +-parse_location=128-142 +-column_ref= +-ColumnRef(type=STRING, column=$groupby.value#4) == @@ -1261,6 +1350,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=54-62 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#4) == @@ -1294,25 +1384,32 @@ QueryStmt | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=62-65 | | +-column_ref= | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) | +-OrderByItem + | | +-parse_location=67-68 | | +-column_ref= | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) | +-OrderByItem + | | +-parse_location=70-73 | | +-column_ref= | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) | +-OrderByItem + | | +-parse_location=75-76 | | +-column_ref= | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) | +-OrderByItem + | +-parse_location=78-87 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol5#3) +-order_by_item_list= +-OrderByItem + | +-parse_location=99-100 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-OrderByItem + +-parse_location=102-103 +-column_ref= +-ColumnRef(type=STRING, column=KeyValue.Value#2) == @@ -1337,6 +1434,7 @@ QueryStmt | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=43-44 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.Key#3) == @@ -1360,6 +1458,7 @@ QueryStmt | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=43-46 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.Key#3) == @@ -1389,6 +1488,7 @@ QueryStmt | +-key#4 := ColumnRef(type=INT64, column=$groupby.key#3) +-order_by_item_list= +-OrderByItem + +-parse_location=56-59 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.key#4) == @@ -1424,9 +1524,11 @@ QueryStmt | +-maxvalue#6 := ColumnRef(type=STRING, column=$aggregate.maxvalue#3) +-order_by_item_list= +-OrderByItem + | +-parse_location=80-83 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.key#5) +-OrderByItem + +-parse_location=85-86 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.maxvalue#6) == @@ -1462,12 +1564,15 @@ QueryStmt | +-maxvalue#6 := ColumnRef(type=STRING, column=$aggregate.maxvalue#3) +-order_by_item_list= +-OrderByItem + | +-parse_location=80-83 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.key#5) +-OrderByItem + | +-parse_location=85-86 | +-column_ref= | +-ColumnRef(type=STRING, column=$distinct.maxvalue#6) +-OrderByItem + +-parse_location=88-96 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.maxvalue#6) == @@ -1507,9 +1612,11 @@ QueryStmt | +-rankvalue#6 := ColumnRef(type=INT64, column=$analytic.rankvalue#4) +-order_by_item_list= +-OrderByItem + | +-parse_location=86-91 | +-column_ref= | +-ColumnRef(type=STRING, column=$distinct.Value#5) +-OrderByItem + +-parse_location=93-94 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.rankvalue#6) == @@ -1549,9 +1656,11 @@ QueryStmt | +-rankvalue#6 := ColumnRef(type=INT64, column=$analytic.rankvalue#4) +-order_by_item_list= +-OrderByItem + | +-parse_location=86-91 | +-column_ref= | +-ColumnRef(type=STRING, column=$distinct.Value#5) +-OrderByItem + +-parse_location=93-102 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.rankvalue#6) == @@ -1632,6 +1741,7 @@ QueryStmt | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + +-parse_location=48-58 +-column_ref= +-ColumnRef(type=STRING, column=$aggregate.$agg1#4) -- @@ -1669,6 +1779,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + +-parse_location=48-56 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg1#4) -- @@ -1719,6 +1830,7 @@ QueryStmt | +-output_column_list=[$distinct.Key#6] +-order_by_item_list= +-OrderByItem + +-parse_location=87-90 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.key#7) == @@ -1758,6 +1870,7 @@ QueryStmt | | | | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=46-49 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$distinct.Key#3) | | +-output_column_list=[$distinct.Key#3] @@ -1775,11 +1888,13 @@ QueryStmt | | | +-Key#6 := ColumnRef(type=INT64, column=KeyValue.Key#4) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=107-110 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$distinct.Key#6) | +-output_column_list=[$distinct.Key#6] +-order_by_item_list= +-OrderByItem + +-parse_location=121-124 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.key#7) == @@ -1823,6 +1938,7 @@ QueryStmt | | | | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=46-49 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$distinct.Key#3) | | +-output_column_list=[$distinct.Key#3] @@ -1835,6 +1951,7 @@ QueryStmt | | | | +-TableScan(column_list=[KeyValue.Key#4], table=KeyValue, column_index_list=[0]) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=103-106 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=KeyValue.Key#4) | | +-output_column_list=[KeyValue.Key#4] @@ -1852,11 +1969,13 @@ QueryStmt | | | +-Key#8 := ColumnRef(type=INT64, column=KeyValue.Key#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=169-172 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$distinct.Key#8) | +-output_column_list=[$distinct.Key#8] +-order_by_item_list= +-OrderByItem + +-parse_location=183-186 +-column_ref= +-ColumnRef(type=INT64, column=$union_distinct.key#9) == @@ -1908,9 +2027,11 @@ QueryStmt | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + | +-parse_location=43-48 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) +-OrderByItem + +-parse_location=50-51 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.Key#3) == @@ -1948,6 +2069,7 @@ QueryStmt | +-key#4 := ColumnRef(type=INT64, column=$groupby.key#3) +-order_by_item_list= +-OrderByItem + +-parse_location=59-64 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) == @@ -1985,6 +2107,7 @@ QueryStmt | +-key#4 := ColumnRef(type=INT64, column=$groupby.key#3) +-order_by_item_list= +-OrderByItem + +-parse_location=56-63 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) == @@ -2014,9 +2137,11 @@ QueryStmt | +-Value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + | +-parse_location=77-80 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + +-parse_location=82-87 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.Value#4) == @@ -2043,15 +2168,19 @@ QueryStmt | +-Value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + | +-parse_location=41-42 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + | +-parse_location=44-45 | +-column_ref= | +-ColumnRef(type=STRING, column=$distinct.Value#4) +-OrderByItem + | +-parse_location=47-50 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + +-parse_location=52-57 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.Value#4) == @@ -2089,12 +2218,15 @@ QueryStmt | +-Value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + | +-parse_location=60-61 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + | +-parse_location=63-66 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + +-parse_location=68-73 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.Value#4) == @@ -2120,12 +2252,15 @@ QueryStmt | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) +-order_by_item_list= +-OrderByItem + | +-parse_location=52-53 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + | +-parse_location=55-58 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + +-parse_location=60-72 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.Key#3) == @@ -2167,18 +2302,23 @@ QueryStmt | +-Value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) +-order_by_item_list= +-OrderByItem + | +-parse_location=85-86 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + | +-parse_location=88-91 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + | +-parse_location=93-98 | +-column_ref= | +-ColumnRef(type=STRING, column=$distinct.Value#4) +-OrderByItem + | +-parse_location=100-112 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.Key#3) +-OrderByItem + +-parse_location=114-128 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.Value#4) == @@ -2220,6 +2360,7 @@ QueryStmt | +-nested_int64#3 := ColumnRef(type=INT64, column=$query.nested_int64#2) +-order_by_item_list= +-OrderByItem + +-parse_location=78-103 +-column_ref= +-ColumnRef(type=INT64, column=$distinct.nested_int64#3) == @@ -2275,6 +2416,7 @@ QueryStmt | +-nested_int64#4 := ColumnRef(type=INT64, column=$groupby.nested_int64#3) +-order_by_item_list= +-OrderByItem + +-parse_location=113-142 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#5) == @@ -2314,6 +2456,7 @@ QueryStmt | +-output_column_list=[KeyValue.Key#3] +-order_by_item_list= +-OrderByItem + +-parse_location=69-72 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.key#5) == @@ -2356,6 +2499,7 @@ QueryStmt | +-output_column_list=[KeyValue.Key#4] +-order_by_item_list= +-OrderByItem + +-parse_location=78-81 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.key#6) == @@ -2393,6 +2537,7 @@ QueryStmt | +-output_column_list=[KeyValue.Key#3] +-order_by_item_list= +-OrderByItem + +-parse_location=85-89 +-column_ref= +-ColumnRef(type=INT64, column=$union_all.key1#5) == @@ -2460,6 +2605,7 @@ QueryStmt | +-output_column_list=[KeyValue.Key#3] +-order_by_item_list= +-OrderByItem + +-parse_location=69-78 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) == @@ -2515,6 +2661,7 @@ QueryStmt | +-output_column_list=[KeyValue.Key#3] +-order_by_item_list= +-OrderByItem + +-parse_location=69-86 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#6) == @@ -2556,6 +2703,7 @@ QueryStmt | +-TableScan(table=KeyValue) +-order_by_item_list= +-OrderByItem + +-parse_location=34-66 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) == @@ -2590,6 +2738,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-51 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#3) == @@ -2613,6 +2762,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-50 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#3) == @@ -2661,6 +2811,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.KitchenSink#5], table=TestTable, column_index_list=[2]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=74-86 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#7) +-input_scan= @@ -2718,6 +2869,7 @@ QueryStmt | | +-TableScan(column_list=[TestTable.KitchenSink#5], table=TestTable, column_index_list=[2]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=74-110 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#7) +-input_scan= @@ -2785,6 +2937,7 @@ QueryStmt | | +-output_column_list=[$union_all2.$col1#7] | +-order_by_item_list= | +-OrderByItem + | +-parse_location=101-113 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#9) +-input_scan= @@ -2796,6 +2949,8 @@ select int32 as foo from SimpleTypes order by {{foo|1|int32}} -- +ALTERNATION GROUP: foo +-- QueryStmt +-output_column_list= | +-SimpleTypes.int32#1 AS foo [INT32] @@ -2807,6 +2962,41 @@ QueryStmt | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=46-49 + +-column_ref= + +-ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-- +ALTERNATION GROUP: 1 +-- +QueryStmt ++-output_column_list= +| +-SimpleTypes.int32#1 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[SimpleTypes.int32#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=46-47 + +-column_ref= + +-ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-- +ALTERNATION GROUP: int32 +-- +QueryStmt ++-output_column_list= +| +-SimpleTypes.int32#1 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[SimpleTypes.int32#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=46-51 +-column_ref= +-ColumnRef(type=INT32, column=SimpleTypes.int32#1) == @@ -2819,13 +3009,95 @@ from SimpleTypes group by {{foo|1|int32}} order by {{foo|1|int32}} -- -ALTERNATION GROUPS: - foo,foo - foo,1 - foo,int32 - 1,foo - 1,1 - 1,int32 +ALTERNATION GROUP: foo,foo +-- +QueryStmt ++-output_column_list= +| +-$groupby.foo#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.foo#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.foo#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=59-62 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.foo#20) +-- +ALTERNATION GROUP: foo,1 +-- +QueryStmt ++-output_column_list= +| +-$groupby.foo#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.foo#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.foo#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=59-60 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.foo#20) +-- +ALTERNATION GROUP: foo,int32 +-- +QueryStmt ++-output_column_list= +| +-$groupby.foo#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.foo#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.foo#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=59-64 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.foo#20) +-- +ALTERNATION GROUP: 1,foo +-- +QueryStmt ++-output_column_list= +| +-$groupby.foo#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.foo#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.foo#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=57-60 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.foo#20) +-- +ALTERNATION GROUP: 1,1 -- QueryStmt +-output_column_list= @@ -2843,13 +3115,33 @@ QueryStmt | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-order_by_item_list= +-OrderByItem + +-parse_location=57-58 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.foo#20) -- -ALTERNATION GROUPS: - int32,foo - int32,1 - int32,int32 +ALTERNATION GROUP: 1,int32 +-- +QueryStmt ++-output_column_list= +| +-$groupby.foo#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.foo#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.foo#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=57-62 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.foo#20) +-- +ALTERNATION GROUP: int32,foo -- QueryStmt +-output_column_list= @@ -2867,6 +3159,51 @@ QueryStmt | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-order_by_item_list= +-OrderByItem + +-parse_location=61-64 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32#20) +-- +ALTERNATION GROUP: int32,1 +-- +QueryStmt ++-output_column_list= +| +-$groupby.int32#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.int32#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int32#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=61-62 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32#20) +-- +ALTERNATION GROUP: int32,int32 +-- +QueryStmt ++-output_column_list= +| +-$groupby.int32#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.int32#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int32#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=61-66 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.int32#20) == @@ -2876,6 +3213,30 @@ select distinct int32 as foo from SimpleTypes order by {{foo|1|int32}} -- +ALTERNATION GROUP: foo +-- +QueryStmt ++-output_column_list= +| +-$distinct.int32#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.int32#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.int32#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=55-58 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.int32#20) +-- +ALTERNATION GROUP: 1 +-- QueryStmt +-output_column_list= | +-$distinct.int32#20 AS foo [INT32] @@ -2892,6 +3253,29 @@ QueryStmt | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) +-order_by_item_list= +-OrderByItem + +-parse_location=55-56 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.int32#20) +-- +ALTERNATION GROUP: int32 +-- +QueryStmt ++-output_column_list= +| +-$distinct.int32#20 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.int32#20] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.int32#20] + | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | +-group_by_list= + | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + +-order_by_item_list= + +-OrderByItem + +-parse_location=55-60 +-column_ref= +-ColumnRef(type=INT32, column=$distinct.int32#20) == @@ -2904,13 +3288,7 @@ from SimpleTypes group by {{foo|1|int32}} order by {{foo|1|int32}} -- -ALTERNATION GROUPS: - foo,foo - foo,1 - foo,int32 - 1,foo - 1,1 - 1,int32 +ALTERNATION GROUP: foo,foo -- QueryStmt +-output_column_list= @@ -2933,13 +3311,200 @@ QueryStmt | +-foo#21 := ColumnRef(type=INT32, column=$groupby.foo#20) +-order_by_item_list= +-OrderByItem + +-parse_location=68-71 +-column_ref= +-ColumnRef(type=INT32, column=$distinct.foo#21) -- -ALTERNATION GROUPS: - int32,foo - int32,1 - int32,int32 +ALTERNATION GROUP: foo,1 +-- +QueryStmt ++-output_column_list= +| +-$distinct.foo#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.foo#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.foo#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.foo#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-foo#21 := ColumnRef(type=INT32, column=$groupby.foo#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=68-69 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.foo#21) +-- +ALTERNATION GROUP: foo,int32 +-- +QueryStmt ++-output_column_list= +| +-$distinct.foo#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.foo#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.foo#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.foo#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-foo#21 := ColumnRef(type=INT32, column=$groupby.foo#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=68-73 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.foo#21) +-- +ALTERNATION GROUP: 1,foo +-- +QueryStmt ++-output_column_list= +| +-$distinct.foo#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.foo#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.foo#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.foo#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-foo#21 := ColumnRef(type=INT32, column=$groupby.foo#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-69 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.foo#21) +-- +ALTERNATION GROUP: 1,1 +-- +QueryStmt ++-output_column_list= +| +-$distinct.foo#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.foo#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.foo#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.foo#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-foo#21 := ColumnRef(type=INT32, column=$groupby.foo#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-67 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.foo#21) +-- +ALTERNATION GROUP: 1,int32 +-- +QueryStmt ++-output_column_list= +| +-$distinct.foo#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.foo#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.foo#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.foo#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-foo#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-foo#21 := ColumnRef(type=INT32, column=$groupby.foo#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-71 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.foo#21) +-- +ALTERNATION GROUP: int32,foo +-- +QueryStmt ++-output_column_list= +| +-$distinct.int32#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.int32#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.int32#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-int32#21 := ColumnRef(type=INT32, column=$groupby.int32#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=70-73 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.int32#21) +-- +ALTERNATION GROUP: int32,1 +-- +QueryStmt ++-output_column_list= +| +-$distinct.int32#21 AS foo [INT32] ++-query= + +-OrderByScan + +-column_list=[$distinct.int32#21] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$distinct.int32#21] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32#20] + | | +-input_scan= + | | | +-TableScan(column_list=[SimpleTypes.int32#1], table=SimpleTypes, column_index_list=[0]) + | | +-group_by_list= + | | +-int32#20 := ColumnRef(type=INT32, column=SimpleTypes.int32#1) + | +-group_by_list= + | +-int32#21 := ColumnRef(type=INT32, column=$groupby.int32#20) + +-order_by_item_list= + +-OrderByItem + +-parse_location=70-71 + +-column_ref= + +-ColumnRef(type=INT32, column=$distinct.int32#21) +-- +ALTERNATION GROUP: int32,int32 -- QueryStmt +-output_column_list= @@ -2962,6 +3527,7 @@ QueryStmt | +-int32#21 := ColumnRef(type=INT32, column=$groupby.int32#20) +-order_by_item_list= +-OrderByItem + +-parse_location=70-75 +-column_ref= +-ColumnRef(type=INT32, column=$distinct.int32#21) == @@ -3042,6 +3608,7 @@ QueryStmt | +-field_idx=0 +-order_by_item_list= +-OrderByItem + +-parse_location=109-113 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.b#3) == @@ -3111,6 +3678,7 @@ QueryStmt | +-TestStruct#7 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=65-94 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#8) == @@ -3161,6 +3729,7 @@ QueryStmt | +-TestStruct#9 := ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) +-order_by_item_list= +-OrderByItem + +-parse_location=99-114 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#10) -- @@ -3199,6 +3768,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-49 +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-null_order=NULLS_FIRST @@ -3216,6 +3786,7 @@ QueryStmt | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) +-order_by_item_list= +-OrderByItem + +-parse_location=34-48 +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-null_order=NULLS_LAST diff --git a/zetasql/analyzer/testdata/pipe_aggregate.test b/zetasql/analyzer/testdata/pipe_aggregate.test new file mode 100644 index 000000000..70e9f2cee --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_aggregate.test @@ -0,0 +1,1517 @@ +[default language_features=PIPES,GROUP_BY_ROLLUP,V_1_4_GROUPING_SETS,V_1_4_GROUPING_BUILTIN,V_1_1_SELECT_STAR_EXCEPT_REPLACE] +select 1 x, 2 y +|> AGGREGATE GROUP BY x, y +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#3 AS x [INT64] +| +-$groupby.y#4 AS y [INT64] ++-query= + +-AggregateScan + +-column_list=$groupby.[x#3, y#4] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-x#3 := ColumnRef(type=INT64, column=$query.x#1) + +-y#4 := ColumnRef(type=INT64, column=$query.y#2) +== + +# We get an output column for each item in GROUP BY, even when they +# are duplicate expressions, and even when they get de-duplicated +# in group_by_list. +select 1 x, 2 y, struct(3 AS z) st +|> AGGREGATE GROUP BY x, y, x, x, st.z, (st).z, 1+x, 2+x, 1+x +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.y#5 AS y [INT64] +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.z#6 AS z [INT64] +| +-$groupby.z#6 AS z [INT64] +| +-$groupby.$groupbycol4#7 AS `$groupbycol4` [INT64] +| +-$groupby.$groupbycol5#8 AS `$groupbycol5` [INT64] +| +-$groupby.$groupbycol6#9 AS `$groupbycol6` [INT64] ++-query= + +-AggregateScan + +-column_list=$groupby.[x#4, y#5, z#6, $groupbycol4#7, $groupbycol5#8, $groupbycol6#9] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, st#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-st#3 := Literal(type=STRUCT, value={z:3}) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-x#4 := ColumnRef(type=INT64, column=$query.x#1) + +-y#5 := ColumnRef(type=INT64, column=$query.y#2) + +-z#6 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$query.st#3) + | +-field_idx=0 + +-$groupbycol4#7 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Literal(type=INT64, value=1) + | +-ColumnRef(type=INT64, column=$query.x#1) + +-$groupbycol5#8 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Literal(type=INT64, value=2) + | +-ColumnRef(type=INT64, column=$query.x#1) + +-$groupbycol6#9 := + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-Literal(type=INT64, value=1) + +-ColumnRef(type=INT64, column=$query.x#1) +== + +# We get an output column for each item in GROUP BY, even when they +# are duplicate expressions, and even when they get de-duplicated +# in group_by_list, including when x and t.x are the same column. +FROM (select 1 x, 2 y) AS t +|> AGGREGATE SUM(y), SUM(t.y) GROUP BY x, t.x +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#5 AS x [INT64] +| +-$groupby.x#5 AS x [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] +| +-$aggregate.$agg2#4 AS `$col2` [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#5, $aggregate.$agg1#3, $aggregate.$agg2#4] + +-input_scan= + | +-ProjectScan + | +-column_list=t.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#5 := ColumnRef(type=INT64, column=t.x#1) + +-aggregate_list= + +-$agg1#3 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=t.y#2) + +-$agg2#4 := + +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-ColumnRef(type=INT64, column=t.y#2) +== + +select 1 x, 2 y, 3 z +|> AGGREGATE +-- +ERROR: Pipe AGGREGATE cannot have both an empty aggregate list and an empty GROUP BY [at 2:4] +|> AGGREGATE + ^ +== + +select 1 x, 2 y, 3 z +|> AGGREGATE GROUP BY () +-- +ERROR: Pipe AGGREGATE cannot have both an empty aggregate list and an empty GROUP BY [at 2:4] +|> AGGREGATE GROUP BY () + ^ +== + +select 1 x, 2 y, 3 z +|> AGGREGATE GROUP BY (), () +-- +ERROR: Pipe AGGREGATE cannot have both an empty aggregate list and an empty GROUP BY [at 2:4] +|> AGGREGATE GROUP BY (), () + ^ +== + +select 1 x, 2 y, 3 z +|> AGGREGATE GROUP BY x, (), y +-- +ERROR: GROUP BY () is only allowed when there are no other grouping items [at 2:26] +|> AGGREGATE GROUP BY x, (), y + ^ +== + +# Group by computed expressions. +# Aliases are inferred for expressions where possible. +select struct(1 AS x) s, 2 y, 3 z +|> AGGREGATE GROUP BY s.x, y+1, cast(y as string), struct(y).y +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.$groupbycol2#5 AS `$groupbycol2` [INT64] +| +-$groupby.$groupbycol3#6 AS `$groupbycol3` [STRING] +| +-$groupby.y#7 AS y [INT64] ++-query= + +-AggregateScan + +-column_list=$groupby.[x#4, $groupbycol2#5, $groupbycol3#6, y#7] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[s#1, y#2, z#3] + | +-expr_list= + | | +-s#1 := Literal(type=STRUCT, value={x:1}) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-x#4 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$query.s#1) + | +-field_idx=0 + +-$groupbycol2#5 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-Literal(type=INT64, value=1) + +-$groupbycol3#6 := + | +-Cast(INT64 -> STRING) + | +-ColumnRef(type=INT64, column=$query.y#2) + +-y#7 := + +-GetStructField + +-type=INT64 + +-expr= + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$query.y#2) + +-field_idx=0 +== + +# Group by an expression on columns, some of which (y) aren't already grouped. +select 1 x, 2 y, 3 z +|> AGGREGATE GROUP BY x, x+y +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.$groupbycol2#5 AS `$groupbycol2` [INT64] ++-query= + +-AggregateScan + +-column_list=$groupby.[x#4, $groupbycol2#5] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-x#4 := ColumnRef(type=INT64, column=$query.x#1) + +-$groupbycol2#5 := + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.x#1) + +-ColumnRef(type=INT64, column=$query.y#2) +== + +# Group by an expression on columns, all of which are already grouped. +select 1 x, 2 y, 3 z +|> AGGREGATE GROUP BY x, y, x+y +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.y#5 AS y [INT64] +| +-$groupby.$groupbycol3#6 AS `$groupbycol3` [INT64] ++-query= + +-AggregateScan + +-column_list=$groupby.[x#4, y#5, $groupbycol3#6] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-x#4 := ColumnRef(type=INT64, column=$query.x#1) + +-y#5 := ColumnRef(type=INT64, column=$query.y#2) + +-$groupbycol3#6 := + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.x#1) + +-ColumnRef(type=INT64, column=$query.y#2) +== + +select 1 x, 2 y, 3 z +|> AGGREGATE GROUP BY 1,2 +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 2:23] +|> AGGREGATE GROUP BY 1,2 + ^ +== + +# Aggregates with empty group by. +select 1 x, 2 y +|> AGGREGATE count(*), sum(y) GROUP BY () +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [INT64] +| +-$aggregate.$agg2#4 AS `$col2` [INT64] ++-query= + +-AggregateScan + +-column_list=$aggregate.[$agg1#3, $agg2#4] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-aggregate_list= + +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-$agg2#4 := + +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.y#2) +== + +# Aggregates with no group by. +select 1 x, 2 y +|> AGGREGATE count(*), sum(y) +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [INT64] +| +-$aggregate.$agg2#4 AS `$col2` [INT64] ++-query= + +-AggregateScan + +-column_list=$aggregate.[$agg1#3, $agg2#4] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-aggregate_list= + +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-$agg2#4 := + +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.y#2) +== + +# Aggregates with expressions inside and outside the aggregate functions. +select 1 x, 2 y +|> AGGREGATE avg(x), 1+sum(y+2), sum(y)/count(x) +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [DOUBLE] +| +-$aggregate.$col2#7 AS `$col2` [INT64] +| +-$aggregate.$col3#8 AS `$col3` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#3, $col2#7, $col3#8] + +-expr_list= + | +-$col2#7 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=INT64, column=$aggregate.$agg2#4) + | +-$col3#8 := + | +-FunctionCall(ZetaSQL:$divide(DOUBLE, DOUBLE) -> DOUBLE) + | +-Cast(INT64 -> DOUBLE) + | | +-ColumnRef(type=INT64, column=$aggregate.$agg3#5) + | +-Cast(INT64 -> DOUBLE) + | +-ColumnRef(type=INT64, column=$aggregate.$agg4#6) + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#3, $agg2#4, $agg3#5, $agg4#6] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-aggregate_list= + +-$agg1#3 := + | +-AggregateFunctionCall(ZetaSQL:avg(INT64) -> DOUBLE) + | +-ColumnRef(type=INT64, column=$query.x#1) + +-$agg2#4 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-Literal(type=INT64, value=2) + +-$agg3#5 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + +-$agg4#6 := + +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.x#1) +== + + +# Aliases in aggregate list, including inferred aliases. +select 1 x, struct(1 as f) sf +|> AGGREGATE count(*) cnt, sum(x) AS xx, any_value(sf).f +-- +QueryStmt ++-output_column_list= +| +-$aggregate.cnt#3 AS cnt [INT64] +| +-$aggregate.xx#4 AS xx [INT64] +| +-$aggregate.f#6 AS f [INT64] ++-query= + +-ProjectScan + +-column_list=$aggregate.[cnt#3, xx#4, f#6] + +-expr_list= + | +-f#6 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$aggregate.$agg3#5) + | +-field_idx=0 + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[cnt#3, xx#4, $agg3#5] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, sf#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-sf#2 := Literal(type=STRUCT, value={f:1}) + | +-input_scan= + | +-SingleRowScan + +-aggregate_list= + +-cnt#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-xx#4 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + +-$agg3#5 := + +-AggregateFunctionCall(ZetaSQL:any_value(STRUCT) -> STRUCT) + +-ColumnRef(type=STRUCT, column=$query.sf#2) +== + +# Aggregates and grouping with computed expressions in both. +select 1 x, 2 y +|> AGGREGATE 1+sum(x+2) GROUP BY y+2 +-- +QueryStmt ++-output_column_list= +| +-$groupby.$groupbycol1#4 AS `$groupbycol1` [INT64] +| +-$aggregate.$col1#5 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.$groupbycol1#4, $aggregate.$col1#5] + +-expr_list= + | +-$col1#5 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Literal(type=INT64, value=1) + | +-ColumnRef(type=INT64, column=$aggregate.$agg1#3) + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.$groupbycol1#4, $aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-$groupbycol1#4 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-Literal(type=INT64, value=2) + +-aggregate_list= + +-$agg1#3 := + +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.x#1) + +-Literal(type=INT64, value=2) +== + +# Dot-star on the output of an aggregate, and with modifiers. +select 1 x, struct(1 as f, 2 as g) sf +|> AGGREGATE any_value(sf).*, + any_value(sf).* except (f), + any_value(sf).* replace ('abc' as g), + any_value(sf).* except (f) replace (max(sf.g) as g), +-- +QueryStmt ++-output_column_list= +| +-$aggregate.f#8 AS f [INT64] +| +-$aggregate.g#9 AS g [INT64] +| +-$aggregate.g#10 AS g [INT64] +| +-$aggregate.f#11 AS f [INT64] +| +-$aggregate.g#12 AS g [STRING] +| +-$aggregate.g#7 AS g [INT64] ++-query= + +-ProjectScan + +-column_list=$aggregate.[f#8, g#9, g#10, f#11, g#12, g#7] + +-expr_list= + | +-f#8 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$aggregate.$agg1#3) + | | +-field_idx=0 + | +-g#9 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$aggregate.$agg1#3) + | | +-field_idx=1 + | +-g#10 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$aggregate.$agg2#4) + | | +-field_idx=1 + | +-f#11 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$aggregate.$agg3#5) + | | +-field_idx=0 + | +-g#12 := Literal(type=STRING, value="abc") + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#3, $agg2#4, $agg3#5, $agg4#6, g#7] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, sf#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-sf#2 := Literal(type=STRUCT, value={f:1, g:2}) + | +-input_scan= + | +-SingleRowScan + +-aggregate_list= + +-$agg1#3 := + | +-AggregateFunctionCall(ZetaSQL:any_value(STRUCT) -> STRUCT) + | +-ColumnRef(type=STRUCT, column=$query.sf#2) + +-$agg2#4 := + | +-AggregateFunctionCall(ZetaSQL:any_value(STRUCT) -> STRUCT) + | +-ColumnRef(type=STRUCT, column=$query.sf#2) + +-$agg3#5 := + | +-AggregateFunctionCall(ZetaSQL:any_value(STRUCT) -> STRUCT) + | +-ColumnRef(type=STRUCT, column=$query.sf#2) + +-$agg4#6 := + | +-AggregateFunctionCall(ZetaSQL:any_value(STRUCT) -> STRUCT) + | +-ColumnRef(type=STRUCT, column=$query.sf#2) + +-g#7 := + +-AggregateFunctionCall(ZetaSQL:max(INT64) -> INT64) + +-GetStructField + +-type=INT64 + +-expr= + | +-ColumnRef(type=STRUCT, column=$query.sf#2) + +-field_idx=1 +== + +# The usual errors for dot-star apply here. +select 1 x +|> AGGREGATE max(x).* +-- +ERROR: Dot-star is not supported for type INT64 [at 2:14] +|> AGGREGATE max(x).* + ^ +== + +select struct(1 as f, 2 as g) sf +|> AGGREGATE any_value(sf).* except (k) +-- +ERROR: Column k in SELECT * EXCEPT list does not exist [at 2:38] +|> AGGREGATE any_value(sf).* except (k) + ^ +== + +select struct(1 as f, 2 as g) sf +|> AGGREGATE any_value(sf).* replace (123 as k) +-- +ERROR: Column k in SELECT * REPLACE list does not exist [at 2:46] +|> AGGREGATE any_value(sf).* replace (123 as k) + ^ +== + +select struct(1 as f, 2 as g) sf +|> AGGREGATE any_value(sf).* except (f,g) +-- +ERROR: SELECT * expands to zero columns after applying EXCEPT [at 2:14] +|> AGGREGATE any_value(sf).* except (f,g) + ^ +== + +select struct() sf +|> AGGREGATE any_value(sf).* +-- +ERROR: Star expansion is not allowed on a struct with zero fields [at 2:14] +|> AGGREGATE any_value(sf).* + ^ +== + +select struct(1 as f, 2 as g) sf +|> AGGREGATE sf.* +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:14] +|> AGGREGATE sf.* + ^ +== + +# REPLACE expression cannot be a non-aggregate expression. +select struct(1 as f, 2 as g) sf +|> AGGREGATE any_value(sf).* replace (1+sf.g as f) +-- +ERROR: AGGREGATE list expression references sf.g which is not aggregated [at 2:41] +|> AGGREGATE any_value(sf).* replace (1+sf.g as f) + ^ +== + +# Dot-star on a proto value table. +from TestExtraValueTable vt +|> AGGREGATE any_value(vt).* {{|except(int32_val2)}} +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$aggregate.int32_val1#5 AS int32_val1 [INT32] +| +-$aggregate.int32_val2#6 AS int32_val2 [INT32] +| +-$aggregate.str_value#7 AS str_value [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[int32_val1#5, int32_val2#6, str_value#7] + +-expr_list= + | +-int32_val1#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$aggregate.$agg1#4) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-int32_val2#6 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$aggregate.$agg1#4) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-str_value#7 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=$aggregate.$agg1#4) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + +-aggregate_list= + +-$agg1#4 := + +-AggregateFunctionCall(ZetaSQL:any_value(PROTO) -> PROTO) + +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) +-- +ALTERNATION GROUP: except(int32_val2) +-- +QueryStmt ++-output_column_list= +| +-$aggregate.int32_val1#5 AS int32_val1 [INT32] +| +-$aggregate.str_value#6 AS str_value [ARRAY] ++-query= + +-ProjectScan + +-column_list=$aggregate.[int32_val1#5, str_value#6] + +-expr_list= + | +-int32_val1#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$aggregate.$agg1#4) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-str_value#6 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=$aggregate.$agg1#4) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + +-aggregate_list= + +-$agg1#4 := + +-AggregateFunctionCall(ZetaSQL:any_value(PROTO) -> PROTO) + +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) +== + +# GROUP BY ALL is not allowed. +# This is currently caught by the parser, but there is still an +# analyzer error for this, since parser implementation details may +# change to allow ALL through (which would give a better error). +select 1 +|> AGGREGATE COUNT(*) GROUP BY ALL +-- +ERROR: Syntax error: Unexpected keyword ALL [at 2:32] +|> AGGREGATE COUNT(*) GROUP BY ALL + ^ +== + +# Non-aggregate expressions not allowed. +select 1 x, 2 y +|> AGGREGATE sum(x), x{{| GROUP BY y}} +-- +ALTERNATION GROUP: +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:22] +|> AGGREGATE sum(x), x + ^ +-- +ALTERNATION GROUP: GROUP BY y +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:22] +|> AGGREGATE sum(x), x GROUP BY y + ^ +== + +# This error points at the whole expression, not the referenced column. +# It's not obvious that pointing at the column would be better. +# Note the more complex case with sum(x)+y below, where it is aggregate. +select 1 x, 2 y +|> AGGREGATE 1+x{{| GROUP BY y}} +-- +ALTERNATION GROUP: +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:14] +|> AGGREGATE 1+x + ^ +-- +ALTERNATION GROUP: GROUP BY y +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:14] +|> AGGREGATE 1+x GROUP BY y + ^ +== + +select 1 x +|> AGGREGATE ANY_VALUE(5), sqrt(15){{| GROUP BY y}} +-- +ALTERNATION GROUP: +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:28] +|> AGGREGATE ANY_VALUE(5), sqrt(15) + ^ +-- +ALTERNATION GROUP: GROUP BY y +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:28] +|> AGGREGATE ANY_VALUE(5), sqrt(15) GROUP BY y + ^ +== + +select 1 x +|> AGGREGATE x GROUP BY x +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:14] +|> AGGREGATE x GROUP BY x + ^ +== + +select 1 x, 2 y, 3 z +|> AGGREGATE sum(x)+y{{| GROUP BY z}} +-- +ALTERNATION GROUP: +-- +ERROR: AGGREGATE list expression references column y which is not aggregated [at 2:21] +|> AGGREGATE sum(x)+y + ^ +-- +ALTERNATION GROUP: GROUP BY z +-- +ERROR: AGGREGATE list expression references column y which is not aggregated [at 2:21] +|> AGGREGATE sum(x)+y GROUP BY z + ^ +== + +select 1 x +|> AGGREGATE GROUP BY rand() +-- +QueryStmt ++-output_column_list= +| +-$groupby.$groupbycol1#2 AS `$groupbycol1` [DOUBLE] ++-query= + +-AggregateScan + +-column_list=[$groupby.$groupbycol1#2] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-$groupbycol1#2 := FunctionCall(ZetaSQL:rand() -> DOUBLE) +== + +# Window functions not allowed. +[language_features=PIPES,ANALYTIC_FUNCTIONS] +select 1 x +|> AGGREGATE sum(x), 1 + sum(x) OVER () +-- +ERROR: Analytic function not allowed in pipe AGGREGATE [at 2:26] +|> AGGREGATE sum(x), 1 + sum(x) OVER () + ^ +== + +# Hints in the GROUP BY make it into the AggregateScan. +select 1 x +|> AGGREGATE GROUP @{hint=1} BY x +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#2 AS x [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#2] + +-hint_list= + | +-hint := Literal(type=INT64, value=1) + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-x#2 := ColumnRef(type=INT64, column=$query.x#1) +== + +select 1 x, 2 y +|> AGGREGATE COUNT(*) GROUP BY ROLLUP(x,y) +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.y#5 AS y [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#4, $groupby.y#5, $aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#4 := ColumnRef(type=INT64, column=$query.x#1) + | +-y#5 := ColumnRef(type=INT64, column=$query.y#2) + +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-grouping_set_list= + +-Rollup + +-rollup_column_list= + +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.x#4) + +-GroupingSetMultiColumn + +-column_list= + +-ColumnRef(type=INT64, column=$groupby.y#5) +== + +# ROLLUP with duplicate columns and with expressions. +# We get an output column for each unique item in the list. +select 1 x, 2 y +|> AGGREGATE COUNT(*) GROUP BY ROLLUP(x,x,y,x,x+y,x+y) +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.y#5 AS y [INT64] +| +-$groupby.$groupbycol3#6 AS `$groupbycol3` [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#4, $groupby.y#5, $groupby.$groupbycol3#6, $aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#4 := ColumnRef(type=INT64, column=$query.x#1) + | +-y#5 := ColumnRef(type=INT64, column=$query.y#2) + | +-$groupbycol3#6 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-ColumnRef(type=INT64, column=$query.y#2) + +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-grouping_set_list= + +-Rollup + +-rollup_column_list= + +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.x#4) + +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.x#4) + +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.y#5) + +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.x#4) + +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol3#6) + +-GroupingSetMultiColumn + +-column_list= + +-ColumnRef(type=INT64, column=$groupby.$groupbycol3#6) +== + +# For GROUPING SETS, we get an output column for each unique item. +select 1 x, 2 y +|> AGGREGATE COUNT(*) GROUP BY GROUPING SETS((), (x,y), (x), (x)) +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#4 AS x [INT64] +| +-$groupby.y#5 AS y [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#4, $groupby.y#5, $aggregate.$agg1#3] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#4 := ColumnRef(type=INT64, column=$query.x#1) + | +-y#5 := ColumnRef(type=INT64, column=$query.y#2) + +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-grouping_set_list= + +-GroupingSet + +-GroupingSet + | +-group_by_column_list= + | +-ColumnRef(type=INT64, column=$groupby.x#4) + | +-ColumnRef(type=INT64, column=$groupby.y#5) + +-GroupingSet + | +-group_by_column_list= + | +-ColumnRef(type=INT64, column=$groupby.x#4) + +-GroupingSet + +-group_by_column_list= + +-ColumnRef(type=INT64, column=$groupby.x#4) +== + +# This shows a complex GROUPING SET, and the GROUPING function. +select 1 x, 2 y, 3 z +|> AGGREGATE COUNT(*), GROUPING(x), GROUPING(x) AS g_x + GROUP BY GROUPING SETS((x), ROLLUP(y,z), CUBE(x, z)) +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#7 AS x [INT64] +| +-$groupby.y#8 AS y [INT64] +| +-$groupby.z#9 AS z [INT64] +| +-$aggregate.$agg1#4 AS `$col1` [INT64] +| +-$grouping_call.$grouping_call1#10 AS `$col2` [INT64] +| +-$grouping_call.$grouping_call2#11 AS g_x [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#7, $groupby.y#8, $groupby.z#9, $aggregate.$agg1#4, $grouping_call.$grouping_call1#10, $grouping_call.$grouping_call2#11] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#7 := ColumnRef(type=INT64, column=$query.x#1) + | +-y#8 := ColumnRef(type=INT64, column=$query.y#2) + | +-z#9 := ColumnRef(type=INT64, column=$query.z#3) + +-aggregate_list= + | +-$agg1#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-grouping_set_list= + | +-GroupingSet + | | +-group_by_column_list= + | | +-ColumnRef(type=INT64, column=$groupby.x#7) + | +-Rollup + | | +-rollup_column_list= + | | +-GroupingSetMultiColumn + | | | +-column_list= + | | | +-ColumnRef(type=INT64, column=$groupby.y#8) + | | +-GroupingSetMultiColumn + | | +-column_list= + | | +-ColumnRef(type=INT64, column=$groupby.z#9) + | +-Cube + | +-cube_column_list= + | +-GroupingSetMultiColumn + | | +-column_list= + | | +-ColumnRef(type=INT64, column=$groupby.x#7) + | +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=INT64, column=$groupby.z#9) + +-grouping_call_list= + +-GroupingCall + | +-group_by_column= + | | +-ColumnRef(type=INT64, column=$groupby.x#7) + | +-output_column=$grouping_call.$grouping_call1#10 + +-GroupingCall + +-group_by_column= + | +-ColumnRef(type=INT64, column=$groupby.x#7) + +-output_column=$grouping_call.$grouping_call2#11 +== + +# Ordinals disallowed in ROLLUP +select 1 x, 2 y +|> AGGREGATE COUNT(*) GROUP BY ROLLUP(x,2) +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 2:41] +|> AGGREGATE COUNT(*) GROUP BY ROLLUP(x,2) + ^ +== + +# Ordinals disallowed in all forms in GROUPING SETS +select 1 x, 2 y +|> AGGREGATE COUNT(*) GROUP BY GROUPING SETS( + {{1|(x,1)|CUBE(x,1)|ROLLUP(x,1)}} + ) +-- +ALTERNATION GROUP: 1 +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 3:6] + 1 + ^ +-- +ALTERNATION GROUP: (x,1) +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 3:9] + (x,1) + ^ +-- +ALTERNATION GROUP: CUBE(x,1) +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 3:13] + CUBE(x,1) + ^ +-- +ALTERNATION GROUP: ROLLUP(x,1) +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 3:15] + ROLLUP(x,1) + ^ +== + +select 1 x +|> AGGREGATE GROUP BY "abc" +-- +ERROR: Cannot GROUP BY literal values [at 2:23] +|> AGGREGATE GROUP BY "abc" + ^ +== + +# Scoping: aggregate and group by expressions are resolving independently +# and not matched against each other. +select 1 x +|> AGGREGATE COUNT(x) GROUP BY x +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#3 AS x [INT64] +| +-$aggregate.$agg1#2 AS `$col1` [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.x#3, $aggregate.$agg1#2] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#3 := ColumnRef(type=INT64, column=$query.x#1) + +-aggregate_list= + +-$agg1#2 := + +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.x#1) +== + +# Scoping: column referenced unaggregated in aggregate clause won't find the +# group by column. +select 1 x +|> AGGREGATE x GROUP BY x +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:14] +|> AGGREGATE x GROUP BY x + ^ +== + +# Scoping: GROUP BY can't see a name introduced in aggregate list. +select 1 x +|> AGGREGATE COUNT(x) AS y GROUP BY y +-- +ERROR: Unrecognized name: y [at 2:37] +|> AGGREGATE COUNT(x) AS y GROUP BY y + ^ +== + +# Scoping: AGGREGATE list can't see a name introduced in GROUP BY. +select 1 x +|> AGGREGATE {{y|COUNT(y)}} GROUP BY STRUCT(2 AS y).y +-- +ALTERNATION GROUP: y +-- +ERROR: Unrecognized name: y [at 2:14] +|> AGGREGATE y GROUP BY STRUCT(2 AS y).y + ^ +-- +ALTERNATION GROUP: COUNT(y) +-- +ERROR: Unrecognized name: y [at 2:20] +|> AGGREGATE COUNT(y) GROUP BY STRUCT(2 AS y).y + ^ +== + +# Scoping: mixed aggregate and non-aggregate reference in the same expression, +# referencing columns that may also be grouped. +# (Including sum(x) bypasses check for non-aggregate expression.) +# These should all be errors. +select 1 x,2 y +|> AGGREGATE sum(x)+{{x|y}} GROUP BY {{x|y}} +-- +ALTERNATION GROUP: x,x +-- +ERROR: AGGREGATE list expression references column x which is not aggregated [at 2:21] +|> AGGREGATE sum(x)+x GROUP BY x + ^ +-- +ALTERNATION GROUP: x,y +-- +ERROR: AGGREGATE list expression references column x which is not aggregated [at 2:21] +|> AGGREGATE sum(x)+x GROUP BY y + ^ +-- +ALTERNATION GROUP: y,x +-- +ERROR: AGGREGATE list expression references column y which is not aggregated [at 2:21] +|> AGGREGATE sum(x)+y GROUP BY x + ^ +-- +ALTERNATION GROUP: y,y +-- +ERROR: AGGREGATE list expression references column y which is not aggregated [at 2:21] +|> AGGREGATE sum(x)+y GROUP BY y + ^ +== + +select 1 x, 2 y +|> AGGREGATE sum(x+1),x+1 GROUP BY x+1 +-- +ERROR: Pipe AGGREGATE cannot include non-aggregate expressions [at 2:23] +|> AGGREGATE sum(x+1),x+1 GROUP BY x+1 + ^ +== + +select 1 x +|> AGGREGATE sum(x) AS sum_x, sum(sum_x) +-- +ERROR: Unrecognized name: sum_x [at 2:35] +|> AGGREGATE sum(x) AS sum_x, sum(sum_x) + ^ +== + +select 1 x +|> AGGREGATE sum(x) AS sum_x, sum_x +-- +ERROR: Unrecognized name: sum_x [at 2:31] +|> AGGREGATE sum(x) AS sum_x, sum_x + ^ +== + +select 1 x +|> AGGREGATE sum(sum(x)) AS sum_x +-- +ERROR: Aggregations of aggregations are not allowed [at 2:14] +|> AGGREGATE sum(sum(x)) AS sum_x + ^ +== + +# Test the output NameScope. SELECT * shows the anonymous columns too. +select 1 x, 2 y, 3 z +|> AGGREGATE count(*), sum(x), sum(y) sum_y + GROUP BY x,z,z+1 +|> WHERE x=1 +|> WHERE sum_y=2 +|> SELECT * +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#7 AS x [INT64] +| +-$groupby.z#8 AS z [INT64] +| +-$groupby.$groupbycol3#9 AS `$groupbycol3` [INT64] +| +-$aggregate.$agg1#4 AS `$col1` [INT64] +| +-$aggregate.$agg2#5 AS `$col2` [INT64] +| +-$aggregate.sum_y#6 AS sum_y [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.x#7, $groupby.z#8, $groupby.$groupbycol3#9, $aggregate.$agg1#4, $aggregate.$agg2#5, $aggregate.sum_y#6] + +-input_scan= + +-FilterScan + +-column_list=[$groupby.x#7, $groupby.z#8, $groupby.$groupbycol3#9, $aggregate.$agg1#4, $aggregate.$agg2#5, $aggregate.sum_y#6] + +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.x#7, $groupby.z#8, $groupby.$groupbycol3#9, $aggregate.$agg1#4, $aggregate.$agg2#5, $aggregate.sum_y#6] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.x#7, $groupby.z#8, $groupby.$groupbycol3#9, $aggregate.$agg1#4, $aggregate.$agg2#5, $aggregate.sum_y#6] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$query.[x#1, y#2, z#3] + | | | +-expr_list= + | | | | +-x#1 := Literal(type=INT64, value=1) + | | | | +-y#2 := Literal(type=INT64, value=2) + | | | | +-z#3 := Literal(type=INT64, value=3) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-group_by_list= + | | | +-x#7 := ColumnRef(type=INT64, column=$query.x#1) + | | | +-z#8 := ColumnRef(type=INT64, column=$query.z#3) + | | | +-$groupbycol3#9 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$query.z#3) + | | | +-Literal(type=INT64, value=1) + | | +-aggregate_list= + | | +-$agg1#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + | | +-$agg2#5 := + | | | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$query.x#1) + | | +-sum_y#6 := + | | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.y#2) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$groupby.x#7) + | +-Literal(type=INT64, value=1) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$aggregate.sum_y#6) + +-Literal(type=INT64, value=2) +== + +# Ambiguous name from duplicate GROUP BY item +select 1 x, 3 z +|> AGGREGATE count(*) + GROUP BY x,z,x +|> WHERE x=1 +-- +ERROR: Column name x is ambiguous [at 4:10] +|> WHERE x=1 + ^ +== + +# Ambiguous name from two different GROUP BY columns with the same inferred +# alias. +select struct(1 AS x) s1, struct(2.0 AS x) s2 +|> AGGREGATE + GROUP BY s1.x, s2.x +|> WHERE x IS NOT NULL +-- +ERROR: Column name x is ambiguous [at 4:10] +|> WHERE x IS NOT NULL + ^ +== + +# Ambiguous name from AGGREGATE list vs GROUP BY +select 1 x, 2 y, 3 z +|> AGGREGATE count(*) AS c, sum(x) AS x + GROUP BY x +|> WHERE c=1 +|> WHERE x=1 +-- +ERROR: Column name x is ambiguous [at 5:10] +|> WHERE x=1 + ^ +== + +# Names coming from GROUPING SETS are de-duplicated and not ambiguous +select 1 x, '2' y, 3.0 z +|> AGGREGATE COUNT(*), GROUPING(x) AS g_x + GROUP BY GROUPING SETS((x), (x,y), CUBE(x,z)) +|> SELECT x, y, z, g_x +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#6 AS x [INT64] +| +-$groupby.y#7 AS y [STRING] +| +-$groupby.z#8 AS z [DOUBLE] +| +-$grouping_call.$grouping_call1#9 AS g_x [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.x#6, $groupby.y#7, $groupby.z#8, $grouping_call.$grouping_call1#9] + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.x#6, $groupby.y#7, $groupby.z#8, $aggregate.$agg1#4, $grouping_call.$grouping_call1#9] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=STRING, value="2") + | | +-z#3 := Literal(type=DOUBLE, value=3) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-x#6 := ColumnRef(type=INT64, column=$query.x#1) + | +-y#7 := ColumnRef(type=STRING, column=$query.y#2) + | +-z#8 := ColumnRef(type=DOUBLE, column=$query.z#3) + +-aggregate_list= + | +-$agg1#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-grouping_set_list= + | +-GroupingSet + | | +-group_by_column_list= + | | +-ColumnRef(type=INT64, column=$groupby.x#6) + | +-GroupingSet + | | +-group_by_column_list= + | | +-ColumnRef(type=INT64, column=$groupby.x#6) + | | +-ColumnRef(type=STRING, column=$groupby.y#7) + | +-Cube + | +-cube_column_list= + | +-GroupingSetMultiColumn + | | +-column_list= + | | +-ColumnRef(type=INT64, column=$groupby.x#6) + | +-GroupingSetMultiColumn + | +-column_list= + | +-ColumnRef(type=DOUBLE, column=$groupby.z#8) + +-grouping_call_list= + +-GroupingCall + +-group_by_column= + | +-ColumnRef(type=INT64, column=$groupby.x#6) + +-output_column=$grouping_call.$grouping_call1#9 +== + +# Chained aggregates +select 1 x, 2 y, 3 z +|> AGGREGATE sum(z) z + GROUP BY x, y +|> AGGREGATE AVG(z) z + GROUP BY x +|> AGGREGATE COUNT(DISTINCT z) c +-- +QueryStmt ++-output_column_list= +| +-$aggregate.c#9 AS c [INT64] ++-query= + +-AggregateScan + +-column_list=[$aggregate.c#9] + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.x#8, $aggregate.z#7] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.x#5, $groupby.y#6, $aggregate.z#4] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$query.[x#1, y#2, z#3] + | | | +-expr_list= + | | | | +-x#1 := Literal(type=INT64, value=1) + | | | | +-y#2 := Literal(type=INT64, value=2) + | | | | +-z#3 := Literal(type=INT64, value=3) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-group_by_list= + | | | +-x#5 := ColumnRef(type=INT64, column=$query.x#1) + | | | +-y#6 := ColumnRef(type=INT64, column=$query.y#2) + | | +-aggregate_list= + | | +-z#4 := + | | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.z#3) + | +-group_by_list= + | | +-x#8 := ColumnRef(type=INT64, column=$groupby.x#5) + | +-aggregate_list= + | +-z#7 := + | +-AggregateFunctionCall(ZetaSQL:avg(INT64) -> DOUBLE) + | +-ColumnRef(type=INT64, column=$aggregate.z#4) + +-aggregate_list= + +-c#9 := + +-AggregateFunctionCall(ZetaSQL:count(DOUBLE) -> INT64) + +-ColumnRef(type=DOUBLE, column=$aggregate.z#7) + +-distinct=TRUE +== + +# Aliases on the GROUP BY expressions. +from KeyValue +|> AGGREGATE GROUP BY key, key AS key2, key+1, key+2 AS key_plus_2 +|> WHERE key2 = key_plus_2 +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#3 AS key [INT64] +| +-$groupby.key#3 AS key2 [INT64] +| +-$groupby.$groupbycol2#4 AS `$groupbycol2` [INT64] +| +-$groupby.key_plus_2#5 AS key_plus_2 [INT64] ++-query= + +-FilterScan + +-column_list=$groupby.[key#3, $groupbycol2#4, key_plus_2#5] + +-input_scan= + | +-AggregateScan + | +-column_list=$groupby.[key#3, $groupbycol2#4, key_plus_2#5] + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-$groupbycol2#4 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-key_plus_2#5 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$groupby.key#3) + +-ColumnRef(type=INT64, column=$groupby.key_plus_2#5) +== + +# Duplicate aliases on GROUP BY expressions, and against aggregate expressions. +from KeyValue +|> AGGREGATE count(*) AS x + GROUP BY key, key+1 AS key, value AS x, value||'x' AS x +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#4 AS key [INT64] +| +-$groupby.key#5 AS key [INT64] +| +-$groupby.x#6 AS x [STRING] +| +-$groupby.x#7 AS x [STRING] +| +-$aggregate.x#3 AS x [INT64] ++-query= + +-AggregateScan + +-column_list=[$groupby.key#4, $groupby.key#5, $groupby.x#6, $groupby.x#7, $aggregate.x#3] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-group_by_list= + | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-key#5 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-x#6 := ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-x#7 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-Literal(type=STRING, value="x") + +-aggregate_list= + +-x#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) +== + +from KeyValue +|> AGGREGATE count(*) AS x + GROUP BY key, key+1 AS key +|> where key=1 +-- +ERROR: Column name key is ambiguous [at 4:10] +|> where key=1 + ^ +== + +# Test a GROUP BY alias in regular syntax, with FEATURE_PIPES on or off. +[language_features={{PIPES|}}] +select COUNT(*) +from KeyValue +GROUP BY key AS alias +-- +ALTERNATION GROUP: PIPES +-- +ERROR: GROUP BY does not support aliases [at 3:14] +GROUP BY key AS alias + ^ +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: GROUP BY does not support aliases [at 3:14] +GROUP BY key AS alias + ^ +== + +# Collation propagation for pipe AGGREGATE, for both aggregate and grouping +# columns. +# TODO Enable java support for collation. +[no_java] +[language_features=PIPES{{|,V_1_3_ANNOTATION_FRAMEWORK|,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT}}] +from CollatedTable +|> AGGREGATE MAX(COALESCE(string_ci)) as max_ci + GROUP BY COALESCE(string_ci) AS grouping_ci +-- +ALTERNATION GROUPS: + + ,V_1_3_ANNOTATION_FRAMEWORK +-- +QueryStmt ++-output_column_list= +| +-$groupby.grouping_ci#6 AS grouping_ci [STRING] +| +-$aggregate.max_ci#5 AS max_ci [STRING] ++-query= + +-AggregateScan + +-column_list=[$groupby.grouping_ci#6, $aggregate.max_ci#5] + +-input_scan= + | +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) + +-group_by_list= + | +-grouping_ci#6 := + | +-FunctionCall(ZetaSQL:coalesce(repeated(1) STRING) -> STRING) + | +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + +-aggregate_list= + +-max_ci#5 := + +-AggregateFunctionCall(ZetaSQL:max(STRING) -> STRING) + +-FunctionCall(ZetaSQL:coalesce(repeated(1) STRING) -> STRING) + +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) +-- +ALTERNATION GROUP: ,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT +-- +QueryStmt ++-output_column_list= +| +-$groupby.grouping_ci#6{Collation:"und:ci"} AS grouping_ci [STRING] +| +-$aggregate.max_ci#5{Collation:"und:ci"} AS max_ci [STRING] ++-query= + +-AggregateScan + +-column_list=[$groupby.grouping_ci#6{Collation:"und:ci"}, $aggregate.max_ci#5{Collation:"und:ci"}] + +-input_scan= + | +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) + +-group_by_list= + | +-grouping_ci#6 := + | +-FunctionCall(ZetaSQL:coalesce(repeated(1) STRING) -> STRING) + | +-type_annotation_map={Collation:"und:ci"} + | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + +-collation_list=[und:ci] + +-aggregate_list= + +-max_ci#5 := + +-AggregateFunctionCall(ZetaSQL:max(STRING) -> STRING) + +-type_annotation_map={Collation:"und:ci"} + +-FunctionCall(ZetaSQL:coalesce(repeated(1) STRING) -> STRING) + +-type_annotation_map={Collation:"und:ci"} + +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + +-collation_list=[und:ci] diff --git a/zetasql/analyzer/testdata/pipe_aggregate_with_order.test b/zetasql/analyzer/testdata/pipe_aggregate_with_order.test new file mode 100644 index 000000000..377d6ed61 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_aggregate_with_order.test @@ -0,0 +1,756 @@ +[default language_features=PIPES,GROUP_BY_ROLLUP,V_1_4_GROUPING_SETS,V_1_4_GROUPING_BUILTIN,V_1_3_NULLS_FIRST_LAST_IN_ORDER_BY,V_1_2_GROUP_BY_STRUCT] + +# Test GROUP BY, with and without AND ORDER, with order suffixes on some items. +from KeyValue +|> aggregate count(*) + GROUP {{|AND ORDER}} BY + key, + value, + value ASC, + value DESC, + value ASC NULLS FIRST, + value DESC NULLS LAST, + key+1 ASC, + key+2 +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#4 AS key [INT64] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.$groupbycol3#6 AS `$groupbycol3` [INT64] +| +-$groupby.$groupbycol4#7 AS `$groupbycol4` [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#4, $groupby.value#5, $groupby.value#5, $groupby.value#5, $groupby.value#5, $groupby.value#5, $groupby.$groupbycol3#6, $groupby.$groupbycol4#7, $aggregate.$agg1#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#4, $groupby.value#5, $groupby.$groupbycol3#6, $groupby.$groupbycol4#7, $aggregate.$agg1#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-group_by_list= + | | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-$groupbycol3#6 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=1) + | | +-$groupbycol4#7 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=2) + | +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=162-167 + | +-column_ref= + | +-ColumnRef(type=STRING, column=$groupby.value#5) + +-OrderByItem + | +-parse_location=180-185 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.value#5) + | +-is_descending=TRUE + +-OrderByItem + | +-parse_location=199-204 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.value#5) + | +-null_order=NULLS_FIRST + +-OrderByItem + | +-parse_location=229-234 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.value#5) + | +-is_descending=TRUE + | +-null_order=NULLS_LAST + +-OrderByItem + +-parse_location=259-264 + +-column_ref= + +-ColumnRef(type=INT64, column=$groupby.$groupbycol3#6) +-- +ALTERNATION GROUP: AND ORDER +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#4 AS key [INT64] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.value#5 AS value [STRING] +| +-$groupby.$groupbycol3#6 AS `$groupbycol3` [INT64] +| +-$groupby.$groupbycol4#7 AS `$groupbycol4` [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#4, $groupby.value#5, $groupby.value#5, $groupby.value#5, $groupby.value#5, $groupby.value#5, $groupby.$groupbycol3#6, $groupby.$groupbycol4#7, $aggregate.$agg1#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#4, $groupby.value#5, $groupby.$groupbycol3#6, $groupby.$groupbycol4#7, $aggregate.$agg1#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-group_by_list= + | | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-$groupbycol3#6 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=1) + | | +-$groupbycol4#7 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=2) + | +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=145-148 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.key#4) + +-OrderByItem + | +-parse_location=157-162 + | +-column_ref= + | +-ColumnRef(type=STRING, column=$groupby.value#5) + +-OrderByItem + | +-parse_location=171-176 + | +-column_ref= + | +-ColumnRef(type=STRING, column=$groupby.value#5) + +-OrderByItem + | +-parse_location=189-194 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.value#5) + | +-is_descending=TRUE + +-OrderByItem + | +-parse_location=208-213 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.value#5) + | +-null_order=NULLS_FIRST + +-OrderByItem + | +-parse_location=238-243 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.value#5) + | +-is_descending=TRUE + | +-null_order=NULLS_LAST + +-OrderByItem + | +-parse_location=268-273 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol3#6) + +-OrderByItem + +-parse_location=286-291 + +-column_ref= + +-ColumnRef(type=INT64, column=$groupby.$groupbycol4#7) +== + +# GROUP BY with ordering by computed expressions. +FROM KeyValue +|> EXTEND key+1 AS x +|> AGGREGATE COUNT(*) + GROUP {{|AND ORDER}} BY + x, x+1, x+2 ASC, x+2 DESC, x+3 DESC NULLS LAST +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#5 AS x [INT64] +| +-$groupby.$groupbycol2#6 AS `$groupbycol2` [INT64] +| +-$groupby.$groupbycol3#7 AS `$groupbycol3` [INT64] +| +-$groupby.$groupbycol4#8 AS `$groupbycol4` [INT64] +| +-$groupby.$groupbycol5#9 AS `$groupbycol5` [INT64] +| +-$aggregate.$agg1#4 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.x#5, $groupby.$groupbycol2#6, $groupby.$groupbycol3#7, $groupby.$groupbycol4#8, $groupby.$groupbycol5#9, $aggregate.$agg1#4] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.x#5, $groupby.$groupbycol2#6, $groupby.$groupbycol3#7, $groupby.$groupbycol4#8, $groupby.$groupbycol5#9, $aggregate.$agg1#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#1, $pipe_extend.x#3] + | | +-expr_list= + | | | +-x#3 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | | +-x#5 := ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | +-$groupbycol2#6 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | | +-Literal(type=INT64, value=1) + | | +-$groupbycol3#7 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | | +-Literal(type=INT64, value=2) + | | +-$groupbycol4#8 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | | +-Literal(type=INT64, value=2) + | | +-$groupbycol5#9 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | +-Literal(type=INT64, value=3) + | +-aggregate_list= + | +-$agg1#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=84-87 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol3#7) + +-OrderByItem + | +-parse_location=93-96 + | +-column_ref= + | | +-ColumnRef(type=INT64, column=$groupby.$groupbycol4#8) + | +-is_descending=TRUE + +-OrderByItem + +-parse_location=103-106 + +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol5#9) + +-is_descending=TRUE + +-null_order=NULLS_LAST +-- +ALTERNATION GROUP: AND ORDER +-- +QueryStmt ++-output_column_list= +| +-$groupby.x#5 AS x [INT64] +| +-$groupby.$groupbycol2#6 AS `$groupbycol2` [INT64] +| +-$groupby.$groupbycol3#7 AS `$groupbycol3` [INT64] +| +-$groupby.$groupbycol4#8 AS `$groupbycol4` [INT64] +| +-$groupby.$groupbycol5#9 AS `$groupbycol5` [INT64] +| +-$aggregate.$agg1#4 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.x#5, $groupby.$groupbycol2#6, $groupby.$groupbycol3#7, $groupby.$groupbycol4#8, $groupby.$groupbycol5#9, $aggregate.$agg1#4] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.x#5, $groupby.$groupbycol2#6, $groupby.$groupbycol3#7, $groupby.$groupbycol4#8, $groupby.$groupbycol5#9, $aggregate.$agg1#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#1, $pipe_extend.x#3] + | | +-expr_list= + | | | +-x#3 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | | +-x#5 := ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | +-$groupbycol2#6 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | | +-Literal(type=INT64, value=1) + | | +-$groupbycol3#7 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | | +-Literal(type=INT64, value=2) + | | +-$groupbycol4#8 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | | +-Literal(type=INT64, value=2) + | | +-$groupbycol5#9 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$pipe_extend.x#3) + | | +-Literal(type=INT64, value=3) + | +-aggregate_list= + | +-$agg1#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=85-86 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.x#5) + +-OrderByItem + | +-parse_location=88-91 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol2#6) + +-OrderByItem + | +-parse_location=93-96 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol3#7) + +-OrderByItem + | +-parse_location=102-105 + | +-column_ref= + | | +-ColumnRef(type=INT64, column=$groupby.$groupbycol4#8) + | +-is_descending=TRUE + +-OrderByItem + +-parse_location=112-115 + +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.$groupbycol5#9) + +-is_descending=TRUE + +-null_order=NULLS_LAST +== + +# NULLS FIRST/LAST are allowed without ASC/DESC only with GROUP AND ORDER BY. +from KeyValue +|> aggregate count(*) + GROUP {{|AND ORDER}} BY key {{NULLS FIRST|NULLS LAST}} +-- +ALTERNATION GROUP: NULLS FIRST +-- +ERROR: GROUP BY items cannot have NULLS FIRST/LAST without ASC/DESC except when using GROUP AND ORDER BY [at 3:18] + GROUP BY key NULLS FIRST + ^ +-- +ALTERNATION GROUP: NULLS LAST +-- +ERROR: GROUP BY items cannot have NULLS FIRST/LAST without ASC/DESC except when using GROUP AND ORDER BY [at 3:18] + GROUP BY key NULLS LAST + ^ +-- +ALTERNATION GROUP: AND ORDER,NULLS FIRST +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#4 AS key [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#4, $aggregate.$agg1#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#4, $aggregate.$agg1#3] + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + +-parse_location=58-61 + +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.key#4) + +-null_order=NULLS_FIRST +-- +ALTERNATION GROUP: AND ORDER,NULLS LAST +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#4 AS key [INT64] +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.key#4, $aggregate.$agg1#3] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#4, $aggregate.$agg1#3] + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-group_by_list= + | | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-aggregate_list= + | +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + +-parse_location=58-61 + +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.key#4) + +-null_order=NULLS_LAST +== + +# STRUCTs are groupable but not orderable. +select STRUCT(123 AS x) str +|> AGGREGATE COUNT(*) + GROUP BY str, str ASC +-- +ERROR: ORDER BY does not support expressions of type STRUCT [at 3:18] + GROUP BY str, str ASC + ^ +== + +select STRUCT(123 AS x) str +|> AGGREGATE COUNT(*) + GROUP AND ORDER BY str +-- +ERROR: ORDER BY does not support expressions of type STRUCT [at 3:23] + GROUP AND ORDER BY str + ^ +== + +# Order suffixes are disallowed on all non-expression grouping items. +FROM KeyValue +|> AGGREGATE count(*) + GROUP BY {{ALL|()|ROLLUP(key)|CUBE(key)|GROUPING SETS(key)}} ASC +-- +ALTERNATION GROUP: ALL +-- +ERROR: Syntax error: Unexpected keyword ALL [at 3:13] + GROUP BY ALL ASC + ^ +-- +ALTERNATION GROUP: () +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:16] + GROUP BY () ASC + ^ +-- +ALTERNATION GROUP: ROLLUP(key) +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:25] + GROUP BY ROLLUP(key) ASC + ^ +-- +ALTERNATION GROUP: CUBE(key) +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:23] + GROUP BY CUBE(key) ASC + ^ +-- +ALTERNATION GROUP: GROUPING SETS(key) +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:32] + GROUP BY GROUPING SETS(key) ASC + ^ +== + +# AND ORDER BY is disallowed on all non-expression grouping items. +FROM KeyValue +|> AGGREGATE count(*) + GROUP AND ORDER BY {{ALL|()|ROLLUP(key)|CUBE(key)|GROUPING SETS(key)}} +-- +ALTERNATION GROUP: ALL +-- +ERROR: Syntax error: Unexpected keyword ALL [at 3:23] + GROUP AND ORDER BY ALL + ^ +-- +ALTERNATION GROUP: () +-- +ERROR: GROUP AND ORDER BY cannot be used with GROUP BY () [at 3:23] + GROUP AND ORDER BY () + ^ +-- +ALTERNATION GROUP: ROLLUP(key) +-- +ERROR: GROUP AND ORDER BY cannot be used with ROLLUP [at 3:23] + GROUP AND ORDER BY ROLLUP(key) + ^ +-- +ALTERNATION GROUP: CUBE(key) +-- +ERROR: GROUP AND ORDER BY cannot be used with CUBE [at 3:23] + GROUP AND ORDER BY CUBE(key) + ^ +-- +ALTERNATION GROUP: GROUPING SETS(key) +-- +ERROR: GROUP AND ORDER BY cannot be used with GROUPING SETS [at 3:23] + GROUP AND ORDER BY GROUPING SETS(key) + ^ +== + +# Test order modifiers attached to standard GROUP BY syntax. +# With PIPES disabled, the parser rejects these. +# With PIPES enabled, they parse, but the analyzer rejects them. +select COUNT(*) +from KeyValue +GROUP {{|AND ORDER}} BY + key{{| ASC| DESC| ASC NULLS FIRST| DESC NULLS LAST| NULLS FIRST}} +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#3] + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-group_by_list= + | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + +-aggregate_list= + +-$agg1#3 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) +-- +ALTERNATION GROUP: ASC +-- +ERROR: GROUP BY does not support order specification outside pipe AGGREGATE [at 4:8] + key ASC + ^ +-- +ALTERNATION GROUP: DESC +-- +ERROR: GROUP BY does not support order specification outside pipe AGGREGATE [at 4:8] + key DESC + ^ +-- +ALTERNATION GROUP: ASC NULLS FIRST +-- +ERROR: GROUP BY does not support order specification outside pipe AGGREGATE [at 4:8] + key ASC NULLS FIRST + ^ +-- +ALTERNATION GROUP: DESC NULLS LAST +-- +ERROR: GROUP BY does not support order specification outside pipe AGGREGATE [at 4:8] + key DESC NULLS LAST + ^ +-- +ALTERNATION GROUP: NULLS FIRST +-- +ERROR: GROUP BY does not support order specification outside pipe AGGREGATE [at 4:8] + key NULLS FIRST + ^ +-- +ALTERNATION GROUPS: + AND ORDER, + AND ORDER, ASC + AND ORDER, DESC + AND ORDER, ASC NULLS FIRST + AND ORDER, DESC NULLS LAST + AND ORDER, NULLS FIRST +-- +ERROR: GROUP AND ORDER BY is not supported outside pipe AGGREGATE [at 3:1] +GROUP AND ORDER BY +^ +== + +select COUNT(*) +from KeyValue +GROUP AND ORDER BY {{key|()|ALL|ROLLUP(key)}} +-- +ALTERNATION GROUP: key +-- +ERROR: GROUP AND ORDER BY is not supported outside pipe AGGREGATE [at 3:1] +GROUP AND ORDER BY key +^ +-- +ALTERNATION GROUP: () +-- +ERROR: GROUP AND ORDER BY is not supported outside pipe AGGREGATE [at 3:1] +GROUP AND ORDER BY () +^ +-- +ALTERNATION GROUP: ALL +-- +ERROR: GROUP AND ORDER BY is not supported outside pipe AGGREGATE [at 3:1] +GROUP AND ORDER BY ALL +^ +-- +ALTERNATION GROUP: ROLLUP(key) +-- +ERROR: GROUP AND ORDER BY is not supported outside pipe AGGREGATE [at 3:1] +GROUP AND ORDER BY ROLLUP(key) +^ +== + +# GROUP BY in AGGREGATE doesn't currently support ordinals. If that gets added, +# we need to test GROUP AND ORDER BY also works correctly with ordinals. +from KeyValue +|> aggregate count(*) + GROUP AND ORDER BY key,{{2|7}} +-- +ALTERNATION GROUP: 2 +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 3:27] + GROUP AND ORDER BY key,2 + ^ +-- +ALTERNATION GROUP: 7 +-- +ERROR: GROUP BY ordinal not allowed in pipe AGGREGATE [at 3:27] + GROUP AND ORDER BY key,7 + ^ +== + +# Ordering suffixes on the aggregate list. +from KeyValue +|> aggregate count(key) ASC, + count(key+1) DESC, + 1+count(key) ASC NULLS FIRST, + sum(key) DESC NULLS LAST, + count(value) +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#3 AS `$col1` [INT64] +| +-$aggregate.$agg2#4 AS `$col2` [INT64] +| +-$aggregate.$col3#8 AS `$col3` [INT64] +| +-$aggregate.$agg4#6 AS `$col4` [INT64] +| +-$aggregate.$agg5#7 AS `$col5` [INT64] ++-query= + +-OrderByScan + +-column_list=$aggregate.[$agg1#3, $agg2#4, $col3#8, $agg4#6, $agg5#7] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=$aggregate.[$agg1#3, $agg2#4, $col3#8, $agg4#6, $agg5#7] + | +-expr_list= + | | +-$col3#8 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=INT64, column=$aggregate.$agg3#5) + | +-input_scan= + | +-AggregateScan + | +-column_list=$aggregate.[$agg1#3, $agg2#4, $agg3#5, $agg4#6, $agg5#7] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-aggregate_list= + | +-$agg1#3 := + | | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-$agg2#4 := + | | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-$agg3#5 := + | | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-$agg4#6 := + | | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-$agg5#7 := + | +-AggregateFunctionCall(ZetaSQL:count(STRING) -> INT64) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=27-37 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$aggregate.$agg1#3) + +-OrderByItem + | +-parse_location=56-68 + | +-column_ref= + | | +-ColumnRef(type=INT64, column=$aggregate.$agg2#4) + | +-is_descending=TRUE + +-OrderByItem + | +-parse_location=88-100 + | +-column_ref= + | | +-ColumnRef(type=INT64, column=$aggregate.$col3#8) + | +-null_order=NULLS_FIRST + +-OrderByItem + +-parse_location=131-139 + +-column_ref= + | +-ColumnRef(type=INT64, column=$aggregate.$agg4#6) + +-is_descending=TRUE + +-null_order=NULLS_LAST +== + +# Ordering suffix in aggregate list combined with GROUP BY. +# When GROUP AND ORDER BY is present, the ordering for aggregate columns +# is still present but won't affect the result because the grouping +# columns already produce a unique order. +from MultipleColumns +|> aggregate count(string_b) ASC, count(int_d), count(*) DESC + GROUP {{|AND ORDER}} BY int_a, string_b DESC +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.int_a#10 AS int_a [INT64] +| +-$groupby.string_b#11 AS string_b [STRING] +| +-$aggregate.$agg1#7 AS `$col1` [INT64] +| +-$aggregate.$agg2#8 AS `$col2` [INT64] +| +-$aggregate.$agg3#9 AS `$col3` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.int_a#10, $groupby.string_b#11, $aggregate.$agg1#7, $aggregate.$agg2#8, $aggregate.$agg3#9] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int_a#10, $groupby.string_b#11, $aggregate.$agg1#7, $aggregate.$agg2#8, $aggregate.$agg3#9] + | +-input_scan= + | | +-TableScan(column_list=MultipleColumns.[int_a#1, string_b#4, int_d#6], table=MultipleColumns, column_index_list=[0, 3, 5]) + | +-group_by_list= + | | +-int_a#10 := ColumnRef(type=INT64, column=MultipleColumns.int_a#1) + | | +-string_b#11 := ColumnRef(type=STRING, column=MultipleColumns.string_b#4) + | +-aggregate_list= + | +-$agg1#7 := + | | +-AggregateFunctionCall(ZetaSQL:count(STRING) -> INT64) + | | +-ColumnRef(type=STRING, column=MultipleColumns.string_b#4) + | +-$agg2#8 := + | | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=MultipleColumns.int_d#6) + | +-$agg3#9 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=103-111 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.string_b#11) + | +-is_descending=TRUE + +-OrderByItem + | +-parse_location=34-49 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$aggregate.$agg1#7) + +-OrderByItem + +-parse_location=69-77 + +-column_ref= + | +-ColumnRef(type=INT64, column=$aggregate.$agg3#9) + +-is_descending=TRUE +-- +ALTERNATION GROUP: AND ORDER +-- +QueryStmt ++-output_column_list= +| +-$groupby.int_a#10 AS int_a [INT64] +| +-$groupby.string_b#11 AS string_b [STRING] +| +-$aggregate.$agg1#7 AS `$col1` [INT64] +| +-$aggregate.$agg2#8 AS `$col2` [INT64] +| +-$aggregate.$agg3#9 AS `$col3` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.int_a#10, $groupby.string_b#11, $aggregate.$agg1#7, $aggregate.$agg2#8, $aggregate.$agg3#9] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int_a#10, $groupby.string_b#11, $aggregate.$agg1#7, $aggregate.$agg2#8, $aggregate.$agg3#9] + | +-input_scan= + | | +-TableScan(column_list=MultipleColumns.[int_a#1, string_b#4, int_d#6], table=MultipleColumns, column_index_list=[0, 3, 5]) + | +-group_by_list= + | | +-int_a#10 := ColumnRef(type=INT64, column=MultipleColumns.int_a#1) + | | +-string_b#11 := ColumnRef(type=STRING, column=MultipleColumns.string_b#4) + | +-aggregate_list= + | +-$agg1#7 := + | | +-AggregateFunctionCall(ZetaSQL:count(STRING) -> INT64) + | | +-ColumnRef(type=STRING, column=MultipleColumns.string_b#4) + | +-$agg2#8 := + | | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=MultipleColumns.int_d#6) + | +-$agg3#9 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=105-110 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$groupby.int_a#10) + +-OrderByItem + | +-parse_location=112-120 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$groupby.string_b#11) + | +-is_descending=TRUE + +-OrderByItem + | +-parse_location=34-49 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$aggregate.$agg1#7) + +-OrderByItem + +-parse_location=69-77 + +-column_ref= + | +-ColumnRef(type=INT64, column=$aggregate.$agg3#9) + +-is_descending=TRUE +== + +# Using ordering suffix on an unorderable column type in the aggregate list. +select 123 key, STRUCT(123 AS x) str +|> AGGREGATE ANY_VALUE(str) ASC + {{GROUP BY key|GROUP BY KEY asc}} +-- +ERROR: ORDER BY does not support expressions of type STRUCT [at 2:14] +|> AGGREGATE ANY_VALUE(str) ASC + ^ diff --git a/zetasql/analyzer/testdata/pipe_as.test b/zetasql/analyzer/testdata/pipe_as.test new file mode 100644 index 000000000..9100aee0b --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_as.test @@ -0,0 +1,419 @@ +[default language_features=PIPES] +select 1 x, 2 y +|> AS t +|> SELECT t.x, t.y, x, y, *, t.*, t +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$pipe_select.t#4 AS t [STRUCT] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $query.y#2, $query.x#1, $query.y#2, $query.x#1, $query.y#2, $query.x#1, $query.y#2, $pipe_select.t#4] + +-expr_list= + | +-t#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-ColumnRef(type=INT64, column=$query.y#2) + +-input_scan= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +select 1 x, 2 y +|> AS t1 +|> AS t2 +|> SELECT t2.x, y +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-input_scan= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +select 1 x, 2 y +|> AS t1 +|> AS t2 +|> SELECT t1.x, t1.y +-- +ERROR: Unrecognized name: t1 [at 4:11] +|> SELECT t1.x, t1.y + ^ +== + +# t1 is just an alias, not a table, so self-join is not allowed here. +# A workaround for self-join is to use WITH: +# +# WITH t AS (...) +# FROM t AS t1 +# |> JOIN t AS t2 USING(...) +# |> ... +# +# Note, we might add "|> AS TABLE " syntax to support the self-join case. +select 1 x, 2 y +|> AS t1 +|> join t1 as t2 using(x) +|> select x, t1.y, t2.y +-- +ERROR: Table not found: t1 (Unqualified identifiers in a FROM clause are always resolved as tables. Identifier t1 is in scope but unqualified names cannot be resolved here.) [at 3:9] +|> join t1 as t2 using(x) + ^ +== + +# Since t1 is just an alias, it can't be used in correlated subqueries. +select 1 x, 2 y +|> AS t1 +|> SELECT (FROM t1) +-- +ERROR: Table not found: t1 (Unqualified identifiers in a FROM clause are always resolved as tables. Identifier t1 is in scope but unqualified names cannot be resolved here.) [at 3:17] +|> SELECT (FROM t1) + ^ +== + +# Normal table with pseudo columns. +from EnumTable +|> AS t +|> select t.key, t.RowId; +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.RowId#5 AS RowId [BYTES] ++-query= + +-ProjectScan + +-column_list=EnumTable.[key#1, RowId#5] + +-input_scan= + +-TableScan(column_list=EnumTable.[key#1, RowId#5], table=EnumTable, column_index_list=[0, 4]) +== + +# Pipe AS operator in subquery won't lose pseudo-columns, and "SELECT *" will +# still work properly. +from (from EnumTable |> AS t) +|> SELECT Key, TestEnum, AnotherTestEnum, Filename, RowId, * +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS Key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-EnumTable.Filename#4 AS Filename [STRING] +| +-EnumTable.RowId#5 AS RowId [BYTES] +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] ++-query= + +-ProjectScan + +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5, key#1, TestEnum#2, AnotherTestEnum#3] + +-input_scan= + +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +== + + +from EnumTable +|> AS t +|> select t.* +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] ++-query= + +-ProjectScan + +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3] + +-input_scan= + +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3], table=EnumTable, column_index_list=[0, 1, 2]) +== + +# Value table will be kept. +# If the value table has an alias, the old alias will be dropped, so accessing +# the old alias would be an error. +from KitchenSinkValueTable {{| AS old_alias}} +|> AS t +|> WHERE int32_val = 5 AND t.int32_val = 5 {{| AND old_alias.int32_val = 5}}; +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-KitchenSinkValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[KitchenSinkValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0]) + +-filter_expr= + +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | +-field_descriptor=int32_val + | | +-default_value=77 + | +-Literal(type=INT32, value=5) + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | +-field_descriptor=int32_val + | +-default_value=77 + +-Literal(type=INT32, value=5) +-- +ALTERNATION GROUPS: + AND old_alias.int32_val = 5 + AS old_alias, AND old_alias.int32_val = 5 +-- +ERROR: Unrecognized name: old_alias [at 3:49] +|> WHERE int32_val = 5 AND t.int32_val = 5 AND old_alias.int32_val = 5; + ^ +-- +ALTERNATION GROUP: AS old_alias, +-- +QueryStmt ++-output_column_list= +| +-KitchenSinkValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[KitchenSinkValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0], alias="old_alias") + +-filter_expr= + +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | +-field_descriptor=int32_val + | | +-default_value=77 + | +-Literal(type=INT32, value=5) + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | +-field_descriptor=int32_val + | +-default_value=77 + +-Literal(type=INT32, value=5) +== + +FROM unnest([1,2,3]) +|> AS value +|> WHERE value=2 +-- +QueryStmt ++-output_column_list= +| +-$array.$unnest1#1 AS `$value` [INT64] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[$array.$unnest1#1] + +-input_scan= + | +-ArrayScan + | +-column_list=[$array.$unnest1#1] + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=[1, 2, 3]) + | +-element_column_list=[$array.$unnest1#1] + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$array.$unnest1#1) + +-Literal(type=INT64, value=2) +== + +# Value table in subquery will be kept too. +from (from TestExtraValueTable |> AS t) +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +from KitchenSinkValueTable t1 +|> AS t2 +|> select t2.int64_key_1, string_val +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.int64_key_1#2 AS int64_key_1 [INT64] +| +-$pipe_select.string_val#3 AS string_val [STRING] ++-query= + +-ProjectScan + +-column_list=$pipe_select.[int64_key_1#2, string_val#3] + +-expr_list= + | +-int64_key_1#2 := + | | +-GetProtoField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | | +-field_descriptor=int64_key_1 + | +-string_val#3 := + | +-GetProtoField + | +-type=STRING + | +-expr= + | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | +-field_descriptor=string_val + | +-default_value="default_name" + +-input_scan= + +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0], alias="t1") +== + +# The equivalent form is: +# +# from ( +# from KitchenSinkValueTable t1 +# ) t2 +# |> ... +# +# t1 is in the inner scope, so it's not visible to the follow up pipes. +from KitchenSinkValueTable t1 +|> AS t2 +|> select t1.int64_key_1, string_val +-- +ERROR: Unrecognized name: t1 [at 3:11] +|> select t1.int64_key_1, string_val + ^ +== + +# The output column list of the join is [Key, Value, Value], so range variable t +# will be t->RANGE_VARIABLE, which is ok as long as we don't +# access t.Value. +# Note, we can still output both Value columns using "*". +from KeyValue t1 +|> join KeyValue t2 USING(key) +|> AS t +|> select t.key, *; +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Key#1, Value#2, Value#4] + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="t1") + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="t2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +== + +from KeyValue t1 +|> join KeyValue t2 USING(key) +|> AS t +|> select t.key, t.value +-- +ERROR: Name value is ambiguous inside t [at 4:20] +|> select t.key, t.value + ^ +== + +# t1 and t2 are in the inner scope, not visible to follow up pipes. +from KeyValue t1 +|> join KeyValue2 t2 USING(key) +|> AS t +|> SELECT t1.Value +-- +ERROR: Unrecognized name: t1 [at 4:11] +|> SELECT t1.Value + ^ +== + +from KitchenSinkValueTable +|> aggregate sum(int32_val) as value group by int64_key_1 as key +|> AS t +|> select t.key, t.value; +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#3 AS key [INT64] +| +-$aggregate.value#2 AS value [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.key#3, $aggregate.value#2] + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.key#3, $aggregate.value#2] + +-input_scan= + | +-TableScan(column_list=[KitchenSinkValueTable.value#1], table=KitchenSinkValueTable, column_index_list=[0]) + +-group_by_list= + | +-key#3 := + | +-GetProtoField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + | +-field_descriptor=int64_key_1 + +-aggregate_list= + +-value#2 := + +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-Cast(INT32 -> INT64) + +-GetProtoField + +-type=INT32 + +-expr= + | +-ColumnRef(type=PROTO, column=KitchenSinkValueTable.value#1) + +-field_descriptor=int32_val + +-default_value=77 +== + +# A join unnesting an array that came from an AS alias. +from ArrayTypes +|> AS t +|> CROSS JOIN UNNEST(t.Int32Array) a +|> SELECT t.StringArray, a +-- +QueryStmt ++-output_column_list= +| +-ArrayTypes.StringArray#5 AS StringArray [ARRAY] +| +-$array.a#21 AS a [INT32] ++-query= + +-ProjectScan + +-column_list=[ArrayTypes.StringArray#5, $array.a#21] + +-input_scan= + +-ArrayScan + +-column_list=[ArrayTypes.Int32Array#1, ArrayTypes.StringArray#5, $array.a#21] + +-input_scan= + | +-TableScan(column_list=ArrayTypes.[Int32Array#1, StringArray#5], table=ArrayTypes, column_index_list=[0, 4]) + +-array_expr_list= + | +-ColumnRef(type=ARRAY, column=ArrayTypes.Int32Array#1) + +-element_column_list=[$array.a#21] diff --git a/zetasql/analyzer/testdata/pipe_assert.test b/zetasql/analyzer/testdata/pipe_assert.test new file mode 100644 index 000000000..dc3807776 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_assert.test @@ -0,0 +1,747 @@ +[language_features={{|PIPES|PIPE_ASSERT}}] +SELECT * FROM KeyValue +|> ASSERT true +-- +ALTERNATION GROUPS: + + PIPE_ASSERT +-- +ERROR: Pipe query syntax not supported [at 2:1] +|> ASSERT true +^ +-- +ALTERNATION GROUP: PIPES +-- +ERROR: Pipe ASSERT not supported [at 2:1] +|> ASSERT true +^ +== + +[default language_features=PIPES,PIPE_ASSERT] +[default enabled_ast_rewrites=DEFAULTS,+PIPE_ASSERT] +# ResolvedLiteral strings that didn't come from the query text. + +FROM KeyValue +|> ASSERT true +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-condition= + | +-Literal(type=BOOL, value=true) + +-message= + +-Literal(type=STRING, value="true") + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-Literal(type=BOOL, value=true) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-Literal(type=STRING, value="true") +== + +FROM KeyValue +|> ASSERT key=4 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-condition= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=4) + +-message= + +-Literal(type=STRING, value="key=4") + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=4) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-Literal(type=STRING, value="key=4") +== + +FROM KeyValue +|> ASSERT value='abc' +|> where key=4 +|> SELECT * +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-AssertScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-condition= + | | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-Literal(type=STRING, value="abc") + | +-message= + | +-Literal(type=STRING, value="value='abc'") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=INT64, value=4) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-BarrierScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-Literal(type=STRING, value="abc") + | +-Literal(type=BOOL, value=true) + | +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-Literal(type=STRING, value="Assert failed: ") + | +-Literal(type=STRING, value="value='abc'") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=INT64, value=4) +== + +FROM KeyValue +|> ASSERT 7 +-- +ERROR: ASSERT condition should return type BOOL, but returns INT64 [at 2:11] +|> ASSERT 7 + ^ +== + +FROM KeyValue +|> ASSERT SUM(key) > 10 +-- +ERROR: Aggregate function SUM not allowed in ASSERT condition [at 2:11] +|> ASSERT SUM(key) > 10 + ^ +== + +# Error in condition expression. +FROM KeyValue +|> ASSERT x=5 +-- +ERROR: Unrecognized name: x [at 2:11] +|> ASSERT x=5 + ^ +== + +FROM KeyValue +|> ASSERT key=5, 'oops' +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-condition= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-message= + +-Literal(type=STRING, value="oops") + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-Literal(type=STRING, value="oops") +== + +# A non-string message gets casted to string. +FROM KeyValue +|> ASSERT key=5, key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-condition= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-message= + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + +-Cast(INT64 -> STRING) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=STRING, value="NULL") + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + +-Cast(INT64 -> STRING) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=STRING, value="NULL") +== + +# Multiple message args are joined with CONCAT, with spaces. +# The non-string args are casted. +FROM KeyValue +|> ASSERT key=5, key+1, NULL, 'abc', key*1.5 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-condition= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-message= + +-FunctionCall(ZetaSQL:concat(STRING, repeated(6) STRING) -> STRING) + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-Cast(INT64 -> STRING) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-Literal(type=STRING, value="NULL") + +-Literal(type=STRING, value=" ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-Literal(type=STRING, value=NULL) + | +-Literal(type=STRING, value="NULL") + +-Literal(type=STRING, value=" ") + +-Literal(type=STRING, value="abc") + +-Literal(type=STRING, value=" ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + +-Cast(DOUBLE -> STRING) + | +-FunctionCall(ZetaSQL:$multiply(DOUBLE, DOUBLE) -> DOUBLE) + | +-Cast(INT64 -> DOUBLE) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=DOUBLE, value=1.5) + +-Literal(type=STRING, value="NULL") + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-FunctionCall(ZetaSQL:concat(STRING, repeated(6) STRING) -> STRING) + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-Cast(INT64 -> STRING) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-Literal(type=STRING, value="NULL") + +-Literal(type=STRING, value=" ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-Literal(type=STRING, value=NULL) + | +-Literal(type=STRING, value="NULL") + +-Literal(type=STRING, value=" ") + +-Literal(type=STRING, value="abc") + +-Literal(type=STRING, value=" ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + +-Cast(DOUBLE -> STRING) + | +-FunctionCall(ZetaSQL:$multiply(DOUBLE, DOUBLE) -> DOUBLE) + | +-Cast(INT64 -> DOUBLE) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=DOUBLE, value=1.5) + +-Literal(type=STRING, value="NULL") +== + +FROM KeyValue +|> ASSERT key=5, (key,value) +-- +ERROR: ASSERT message value has type STRUCT which cannot be coerced to STRING [at 2:18] +|> ASSERT key=5, (key,value) + ^ +== + +# Error in message expression. +FROM KeyValue +|> ASSERT key=5, key=x +-- +ERROR: Unrecognized name: x [at 2:22] +|> ASSERT key=5, key=x + ^ +== + +# Show how internal comments and whitespace are captured in the +# assert message, but leading and trailing are stripped. +FROM KeyValue +|> ASSERT /* abc */ 1 /* def */ = /* ghi */ 2 /* jkl */ +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-condition= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-Literal(type=INT64, value=1) + | +-Literal(type=INT64, value=2) + +-message= + +-Literal(type=STRING, value="1 /* def */ = /* ghi */ 2") + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-Literal(type=INT64, value=1) + | +-Literal(type=INT64, value=2) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-Literal(type=STRING, value="1 /* def */ = /* ghi */ 2") +== + +# Newlines inside the assert expression are also kept. +# Line comments could be a particular danger. +FROM KeyValue +|> ASSERT + +1 + +2 + +# comment +\-- comment + += 3 + +|> where true +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-AssertScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-condition= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-Literal(type=INT64, value=1) + | | | +-Literal(type=INT64, value=2) + | | +-Literal(type=INT64, value=3) + | +-message= + | +-Literal(type=STRING, value="1 +\n2\n\n# comment\n-- comment\n\n= 3") + +-filter_expr= + +-Literal(type=BOOL, value=true) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-BarrierScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-Literal(type=INT64, value=1) + | | | +-Literal(type=INT64, value=2) + | | +-Literal(type=INT64, value=3) + | +-Literal(type=BOOL, value=true) + | +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-Literal(type=STRING, value="Assert failed: ") + | +-Literal(type=STRING, value="1 +\n2\n\n# comment\n-- comment\n\n= 3") + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +[show_unparsed] +from KeyValue +|> assert key=10 +|> assert key=11, value +|> assert key=11, key, value +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-AssertScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-AssertScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-AssertScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-condition= + | | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=10) + | | +-message= + | | +-Literal(type=STRING, value="key=10") + | +-condition= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=11) + | +-message= + | +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-Literal(type=STRING, value="NULL") + +-condition= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=11) + +-message= + +-FunctionCall(ZetaSQL:concat(STRING, repeated(2) STRING) -> STRING) + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-Cast(INT64 -> STRING) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=STRING, value="NULL") + +-Literal(type=STRING, value=" ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-Literal(type=STRING, value="NULL") + +[UNPARSED_SQL] +SELECT + assertscan_6.a_1 AS Key, + assertscan_6.a_2 AS Value +FROM + ( + SELECT + assertscan_5.a_1 AS a_1, + assertscan_5.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + assertscan_5.a_1 AS a_1, + assertscan_5.a_2 AS a_2 + FROM + ( + SELECT + assertscan_4.a_1 AS a_1, + assertscan_4.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + assertscan_4.a_1 AS a_1, + assertscan_4.a_2 AS a_2 + FROM + ( + SELECT + keyvalue_3.a_1 AS a_1, + keyvalue_3.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3 + |> ASSERT(keyvalue_3.a_1) = 10, "key=10" + ) + ) AS assertscan_4 + ) AS assertscan_4 + |> ASSERT(assertscan_4.a_1) = 11, IFNULL(assertscan_4.a_2, "NULL") + ) + ) AS assertscan_5 + ) AS assertscan_5 + |> ASSERT(assertscan_5.a_1) = 11, CONCAT(IFNULL(CAST(assertscan_5.a_1 AS STRING), "NULL"), " ", IFNULL(assertscan_5.a_2, + "NULL")) + ) + ) AS assertscan_6; + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-BarrierScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-BarrierScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-BarrierScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | +-BarrierScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | +-FilterScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | | +-BarrierScan + | | | +-column_list=KeyValue.[Key#1, Value#2] + | | | +-input_scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=10) + | | +-Literal(type=BOOL, value=true) + | | +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-Literal(type=STRING, value="Assert failed: ") + | | +-Literal(type=STRING, value="key=10") + | +-filter_expr= + | +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=11) + | +-Literal(type=BOOL, value=true) + | +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-Literal(type=STRING, value="Assert failed: ") + | +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-Literal(type=STRING, value="NULL") + +-filter_expr= + +-FunctionCall(ZetaSQL:if(BOOL, BOOL, BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=11) + +-Literal(type=BOOL, value=true) + +-FunctionCall(ZetaSQL:error(STRING) -> BOOL) + +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + +-Literal(type=STRING, value="Assert failed: ") + +-FunctionCall(ZetaSQL:concat(STRING, repeated(2) STRING) -> STRING) + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + | +-Cast(INT64 -> STRING) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=STRING, value="NULL") + +-Literal(type=STRING, value=" ") + +-FunctionCall(ZetaSQL:ifnull(STRING, STRING) -> STRING) + +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-Literal(type=STRING, value="NULL") +[UNPARSED_SQL] +SELECT + barrierscan_5.a_1 AS Key, + barrierscan_5.a_2 AS Value +FROM + ( + SELECT + barrierscan_4.a_1 AS a_1, + barrierscan_4.a_2 AS a_2 + FROM + ( + SELECT + barrierscan_3.a_1 AS a_1, + barrierscan_3.a_2 AS a_2 + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS barrierscan_3 + WHERE + `IF`((barrierscan_3.a_1) = 10, true, ERROR(CONCAT("Assert failed: ", "key=10"))) + ) AS barrierscan_4 + WHERE + `IF`((barrierscan_4.a_1) = 11, true, ERROR(CONCAT("Assert failed: ", IFNULL(barrierscan_4.a_2, "NULL")))) + ) AS barrierscan_5 +WHERE + `IF`((barrierscan_5.a_1) = 11, true, ERROR(CONCAT("Assert failed: ", CONCAT(IFNULL(CAST(barrierscan_5.a_1 AS STRING), + "NULL"), " ", IFNULL(barrierscan_5.a_2, "NULL"))))); diff --git a/zetasql/analyzer/testdata/pipe_assert_sqlbuilder.test b/zetasql/analyzer/testdata/pipe_assert_sqlbuilder.test new file mode 100644 index 000000000..10a9c5cc0 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_assert_sqlbuilder.test @@ -0,0 +1,177 @@ +[default show_unparsed] +[default no_show_resolved_ast] +[default language_features=PIPES,PIPE_ASSERT] +# No payload. +FROM KeyValue +|> ASSERT key = 10 +-- +[UNPARSED_SQL] +SELECT + assertscan_4.a_1 AS Key, + assertscan_4.a_2 AS Value +FROM + ( + SELECT + keyvalue_3.a_1 AS a_1, + keyvalue_3.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3 + |> ASSERT(keyvalue_3.a_1) = 10, "key = 10" + ) + ) AS assertscan_4; +== + +# One payload. +FROM KeyValue +|> ASSERT key = 10, "payload1" +-- +[UNPARSED_SQL] +SELECT + assertscan_4.a_1 AS Key, + assertscan_4.a_2 AS Value +FROM + ( + SELECT + keyvalue_3.a_1 AS a_1, + keyvalue_3.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3 + |> ASSERT(keyvalue_3.a_1) = 10, "payload1" + ) + ) AS assertscan_4; +== + +# Multiple payloads. +FROM KeyValue +|> ASSERT key = 10, key, "payload2" +-- +[UNPARSED_SQL] +SELECT + assertscan_4.a_1 AS Key, + assertscan_4.a_2 AS Value +FROM + ( + SELECT + keyvalue_3.a_1 AS a_1, + keyvalue_3.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3 + |> ASSERT(keyvalue_3.a_1) = 10, CONCAT(IFNULL(CAST(keyvalue_3.a_1 AS STRING), "NULL"), " ", "payload2") + ) + ) AS assertscan_4; +== + +# Multiple ASSERT operators. +FROM KeyValue +|> ASSERT key = 10, "payload1" +|> ASSERT key = 20, "payload2" +-- +[UNPARSED_SQL] +SELECT + assertscan_5.a_1 AS Key, + assertscan_5.a_2 AS Value +FROM + ( + SELECT + assertscan_4.a_1 AS a_1, + assertscan_4.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + assertscan_4.a_1 AS a_1, + assertscan_4.a_2 AS a_2 + FROM + ( + SELECT + keyvalue_3.a_1 AS a_1, + keyvalue_3.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3 + |> ASSERT(keyvalue_3.a_1) = 10, "payload1" + ) + ) AS assertscan_4 + ) AS assertscan_4 + |> ASSERT(assertscan_4.a_1) = 20, "payload2" + ) + ) AS assertscan_5; +== + +# Nested ASSERT operators. +SELECT * FROM ( + FROM KeyValue + |> ASSERT key = 10, "payload1" +) +|> ASSERT key = 20, "payload2" +-- +[UNPARSED_SQL] +SELECT + assertscan_6.a_1 AS Key, + assertscan_6.a_2 AS Value +FROM + ( + SELECT + projectscan_5.a_1 AS a_1, + projectscan_5.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + assertscan_4.a_1 AS a_1, + assertscan_4.a_2 AS a_2 + FROM + ( + SELECT + keyvalue_3.a_1 AS a_1, + keyvalue_3.a_2 AS a_2 + FROM + ( + FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3 + |> ASSERT(keyvalue_3.a_1) = 10, "payload1" + ) + ) AS assertscan_4 + ) AS projectscan_5 + |> ASSERT(projectscan_5.a_1) = 20, "payload2" + ) + ) AS assertscan_6; diff --git a/zetasql/analyzer/testdata/pipe_call_tvf.test b/zetasql/analyzer/testdata/pipe_call_tvf.test new file mode 100644 index 000000000..c1cec316b --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_call_tvf.test @@ -0,0 +1,2566 @@ +[default also_show_signature_mismatch_details] +[language_features=PIPES] +select 1 x +|> call tvf() +-- +ERROR: Table-valued functions are not supported [at 2:9] +|> call tvf() + ^ +== + +[default language_features=PIPES,TABLE_VALUED_FUNCTIONS,NAMED_ARGUMENTS,CREATE_TABLE_FUNCTION,TEMPLATE_FUNCTIONS,FUNCTION_ARGUMENTS_WITH_DEFAULTS] +select 1 x +|> call tvf() +-- +ERROR: Table-valued function not found: tvf [at 2:9] +|> call tvf() + ^ +== + +select 1 x +|> call sqrt() +-- +ERROR: Table-valued function not found: sqrt [at 2:9] +|> call sqrt() + ^ +== + +select 1 x +|> call tvf_no_args() +-- +ERROR: Table-valued function cannot be called with pipe CALL syntax because its signature does not include a table-typed argument; Supported signature: TVF_NO_ARGS() [at 2:9] +|> call tvf_no_args() + ^ +== + +select 1 x +|> call tvf_exactly_3_int64_args(1,2) +-- +ERROR: Table-valued function cannot be called with pipe CALL syntax because its signature does not include a table-typed argument; Supported signature: TVF_EXACTLY_3_INT64_ARGS(INT64, INT64, INT64) [at 2:9] +|> call tvf_exactly_3_int64_args(1,2) + ^ +== + +# Not allowed because the first table arg is a named-only arg. +select 1 x +|> call tvf_two_named_only_tables() +-- +ERROR: Table-valued function cannot be called with pipe CALL syntax because its first table-typed argument is a named-only argument; Supported signature: TVF_TWO_NAMED_ONLY_TABLES([table1 => TABLE], [table2 => TABLE]) [at 2:9] +|> call tvf_two_named_only_tables() + ^ +== + +# Its signature is ANY TABLE->TABLE +# Test the error message for both regular and value tables since they pass in +# the is_pipe_input_table bit separately. +select {{|AS STRUCT}} 1 x +|> call tvf_one_relation_arg_with_fixed_output((select true)) +-- +ALTERNATION GROUP: +-- +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output for argument types: pipe_input:TABLE, BOOL. Supported signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output((select true)) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output + Argument types: pipe_input:TABLE, BOOL + Signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) + Signature accepts at most 1 argument, found 2 arguments [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output((select true)) + ^ +-- +ALTERNATION GROUP: AS STRUCT +-- +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output for argument types: pipe_input:TABLE>, BOOL. Supported signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output((select true)) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output + Argument types: pipe_input:TABLE>, BOOL + Signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) + Signature accepts at most 1 argument, found 2 arguments [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output((select true)) + ^ +== + +# Simple TVF call, with a hint. +select true, 5 +|> call tvf_one_relation_arg_with_fixed_output() @{hint=1} +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_with_fixed_output.column_bool#3 AS column_bool [BOOL] +| +-tvf_one_relation_arg_with_fixed_output.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_with_fixed_output.[column_bool#3, column_bytes#4] + +-hint_list= + | +-hint := Literal(type=INT64, value=1) + +-tvf=tvf_one_relation_arg_with_fixed_output((ANY TABLE) -> TABLE) + +-signature=(TABLE<$col1 BOOL, $col2 INT64>) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=$query.[$col1#1, $col2#2] + | | +-expr_list= + | | | +-$col1#1 := Literal(type=BOOL, value=true) + | | | +-$col2#2 := Literal(type=INT64, value=5) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=$query.[$col1#1, $col2#2] + +-column_index_list=[0, 1] +== + +select true +|> call tvf_one_relation_arg_with_fixed_output(6) +-- +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output for argument types: pipe_input:TABLE, INT64. Supported signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output(6) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output + Argument types: pipe_input:TABLE, INT64 + Signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) + Signature accepts at most 1 argument, found 2 arguments [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output(6) + ^ +== + +select true +|> call tvf_one_relation_arg_with_fixed_output(TABLE KeyValue) +-- +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output for argument types: pipe_input:TABLE, TABLE. Supported signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output(TABLE KeyValue) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_relation_arg_with_fixed_output + Argument types: pipe_input:TABLE, TABLE + Signature: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) + Signature accepts at most 1 argument, found 2 arguments [at 2:9] +|> call tvf_one_relation_arg_with_fixed_output(TABLE KeyValue) + ^ +== + +# Passing a table schema through. +select 1 x, true +|> call tvf_one_relation_arg_output_schema_is_input_schema() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.x#3 AS x [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.$col2#4 AS `$col2` [BOOL] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[x#3, $col2#4] + +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, $col2#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-$col2#2 := Literal(type=BOOL, value=true) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=$query.[x#1, $col2#2] + +-column_index_list=[0, 1] +== + +# Passing a value table through. +select as value vt from TestExtraValueTable vt +|> call tvf_one_relation_arg_output_schema_is_input_schema() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.$col0#4 AS `$col0` [PROTO] ++-is_value_table=TRUE ++-query= + +-TVFScan + +-column_list=[tvf_one_relation_arg_output_schema_is_input_schema.$col0#4] + +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE>) -> TABLE> + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[TestExtraValueTable.value#1] + | | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + | +-argument_column_list=[TestExtraValueTable.value#1] + +-column_index_list=[0] +== + +# Column access after pass-through TVF. +from KeyValue +|> call tvf_one_relation_arg_output_schema_is_input_schema() +|> where key = 1 +|> select * +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.Key#3 AS Key [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.Value#4 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + +-input_scan= + +-FilterScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + +-input_scan= + | +-TVFScan + | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | +-signature=(TABLE) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-column_index_list=[0, 1] + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=tvf_one_relation_arg_output_schema_is_input_schema.Key#3) + +-Literal(type=INT64, value=1) +== + +# This handles an optional table arg. +select "abc" +|> call tvf_one_optional_relation_arg_return_int64_value_table() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_optional_relation_arg_return_int64_value_table.$col0#2 AS `$col0` [INT64] ++-is_value_table=TRUE ++-query= + +-TVFScan + +-column_list=[tvf_one_optional_relation_arg_return_int64_value_table.$col0#2] + +-tvf=tvf_one_optional_relation_arg_return_int64_value_table((optional ANY TABLE) -> TABLE) + +-signature=(TABLE<$col1 STRING>) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$query.$col1#1] + | | +-expr_list= + | | | +-$col1#1 := Literal(type=STRING, value="abc") + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$query.$col1#1] + +-column_index_list=[0] +== + +select "abc" +|> call tvf_one_optional_relation_arg_return_int64_value_table(5) +-- +ERROR: No matching signature for tvf_one_optional_relation_arg_return_int64_value_table for argument types: pipe_input:TABLE, INT64. Supported signature: TVF_ONE_OPTIONAL_RELATION_ARG_RETURN_INT64_VALUE_TABLE([TABLE]) [at 2:9] +|> call tvf_one_optional_relation_arg_return_int64_value_table(5) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_optional_relation_arg_return_int64_value_table + Argument types: pipe_input:TABLE, INT64 + Signature: TVF_ONE_OPTIONAL_RELATION_ARG_RETURN_INT64_VALUE_TABLE([TABLE]) + Signature accepts at most 1 argument, found 2 arguments [at 2:9] +|> call tvf_one_optional_relation_arg_return_int64_value_table(5) + ^ +== + +select "abc" +|> call tvf_two_relation_args_return_proto_value_table() +-- +ERROR: No matching signature for tvf_two_relation_args_return_proto_value_table for argument types: pipe_input:TABLE. Supported signature: TVF_TWO_RELATION_ARGS_RETURN_PROTO_VALUE_TABLE(TABLE, TABLE) [at 2:9] +|> call tvf_two_relation_args_return_proto_value_table() + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_relation_args_return_proto_value_table + Argument types: pipe_input:TABLE + Signature: TVF_TWO_RELATION_ARGS_RETURN_PROTO_VALUE_TABLE(TABLE, TABLE) + Signature requires at least 2 arguments, found 1 argument [at 2:9] +|> call tvf_two_relation_args_return_proto_value_table() + ^ +== + +select "abc" +|> call tvf_two_relation_args_return_proto_value_table(TABLE KeyValue) +-- +QueryStmt ++-output_column_list= +| +-tvf_two_relation_args_return_proto_value_table.$col0#4 AS `$col0` [PROTO] ++-is_value_table=TRUE ++-query= + +-TVFScan + +-column_list=[tvf_two_relation_args_return_proto_value_table.$col0#4] + +-tvf=tvf_two_relation_args_return_proto_value_table((ANY TABLE, ANY TABLE) -> TABLE>) + +-signature=(TABLE<$col1 STRING>, TABLE) -> TABLE> + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$query.$col1#1] + | | | +-expr_list= + | | | | +-$col1#1 := Literal(type=STRING, value="abc") + | | | +-input_scan= + | | | +-SingleRowScan + | | +-argument_column_list=[$query.$col1#1] + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#2, Value#3], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#2, Value#3] + +-column_index_list=[0] +== + +select "abc" +|> call tvf_two_relation_args_return_proto_value_table((select 5), (select 6)) +-- +ERROR: No matching signature for tvf_two_relation_args_return_proto_value_table for argument types: pipe_input:TABLE, TABLE, INT64. Supported signature: TVF_TWO_RELATION_ARGS_RETURN_PROTO_VALUE_TABLE(TABLE, TABLE) [at 2:9] +|> call tvf_two_relation_args_return_proto_value_table((select 5), (select 6)) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_relation_args_return_proto_value_table + Argument types: pipe_input:TABLE, TABLE, INT64 + Signature: TVF_TWO_RELATION_ARGS_RETURN_PROTO_VALUE_TABLE(TABLE, TABLE) + Signature accepts at most 2 arguments, found 3 arguments [at 2:9] +|> call tvf_two_relation_args_return_proto_value_table((select 5), (select 6)) + ^ +== + +# This wants MODEL,TABLE but we end up calling it wth TABLE,TABLE. +# The error is a bit confusing because the argument table gets put in +# slot 1 since the pipe input goes in slot 2 (the expected TABLE arg). +select 1 +|> call tvf_model_evaluation_args(TABLE KeyValue) +-- +ERROR: No matching signature for tvf_model_evaluation_args for argument types: TABLE, pipe_input:TABLE. Supported signature: TVF_MODEL_EVALUATION_ARGS(MODEL, [TABLE], [STRUCT]) [at 2:9] +|> call tvf_model_evaluation_args(TABLE KeyValue) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_model_evaluation_args + Argument types: TABLE, pipe_input:TABLE + Signature: TVF_MODEL_EVALUATION_ARGS(MODEL, [TABLE], [STRUCT]) + Argument 1: expected MODEL, found TABLE [at 2:9] +|> call tvf_model_evaluation_args(TABLE KeyValue) + ^ +== + +from KeyValue +|> call tvf_model_evaluation_args(model onedoublemodel) +-- +QueryStmt ++-output_column_list= +| +-tvf_model_evaluation_args.column_bool#3 AS column_bool [BOOL] +| +-tvf_model_evaluation_args.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_model_evaluation_args.[column_bool#3, column_bytes#4] + +-tvf=tvf_model_evaluation_args((ANY MODEL, optional ANY TABLE, optional ) -> TABLE) + +-signature=(ANY MODEL, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-model= + | | +-Model(model=OneDoubleModel) + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0, 1] +== + +from KeyValue +|> call tvf_model_optional_string_optional_table(model onedoublemodel) +-- +ERROR: Table-valued function cannot be called with pipe CALL syntax because it has an optional argument before its first table-typed argument; Supported signature: TVF_MODEL_OPTIONAL_STRING_OPTIONAL_TABLE(MODEL, [STRING], [TABLE]) [at 2:9] +|> call tvf_model_optional_string_optional_table(model onedoublemodel) + ^ +== + +# The table argument is optional but this is allowed because the +# arguments before it are positional. +from KeyValue +|> call tvf_model_optional_table_named_string_default(model onedoublemodel) +-- +QueryStmt ++-output_column_list= +| +-tvf_model_optional_table_named_string_default.column_bool#3 AS column_bool [BOOL] +| +-tvf_model_optional_table_named_string_default.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_model_optional_table_named_string_default.[column_bool#3, column_bytes#4] + +-tvf=tvf_model_optional_table_named_string_default((ANY MODEL, optional ANY TABLE, optional STRING {default_value: "default"} foobar) -> TABLE) + +-signature=(ANY MODEL, TABLE, literal STRING) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-model= + | | +-Model(model=OneDoubleModel) + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-expr= + | +-Literal(type=STRING, value="default") + +-column_index_list=[0, 1] +== + +# The arguemnt after the optional table is an optional arg +# passed named or positionally. +from KeyValue +|> call tvf_model_optional_table_named_string_default( + model onedoublemodel, {{"abc"|foobar=>"abc"}}) +-- +QueryStmt ++-output_column_list= +| +-tvf_model_optional_table_named_string_default.column_bool#3 AS column_bool [BOOL] +| +-tvf_model_optional_table_named_string_default.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_model_optional_table_named_string_default.[column_bool#3, column_bytes#4] + +-tvf=tvf_model_optional_table_named_string_default((ANY MODEL, optional ANY TABLE, optional STRING {default_value: "default"} foobar) -> TABLE) + +-signature=(ANY MODEL, TABLE, literal STRING) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-model= + | | +-Model(model=OneDoubleModel) + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-expr= + | +-Literal(type=STRING, value="abc") + +-column_index_list=[0, 1] +== + +# This TVF expects TABLE with named columns. +select 5, "abc" +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +ERROR: Required column "int64" not found in table passed as argument 1 of TVF_ONE_RELATION_ARG_INT64_STRING_INPUT_COLUMNS(TABLE) [at 2:9] +|> call tvf_one_relation_arg_int64_string_input_columns() + ^ +== + +# Named column matching with out-of-order and extra columns. +select null, "abc" string, true xxx, 5 int64 +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_int64_string_input_columns.column_bool#5 AS column_bool [BOOL] +| +-tvf_one_relation_arg_int64_string_input_columns.column_bytes#6 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_int64_string_input_columns.[column_bool#5, column_bytes#6] + +-tvf=tvf_one_relation_arg_int64_string_input_columns((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=$query.[int64#4, string#2] + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[$col1#1, string#2, xxx#3, int64#4] + | | +-expr_list= + | | | +-$col1#1 := Literal(type=INT64, value=NULL) + | | | +-string#2 := Literal(type=STRING, value="abc") + | | | +-xxx#3 := Literal(type=BOOL, value=true) + | | | +-int64#4 := Literal(type=INT64, value=5) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=$query.[int64#4, string#2] + +-column_index_list=[0, 1] +== + +# The table scan produces lots of extra columns. +# They are pruned for TVF input with an extra ProjectScan. +# They are not pruned from the TableScan column_list because of SELECT *. +select * from SimpleTypes +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_int64_string_input_columns.column_bool#20 AS column_bool [BOOL] +| +-tvf_one_relation_arg_int64_string_input_columns.column_bytes#21 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_int64_string_input_columns.[column_bool#20, column_bytes#21] + +-tvf=tvf_one_relation_arg_int64_string_input_columns((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=SimpleTypes.[int64#2, string#5] + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=SimpleTypes.[int32#1, int64#2, uint32#3, uint64#4, string#5, bytes#6, bool#7, float#8, double#9, date#10, timestamp_seconds#11, timestamp_millis#12, timestamp_micros#13, timestamp_nanos#14, timestamp#15, numeric#16, bignumeric#17, json#18, uuid#19] + | | +-input_scan= + | | +-TableScan(column_list=SimpleTypes.[int32#1, int64#2, uint32#3, uint64#4, string#5, bytes#6, bool#7, float#8, double#9, date#10, timestamp_seconds#11, timestamp_millis#12, timestamp_micros#13, timestamp_nanos#14, timestamp#15, numeric#16, bignumeric#17, json#18, uuid#19], table=SimpleTypes, column_index_list=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]) + | +-argument_column_list=SimpleTypes.[int64#2, string#5] + +-column_index_list=[0, 1] +== + +# In this case where there is no SELECT *, the TableScan column_list +# does get pruned. +from SimpleTypes +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_int64_string_input_columns.column_bool#20 AS column_bool [BOOL] +| +-tvf_one_relation_arg_int64_string_input_columns.column_bytes#21 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_int64_string_input_columns.[column_bool#20, column_bytes#21] + +-tvf=tvf_one_relation_arg_int64_string_input_columns((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=SimpleTypes.[int64#2, string#5] + | | +-input_scan= + | | +-TableScan(column_list=SimpleTypes.[int64#2, string#5], table=SimpleTypes, column_index_list=[1, 4]) + | +-argument_column_list=SimpleTypes.[int64#2, string#5] + +-column_index_list=[0, 1] +== + +select 5 int64 +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +ERROR: Required column "string" not found in table passed as argument 1 of TVF_ONE_RELATION_ARG_INT64_STRING_INPUT_COLUMNS(TABLE) [at 2:9] +|> call tvf_one_relation_arg_int64_string_input_columns() + ^ +== + +# Coercion of input columns from the lhs table input. +select cast(5 as int32) int64, cast(null as string) string +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_int64_string_input_columns.column_bool#4 AS column_bool [BOOL] +| +-tvf_one_relation_arg_int64_string_input_columns.column_bytes#5 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_int64_string_input_columns.[column_bool#4, column_bytes#5] + +-tvf=tvf_one_relation_arg_int64_string_input_columns((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.int64#3, $query.string#2] + | | +-expr_list= + | | | +-int64#3 := + | | | +-Cast(INT32 -> INT64) + | | | +-ColumnRef(type=INT32, column=$query.int64#1) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[int64#1, string#2] + | | +-expr_list= + | | | +-int64#1 := Literal(type=INT32, value=5, has_explicit_type=TRUE) + | | | +-string#2 := Literal(type=STRING, value=NULL, has_explicit_type=TRUE) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$subquery1.int64#3, $query.string#2] + +-column_index_list=[0, 1] +== + +# Coercion of NULL literals doesn't work. (It doesn't in regular syntax either.) +select NULL int64, NULL string +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +ERROR: Invalid type INT64 for column "string STRING" of argument 1 of TVF_ONE_RELATION_ARG_INT64_STRING_INPUT_COLUMNS(TABLE) [at 2:9] +|> call tvf_one_relation_arg_int64_string_input_columns() + ^ +== + +# Duplicate column name. +select "abc" AS string, 5 AS int64, 6 as int64 +|> call tvf_one_relation_arg_int64_string_input_columns() +-- +ERROR: Table-valued function does not allow duplicate input columns named "int64" for argument 1 of TVF_ONE_RELATION_ARG_INT64_STRING_INPUT_COLUMNS(TABLE) [at 2:9] +|> call tvf_one_relation_arg_int64_string_input_columns() + ^ +== + +# TVF with two relation args with required schemas. +# Column matching happens on both. +select bool, uint32 as uint64, string from simpletypes +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns( + (select bool, `date`, string from simpletypes)) +-- +QueryStmt ++-output_column_list= +| +-tvf_two_relation_args_uint64_string_and_date_string_input_columns.column_bool#40 AS column_bool [BOOL] +| +-tvf_two_relation_args_uint64_string_and_date_string_input_columns.column_bytes#41 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_two_relation_args_uint64_string_and_date_string_input_columns.[column_bool#40, column_bytes#41] + +-tvf=tvf_two_relation_args_uint64_string_and_date_string_input_columns((TABLE, TABLE) -> TABLE) + +-signature=(TABLE, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$subquery2.uint64#39, SimpleTypes.string#5] + | | | +-expr_list= + | | | | +-uint64#39 := + | | | | +-Cast(UINT32 -> UINT64) + | | | | +-ColumnRef(type=UINT32, column=SimpleTypes.uint32#3) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=SimpleTypes.[bool#7, uint32#3, string#5] + | | | +-input_scan= + | | | +-TableScan(column_list=SimpleTypes.[uint32#3, string#5, bool#7], table=SimpleTypes, column_index_list=[2, 4, 6]) + | | +-argument_column_list=[$subquery2.uint64#39, SimpleTypes.string#5] + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=SimpleTypes.[date#29, string#24] + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=SimpleTypes.[bool#26, date#29, string#24] + | | +-input_scan= + | | +-TableScan(column_list=SimpleTypes.[string#24, bool#26, date#29], table=SimpleTypes, column_index_list=[4, 6, 9]) + | +-argument_column_list=SimpleTypes.[date#29, string#24] + +-column_index_list=[0, 1] +== + +# Missing column in the second table. +# The error reports this as argument 2. +select uint32 as uint64, string from simpletypes +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns( + (select `date` from simpletypes)) +-- +ERROR: Required column "string" not found in table passed as argument 2 of TVF_TWO_RELATION_ARGS_UINT64_STRING_AND_DATE_STRING_INPUT_COLUMNS(TABLE, TABLE) [at 3:5] + (select `date` from simpletypes)) + ^ +== + +select uint32 as uint64, string from simpletypes +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns( + (select `date`, string from simpletypes), 456) +-- +ERROR: No matching signature for tvf_two_relation_args_uint64_string_and_date_string_input_columns for argument types: pipe_input:TABLE, TABLE, INT64. Supported signature: TVF_TWO_RELATION_ARGS_UINT64_STRING_AND_DATE_STRING_INPUT_COLUMNS(TABLE, TABLE) [at 2:9] +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_relation_args_uint64_string_and_date_string_input_columns + Argument types: pipe_input:TABLE, TABLE, INT64 + Signature: TVF_TWO_RELATION_ARGS_UINT64_STRING_AND_DATE_STRING_INPUT_COLUMNS(TABLE, TABLE) + Signature accepts at most 2 arguments, found 3 arguments [at 2:9] +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns( + ^ +== + +# Pass scalar where a relation is expected. +select uint32 as uint64, string from simpletypes +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns(123) +-- +ERROR: Table-valued function tvf_two_relation_args_uint64_string_and_date_string_input_columns argument 2 must be a relation (i.e. table subquery) [at 2:75] +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns(123) + ^ +== + +# Expected value table input. +select as value Key from KeyValue +|> call tvf_one_relation_arg_int64_input_value_table() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_int64_input_value_table.column_bool#3 AS column_bool [BOOL] +| +-tvf_one_relation_arg_int64_input_value_table.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_int64_input_value_table.[column_bool#3, column_bytes#4] + +-tvf=tvf_one_relation_arg_int64_input_value_table((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#1] + | | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-argument_column_list=[KeyValue.Key#1] + +-column_index_list=[0, 1] +== + +# Expected value table input with non-value column input. +select Key from KeyValue +|> call tvf_one_relation_arg_int64_input_value_table() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_int64_input_value_table.column_bool#3 AS column_bool [BOOL] +| +-tvf_one_relation_arg_int64_input_value_table.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_int64_input_value_table.[column_bool#3, column_bytes#4] + +-tvf=tvf_one_relation_arg_int64_input_value_table((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#1] + | | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-argument_column_list=[KeyValue.Key#1] + +-column_index_list=[0, 1] +== + +select * from KeyValue +|> call tvf_one_relation_arg_int64_input_value_table() +-- +ERROR: Expected value table of type INT64 for argument 1 of TVF_ONE_RELATION_ARG_INT64_INPUT_VALUE_TABLE(TABLE) [at 2:9] +|> call tvf_one_relation_arg_int64_input_value_table() + ^ +== + +# Two chained TVFs. The first one returns a proto value table and +# the second one expects a value table with that proto. +select 1, 2 +|> call tvf_two_relation_args_return_proto_value_table((select 3, 4)) +|> call tvf_one_relation_arg_input_proto_value_table() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_input_proto_value_table.column_bool#6 AS column_bool [BOOL] +| +-tvf_one_relation_arg_input_proto_value_table.column_bytes#7 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_input_proto_value_table.[column_bool#6, column_bytes#7] + +-tvf=tvf_one_relation_arg_input_proto_value_table((TABLE>) -> TABLE) + +-signature=(TABLE>) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-TVFScan + | | +-column_list=[tvf_two_relation_args_return_proto_value_table.$col0#5] + | | +-tvf=tvf_two_relation_args_return_proto_value_table((ANY TABLE, ANY TABLE) -> TABLE>) + | | +-signature=(TABLE<$col1 INT64, $col2 INT64>, TABLE<$col1 INT64, $col2 INT64>) -> TABLE> + | | +-argument_list= + | | | +-FunctionArgument + | | | | +-scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$query.[$col1#1, $col2#2] + | | | | | +-expr_list= + | | | | | | +-$col1#1 := Literal(type=INT64, value=1) + | | | | | | +-$col2#2 := Literal(type=INT64, value=2) + | | | | | +-input_scan= + | | | | | +-SingleRowScan + | | | | +-argument_column_list=$query.[$col1#1, $col2#2] + | | | +-FunctionArgument + | | | +-scan= + | | | | +-ProjectScan + | | | | +-column_list=$subquery1.[$col1#3, $col2#4] + | | | | +-expr_list= + | | | | | +-$col1#3 := Literal(type=INT64, value=3) + | | | | | +-$col2#4 := Literal(type=INT64, value=4) + | | | | +-input_scan= + | | | | +-SingleRowScan + | | | +-argument_column_list=$subquery1.[$col1#3, $col2#4] + | | +-column_index_list=[0] + | +-argument_column_list=[tvf_two_relation_args_return_proto_value_table.$col0#5] + +-column_index_list=[0, 1] +== + +# More chained TVFs, including an identity TVF inside an rhs arg subquery. +select 1, 2 +|> call tvf_two_relation_args_return_proto_value_table( + ( + FROM KeyValue + |> call tvf_one_relation_arg_output_schema_is_input_schema() + ) + ) +|> call tvf_one_relation_arg_output_schema_is_input_schema() +|> call tvf_one_relation_arg_input_proto_value_table() +|> call tvf_one_relation_arg_output_schema_is_input_schema() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.column_bool#11 AS column_bool [BOOL] +| +-tvf_one_relation_arg_output_schema_is_input_schema.column_bytes#12 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[column_bool#11, column_bytes#12] + +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-TVFScan + | | +-column_list=tvf_one_relation_arg_input_proto_value_table.[column_bool#9, column_bytes#10] + | | +-tvf=tvf_one_relation_arg_input_proto_value_table((TABLE>) -> TABLE) + | | +-signature=(TABLE>) -> TABLE + | | +-argument_list= + | | | +-FunctionArgument + | | | +-scan= + | | | | +-TVFScan + | | | | +-column_list=[tvf_one_relation_arg_output_schema_is_input_schema.$col0#8] + | | | | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | | | | +-signature=(TABLE>) -> TABLE> + | | | | +-argument_list= + | | | | | +-FunctionArgument + | | | | | +-scan= + | | | | | | +-TVFScan + | | | | | | +-column_list=[tvf_two_relation_args_return_proto_value_table.$col0#7] + | | | | | | +-tvf=tvf_two_relation_args_return_proto_value_table((ANY TABLE, ANY TABLE) -> TABLE>) + | | | | | | +-signature=(TABLE<$col1 INT64, $col2 INT64>, TABLE) -> TABLE> + | | | | | | +-argument_list= + | | | | | | | +-FunctionArgument + | | | | | | | | +-scan= + | | | | | | | | | +-ProjectScan + | | | | | | | | | +-column_list=$query.[$col1#1, $col2#2] + | | | | | | | | | +-expr_list= + | | | | | | | | | | +-$col1#1 := Literal(type=INT64, value=1) + | | | | | | | | | | +-$col2#2 := Literal(type=INT64, value=2) + | | | | | | | | | +-input_scan= + | | | | | | | | | +-SingleRowScan + | | | | | | | | +-argument_column_list=$query.[$col1#1, $col2#2] + | | | | | | | +-FunctionArgument + | | | | | | | +-scan= + | | | | | | | | +-TVFScan + | | | | | | | | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#5, Value#6] + | | | | | | | | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | | | | | | | | +-signature=(TABLE) -> TABLE + | | | | | | | | +-argument_list= + | | | | | | | | | +-FunctionArgument + | | | | | | | | | +-scan= + | | | | | | | | | | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1]) + | | | | | | | | | +-argument_column_list=KeyValue.[Key#3, Value#4] + | | | | | | | | +-column_index_list=[0, 1] + | | | | | | | +-argument_column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#5, Value#6] + | | | | | | +-column_index_list=[0] + | | | | | +-argument_column_list=[tvf_two_relation_args_return_proto_value_table.$col0#7] + | | | | +-column_index_list=[0] + | | | +-argument_column_list=[tvf_one_relation_arg_output_schema_is_input_schema.$col0#8] + | | +-column_index_list=[0, 1] + | +-argument_column_list=tvf_one_relation_arg_input_proto_value_table.[column_bool#9, column_bytes#10] + +-column_index_list=[0, 1] +== + +# A TVF used in an expression subquery can do correlated references. +select ( + select * from i.structval + |> call tvf_one_relation_arg_one_int64_arg(col) + |> select column_bool + ) +from (select [struct("e" as string, 123 as int64)] as structval, 5 as col) as i +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#8 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#8] + +-expr_list= + | +-$col1#8 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=i.structval#1) + | | +-ColumnRef(type=INT64, column=i.col#2) + | +-subquery= + | +-ProjectScan + | +-column_list=[tvf_one_relation_arg_one_int64_arg.column_bool#6] + | +-input_scan= + | +-TVFScan + | +-column_list=[tvf_one_relation_arg_one_int64_arg.column_bool#6] + | +-tvf=tvf_one_relation_arg_one_int64_arg((ANY TABLE, INT64) -> TABLE) + | +-signature=(TABLE, INT64) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | | +-scan= + | | | | +-ProjectScan + | | | | +-column_list=$expr_subquery.[string#4, int64#5] + | | | | +-expr_list= + | | | | | +-string#4 := + | | | | | | +-GetStructField + | | | | | | +-type=STRING + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$array.structval#3) + | | | | | | +-field_idx=0 + | | | | | +-int64#5 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$array.structval#3) + | | | | | +-field_idx=1 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$array.structval#3] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=i.structval#1, is_correlated=TRUE) + | | | | +-element_column_list=[$array.structval#3] + | | | +-argument_column_list=$expr_subquery.[string#4, int64#5] + | | +-FunctionArgument + | | +-expr= + | | +-ColumnRef(type=INT64, column=i.col#2, is_correlated=TRUE) + | +-column_index_list=[0] + +-input_scan= + +-ProjectScan + +-column_list=i.[structval#1, col#2] + +-expr_list= + | +-structval#1 := Literal(type=ARRAY>, value=[{string:"e", int64:123}]) + | +-col#2 := Literal(type=INT64, value=5) + +-input_scan= + +-SingleRowScan +== + +# A TVF used in an UNNEST(ARRAY(...)) subquery can do correlated references +# to the lateral table on the left. +from (select [struct("e" as string, 123 as int64)] as structval, 5 as col) as i, + unnest(array( + select * from unnest(i.structval) + |> call tvf_one_relation_arg_one_int64_arg(i.col) + |> select column_bool)) +-- +QueryStmt ++-output_column_list= +| +-i.structval#1 AS structval [ARRAY>] +| +-i.col#2 AS col [INT64] +| +-$array.$unnest2#8 AS `$unnest2` [BOOL] ++-query= + +-ArrayScan + +-column_list=[i.structval#1, i.col#2, $array.$unnest2#8] + +-input_scan= + | +-ProjectScan + | +-column_list=i.[structval#1, col#2] + | +-expr_list= + | | +-structval#1 := Literal(type=ARRAY>, value=[{string:"e", int64:123}]) + | | +-col#2 := Literal(type=INT64, value=5) + | +-input_scan= + | +-SingleRowScan + +-array_expr_list= + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=ARRAY + | +-parameter_list= + | | +-ColumnRef(type=ARRAY>, column=i.structval#1) + | | +-ColumnRef(type=INT64, column=i.col#2) + | +-subquery= + | +-ProjectScan + | +-column_list=[tvf_one_relation_arg_one_int64_arg.column_bool#6] + | +-input_scan= + | +-TVFScan + | +-column_list=[tvf_one_relation_arg_one_int64_arg.column_bool#6] + | +-tvf=tvf_one_relation_arg_one_int64_arg((ANY TABLE, INT64) -> TABLE) + | +-signature=(TABLE, INT64) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | | +-scan= + | | | | +-ProjectScan + | | | | +-column_list=$expr_subquery.[string#4, int64#5] + | | | | +-expr_list= + | | | | | +-string#4 := + | | | | | | +-GetStructField + | | | | | | +-type=STRING + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$array.$unnest1#3) + | | | | | | +-field_idx=0 + | | | | | +-int64#5 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$array.$unnest1#3) + | | | | | +-field_idx=1 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$array.$unnest1#3] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=i.structval#1, is_correlated=TRUE) + | | | | +-element_column_list=[$array.$unnest1#3] + | | | +-argument_column_list=$expr_subquery.[string#4, int64#5] + | | +-FunctionArgument + | | +-expr= + | | +-ColumnRef(type=INT64, column=i.col#2, is_correlated=TRUE) + | +-column_index_list=[0] + +-element_column_list=[$array.$unnest2#8] +== + +# TVF gets a column directly from a table input on the lhs. +from KeyValue +|> call tvf_key_input_column_extra_input_columns_allowed() +-- +QueryStmt ++-output_column_list= +| +-tvf_key_input_column_extra_input_columns_allowed.column_bool#3 AS column_bool [BOOL] +| +-tvf_key_input_column_extra_input_columns_allowed.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_key_input_column_extra_input_columns_allowed.[column_bool#3, column_bytes#4] + +-tvf=tvf_key_input_column_extra_input_columns_allowed((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#1] + | | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-argument_column_list=[KeyValue.Key#1] + +-column_index_list=[0, 1] +== + +# TVF resolver doesn't match fields of value tables as expected columns. +(select as struct 5 key, 6 value) +|> call tvf_key_input_column_extra_input_columns_allowed() +-- +ERROR: Required column "key" not found in table passed as argument 1 of TVF_KEY_INPUT_COLUMN_EXTRA_INPUT_COLUMNS_ALLOWED(TABLE) [at 2:9] +|> call tvf_key_input_column_extra_input_columns_allowed() + ^ +== + +# Here the expected column from the lhs table requires a coercion. +from simpletypes +|> call tvf_uint64_input_column_named_uint32() +-- +QueryStmt ++-output_column_list= +| +-tvf_uint64_input_column_named_uint32.column_bool#21 AS column_bool [BOOL] +| +-tvf_uint64_input_column_named_uint32.column_bytes#22 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_uint64_input_column_named_uint32.[column_bool#21, column_bytes#22] + +-tvf=tvf_uint64_input_column_named_uint32((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.uint32#20] + | | +-expr_list= + | | | +-uint32#20 := + | | | +-Cast(UINT32 -> UINT64) + | | | +-ColumnRef(type=UINT32, column=SimpleTypes.uint32#3) + | | +-input_scan= + | | +-TableScan(column_list=[SimpleTypes.uint32#3], table=SimpleTypes, column_index_list=[2]) + | +-argument_column_list=[$subquery1.uint32#20] + +-column_index_list=[0, 1] +== + +# Value table on the lhs has pseudo-columns (RowId and Filename) +from TestExtraValueTable +|> where RowId is null +|> call tvf_one_relation_arg_with_fixed_output() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_with_fixed_output.column_bool#4 AS column_bool [BOOL] +| +-tvf_one_relation_arg_with_fixed_output.column_bytes#5 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_with_fixed_output.[column_bool#4, column_bytes#5] + +-tvf=tvf_one_relation_arg_with_fixed_output((ANY TABLE) -> TABLE) + +-signature=(TABLE>) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-FilterScan + | | +-column_list=TestExtraValueTable.[value#1, RowId#3] + | | +-input_scan= + | | | +-TableScan(column_list=TestExtraValueTable.[value#1, RowId#3], table=TestExtraValueTable, column_index_list=[0, 2]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$is_null(BYTES) -> BOOL) + | | +-ColumnRef(type=BYTES, column=TestExtraValueTable.RowId#3) + | +-argument_column_list=[TestExtraValueTable.value#1] + +-column_index_list=[0, 1] +== + +# Value table on the lhs has pseudo-columns and passes through as a value table. +from TestExtraValueTable +|> where RowId is null +|> call tvf_one_relation_arg_output_schema_is_input_schema() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.$col0#4 AS `$col0` [PROTO] ++-is_value_table=TRUE ++-query= + +-TVFScan + +-column_list=[tvf_one_relation_arg_output_schema_is_input_schema.$col0#4] + +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE>) -> TABLE> + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-FilterScan + | | +-column_list=TestExtraValueTable.[value#1, RowId#3] + | | +-input_scan= + | | | +-TableScan(column_list=TestExtraValueTable.[value#1, RowId#3], table=TestExtraValueTable, column_index_list=[0, 2]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$is_null(BYTES) -> BOOL) + | | +-ColumnRef(type=BYTES, column=TestExtraValueTable.RowId#3) + | +-argument_column_list=[TestExtraValueTable.value#1] + +-column_index_list=[0] +== + +# Pseudo-columns do not pass through. +from TestExtraValueTable +|> where RowId is null +|> call tvf_one_relation_arg_output_schema_is_input_schema() +|> where RowId is not null +-- +ERROR: Unrecognized name: RowId [at 4:10] +|> where RowId is not null + ^ +== + +# TVF in a nested catalog. +# Java test framework does not supported nested catalogs. +[no_java] +from EnumTable +|> call nested_catalog.nested_tvf_one() +-- +QueryStmt ++-output_column_list= +| +-nested_catalog.nested_tvf_one.key#7 AS key [INT64] ++-query= + +-TVFScan + +-column_list=[nested_catalog.nested_tvf_one.key#7] + +-tvf=nested_catalog.nested_tvf_one((TABLE) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.key#6] + | | +-expr_list= + | | | +-key#6 := + | | | +-Cast(INT32 -> INT64) + | | | +-ColumnRef(type=INT32, column=EnumTable.key#1) + | | +-input_scan= + | | +-TableScan(column_list=[EnumTable.key#1], table=EnumTable, column_index_list=[0]) + | +-argument_column_list=[$subquery1.key#6] + +-column_index_list=[0] +== + +# Templated TVF with SQL body that looks for some columns, ignores others. +select value, 111 as xx, key from KeyValue +|> call tvf_templated_select_relation_arg_using_column_names() +-- +QueryStmt ++-output_column_list= +| +-tvf_templated_select_relation_arg_using_column_names.key#4 AS key [INT64] +| +-tvf_templated_select_relation_arg_using_column_names.value#5 AS value [STRING] ++-query= + +-TVFScan + +-column_list=tvf_templated_select_relation_arg_using_column_names.[key#4, value#5] + +-tvf=tvf_templated_select_relation_arg_using_column_names((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Value#2, $query.xx#3, KeyValue.Key#1] + | | +-expr_list= + | | | +-xx#3 := Literal(type=INT64, value=111) + | | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=[KeyValue.Value#2, $query.xx#3, KeyValue.Key#1] + +-column_index_list=[0, 1] + +With Templated SQL TVF signature: + tvf_templated_select_relation_arg_using_column_names(TABLE) -> TABLE +containing resolved templated query: +QueryStmt ++-output_column_list= +| +-t.key#3 AS key [INT64] +| +-t.value#1 AS value [STRING] ++-query= + +-ProjectScan + +-column_list=t.[key#3, value#1] + +-input_scan= + +-RelationArgumentScan(column_list=t.[value#1, key#3], name="t") +== + +# Same as previous, directly from the table. +from KeyValue +|> call tvf_templated_select_relation_arg_using_column_names() +-- +QueryStmt ++-output_column_list= +| +-tvf_templated_select_relation_arg_using_column_names.key#3 AS key [INT64] +| +-tvf_templated_select_relation_arg_using_column_names.value#4 AS value [STRING] ++-query= + +-TVFScan + +-column_list=tvf_templated_select_relation_arg_using_column_names.[key#3, value#4] + +-tvf=tvf_templated_select_relation_arg_using_column_names((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0, 1] + +With Templated SQL TVF signature: + tvf_templated_select_relation_arg_using_column_names(TABLE) -> TABLE +containing resolved templated query: +QueryStmt ++-output_column_list= +| +-t.Key#1 AS key [INT64] +| +-t.Value#2 AS value [STRING] ++-query= + +-ProjectScan + +-column_list=t.[Key#1, Value#2] + +-input_scan= + +-RelationArgumentScan(column_list=t.[Key#1, Value#2], name="t") +== + +# Templated TVF with non-relation in arg 1. +select 42 +|> call tvf_templated_select_scalar_and_relation_args(5) +-- +ERROR: Required column "key" not found in table passed as argument 2 of TVF_TEMPLATED_SELECT_SCALAR_AND_RELATION_ARGS(INT64, TABLE) [at 2:9] +|> call tvf_templated_select_scalar_and_relation_args(5) + ^ +== + +select 5, 42 key, 6 y +|> call tvf_templated_select_scalar_and_relation_args(5) +-- +QueryStmt ++-output_column_list= +| +-tvf_templated_select_scalar_and_relation_args.key#4 AS key [INT64] ++-query= + +-TVFScan + +-column_list=[tvf_templated_select_scalar_and_relation_args.key#4] + +-tvf=tvf_templated_select_scalar_and_relation_args((INT64, TABLE) -> ANY TABLE) + +-signature=(literal INT64, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=5) + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$query.key#2] + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[$col1#1, key#2, y#3] + | | +-expr_list= + | | | +-$col1#1 := Literal(type=INT64, value=5) + | | | +-key#2 := Literal(type=INT64, value=42) + | | | +-y#3 := Literal(type=INT64, value=6) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$query.key#2] + +-column_index_list=[0] + +With Templated SQL TVF signature: + tvf_templated_select_scalar_and_relation_args(literal INT64, TABLE) -> TABLE +containing resolved templated query: +QueryStmt ++-output_column_list= +| +-t.key#1 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=[t.key#1] + +-input_scan= + +-FilterScan + +-column_list=[t.key#1] + +-input_scan= + | +-RelationArgumentScan(column_list=[t.key#1], name="t") + +-filter_expr= + +-FunctionCall(ZetaSQL:$less(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=t.key#1) + +-ArgumentRef(type=INT64, name="x") +== + +select 42 +|> call tvf_templated_select_scalar_and_relation_args("abc") +-- +ERROR: No matching signature for tvf_templated_select_scalar_and_relation_args for argument types: STRING, pipe_input:TABLE. Supported signature: TVF_TEMPLATED_SELECT_SCALAR_AND_RELATION_ARGS(INT64, TABLE) [at 2:9] +|> call tvf_templated_select_scalar_and_relation_args("abc") + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_templated_select_scalar_and_relation_args + Argument types: STRING, pipe_input:TABLE + Signature: TVF_TEMPLATED_SELECT_SCALAR_AND_RELATION_ARGS(INT64, TABLE) + Argument 1: Unable to coerce type STRING to expected type INT64 [at 2:9] +|> call tvf_templated_select_scalar_and_relation_args("abc") + ^ +== + +select 42 +|> call tvf_templated_select_scalar_and_relation_args() +-- +ERROR: Table-valued function call in pipe CALL requires at least 1 positional argument because its first table argument is in position 2, but the call has no arguments; Supported signature: TVF_TEMPLATED_SELECT_SCALAR_AND_RELATION_ARGS(INT64, TABLE) [at 2:9] +|> call tvf_templated_select_scalar_and_relation_args() + ^ +== + +select 42 +|> call tvf_templated_select_scalar_and_relation_args(TABLE KeyValue) +-- +ERROR: No matching signature for tvf_templated_select_scalar_and_relation_args for argument types: TABLE, pipe_input:TABLE. Supported signature: TVF_TEMPLATED_SELECT_SCALAR_AND_RELATION_ARGS(INT64, TABLE) [at 2:9] +|> call tvf_templated_select_scalar_and_relation_args(TABLE KeyValue) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_templated_select_scalar_and_relation_args + Argument types: TABLE, pipe_input:TABLE + Signature: TVF_TEMPLATED_SELECT_SCALAR_AND_RELATION_ARGS(INT64, TABLE) + Argument 1: expected INT64, found TABLE [at 2:9] +|> call tvf_templated_select_scalar_and_relation_args(TABLE KeyValue) + ^ +== + +# Templated SQL TVF with two relations. +# SQLBuilder can't handle this case where one TVF outputs two columns with +# duplicate names because it's impossible to write an explicit select list. +[no_run_unparser] +from KeyValue +|> call tvf_templated_select_two_relation_args(table KeyValue) +-- +QueryStmt ++-output_column_list= +| +-tvf_templated_select_two_relation_args.key#5 AS key [INT64] +| +-tvf_templated_select_two_relation_args.Value#6 AS Value [STRING] +| +-tvf_templated_select_two_relation_args.Value#7 AS Value [STRING] ++-query= + +-TVFScan + +-column_list=tvf_templated_select_two_relation_args.[key#5, Value#6, Value#7] + +-tvf=tvf_templated_select_two_relation_args((ANY TABLE, ANY TABLE) -> ANY TABLE) + +-signature=(TABLE, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#3, Value#4] + +-column_index_list=[0, 1, 2] + +With Templated SQL TVF signature: + tvf_templated_select_two_relation_args(TABLE, TABLE) -> TABLE +containing resolved templated query: +QueryStmt ++-output_column_list= +| +-w1.Key#5 AS key [INT64] +| +-w1.Value#6 AS Value [STRING] +| +-w2.Value#8 AS Value [STRING] ++-query= + +-WithScan + +-column_list=[w1.Key#5, w1.Value#6, w2.Value#8] + +-is_ordered=TRUE + +-with_entry_list= + | +-WithEntry + | | +-with_query_name="w1" + | | +-with_subquery= + | | +-ProjectScan + | | +-column_list=s.[Key#1, Value#2] + | | +-input_scan= + | | +-RelationArgumentScan(column_list=s.[Key#1, Value#2], name="s") + | +-WithEntry + | +-with_query_name="w2" + | +-with_subquery= + | +-ProjectScan + | +-column_list=t.[Key#3, Value#4] + | +-input_scan= + | +-RelationArgumentScan(column_list=t.[Key#3, Value#4], name="t") + +-query= + +-LimitOffsetScan + +-column_list=[w1.Key#5, w1.Value#6, w2.Value#8] + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=[w1.Key#5, w1.Value#6, w2.Value#8] + | +-is_ordered=TRUE + | +-input_scan= + | | +-JoinScan + | | +-column_list=[w1.Key#5, w1.Value#6, w2.Key#7, w2.Value#8] + | | +-left_scan= + | | | +-WithRefScan(column_list=w1.[Key#5, Value#6], with_query_name="w1") + | | +-right_scan= + | | | +-WithRefScan(column_list=w2.[Key#7, Value#8], with_query_name="w2") + | | +-join_expr= + | | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | +-ColumnRef(type=INT64, column=w1.Key#5) + | | | +-ColumnRef(type=INT64, column=w2.Key#7) + | | +-has_using=TRUE + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=111-114 + | +-column_ref= + | +-ColumnRef(type=INT64, column=w1.Key#5) + +-limit= + +-Literal(type=INT64, value=1) +== + +# Templated SQL TVF taking a value table. +from TestStructValueTable +|> call tvf_templated_select_a() +-- +QueryStmt ++-output_column_list= +| +-tvf_templated_select_a.a#2 AS a [INT32] ++-query= + +-TVFScan + +-column_list=[tvf_templated_select_a.a#2] + +-tvf=tvf_templated_select_a((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE>) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=[TestStructValueTable.value#1], table=TestStructValueTable, column_index_list=[0]) + | +-argument_column_list=[TestStructValueTable.value#1] + +-column_index_list=[0] + +With Templated SQL TVF signature: + tvf_templated_select_a(TABLE>) -> TABLE +containing resolved templated query: +QueryStmt ++-output_column_list= +| +-$query.a#2 AS a [INT32] ++-query= + +-ProjectScan + +-column_list=[$query.a#2] + +-expr_list= + | +-a#2 := + | +-GetStructField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=x.$value_column#1) + | +-field_idx=0 + +-input_scan= + +-RelationArgumentScan(column_list=[x.$value_column#1], name="x", is_value_table=TRUE) +== + +# Two input tables from WITH clause, passed as lhs and rhs table inputs +# with required schemas, using TABLE or FROM for the argument. +with q1 as (select uint64, string from SimpleTypes), + q2 as (select date, string from SimpleTypes) +from q1 +|> call tvf_two_relation_args_uint64_string_and_date_string_input_columns( + {{TABLE q2|(FROM q2)}}) +-- +QueryStmt ++-output_column_list= +| +-tvf_two_relation_args_uint64_string_and_date_string_input_columns.column_bool#43 AS column_bool [BOOL] +| +-tvf_two_relation_args_uint64_string_and_date_string_input_columns.column_bytes#44 AS column_bytes [BYTES] ++-query= + +-WithScan + +-column_list=tvf_two_relation_args_uint64_string_and_date_string_input_columns.[column_bool#43, column_bytes#44] + +-with_entry_list= + | +-WithEntry + | | +-with_query_name="q1" + | | +-with_subquery= + | | +-ProjectScan + | | +-column_list=SimpleTypes.[uint64#4, string#5] + | | +-input_scan= + | | +-TableScan(column_list=SimpleTypes.[uint64#4, string#5], table=SimpleTypes, column_index_list=[3, 4]) + | +-WithEntry + | +-with_query_name="q2" + | +-with_subquery= + | +-ProjectScan + | +-column_list=SimpleTypes.[date#29, string#24] + | +-input_scan= + | +-TableScan(column_list=SimpleTypes.[string#24, date#29], table=SimpleTypes, column_index_list=[4, 9]) + +-query= + +-TVFScan + +-column_list=tvf_two_relation_args_uint64_string_and_date_string_input_columns.[column_bool#43, column_bytes#44] + +-tvf=tvf_two_relation_args_uint64_string_and_date_string_input_columns((TABLE, TABLE) -> TABLE) + +-signature=(TABLE, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-WithRefScan(column_list=q1.[uint64#39, string#40], with_query_name="q1") + | | +-argument_column_list=q1.[uint64#39, string#40] + | +-FunctionArgument + | +-scan= + | | +-WithRefScan(column_list=q2.[date#41, string#42], with_query_name="q2") + | +-argument_column_list=q2.[date#41, string#42] + +-column_index_list=[0, 1] +== + +# This TVF takes a required table, optional scalar, and optionally named table. +# It works to pass the optional table after the optional scalar positionally. +from KeyValue +|> call tvf_table_optional_scalar_named_table( + 10, (select 20)) +-- +QueryStmt ++-output_column_list= +| +-tvf_table_optional_scalar_named_table.column_bool#4 AS column_bool [BOOL] +| +-tvf_table_optional_scalar_named_table.column_bytes#5 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_table_optional_scalar_named_table.[column_bool#4, column_bytes#5] + +-tvf=tvf_table_optional_scalar_named_table((ANY TABLE, optional ANY TYPE, optional ANY TABLE foobar) -> TABLE) + +-signature=(TABLE, literal INT64, TABLE<$col1 INT64>) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=10) + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := Literal(type=INT64, value=20) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$subquery1.$col1#3] + +-column_index_list=[0, 1] +== + +# It works to pass the optional table named after the optional scalar. +from KeyValue +|> call tvf_table_optional_scalar_named_table( + 10, foobar=>(select 20)) +-- +QueryStmt ++-output_column_list= +| +-tvf_table_optional_scalar_named_table.column_bool#4 AS column_bool [BOOL] +| +-tvf_table_optional_scalar_named_table.column_bytes#5 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_table_optional_scalar_named_table.[column_bool#4, column_bytes#5] + +-tvf=tvf_table_optional_scalar_named_table((ANY TABLE, optional ANY TYPE, optional ANY TABLE foobar) -> TABLE) + +-signature=(TABLE, literal INT64, TABLE<$col1 INT64>) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=10) + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := Literal(type=INT64, value=20) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$subquery1.$col1#3] + +-column_index_list=[0, 1] +== + +# It works to pass the optional table named without the optional scalar. +from KeyValue +|> call tvf_table_optional_scalar_named_table( + foobar=>(select 20)) +-- +QueryStmt ++-output_column_list= +| +-tvf_table_optional_scalar_named_table.column_bool#4 AS column_bool [BOOL] +| +-tvf_table_optional_scalar_named_table.column_bytes#5 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_table_optional_scalar_named_table.[column_bool#4, column_bytes#5] + +-tvf=tvf_table_optional_scalar_named_table((ANY TABLE, optional ANY TYPE, optional ANY TABLE foobar) -> TABLE) + +-signature=(TABLE, null INT64, TABLE<$col1 INT64>) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=NULL) + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := Literal(type=INT64, value=20) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$subquery1.$col1#3] + +-column_index_list=[0, 1] +== + +# Too many args. +from KeyValue +|> call tvf_table_optional_scalar_named_table( + 10, 11, foobar=>(select 20)) +-- +ERROR: Table-valued function tvf_table_optional_scalar_named_table argument 3 ('foobar') must be a relation (i.e. table subquery) [at 3:10] + 10, 11, foobar=>(select 20)) + ^ +== + +from KeyValue +|> call tvf_table_optional_scalar_named_table( + foobar=>17) +-- +ERROR: Table-valued function tvf_table_optional_scalar_named_table argument 3 ('foobar') must be a relation (i.e. table subquery) [at 3:14] + foobar=>17) + ^ +== + +# Call TVF that does not accept extra columns in the input table. +select value as string, key + 1 as int64, 42 as extra_col from keyvalue +|> call tvf_one_relation_arg_only_int64_string_input_columns() +-- +ERROR: Function does not allow extra input column named "extra_col" for argument 1 of TVF_ONE_RELATION_ARG_ONLY_INT64_STRING_INPUT_COLUMNS(TABLE) [at 2:9] +|> call tvf_one_relation_arg_only_int64_string_input_columns() + ^ +== + +# Call TVF where one of the expected columns is a pseudo-column. +# This does not work currently. +from EnumTable +|> call tvf_key_filename_input_columns() +-- +ERROR: Required column "filename" not found in table passed as argument 1 of TVF_KEY_FILENAME_INPUT_COLUMNS(TABLE) [at 2:9] +|> call tvf_key_filename_input_columns() + ^ +== + +# DESCRIPTOR arg. +select 42 as int64a +|> call tvf_one_relation_arg_one_descriptor(descriptor(test_col)) +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_one_descriptor.column_bool#2 AS column_bool [BOOL] +| +-tvf_one_relation_arg_one_descriptor.column_bytes#3 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_one_descriptor.[column_bool#2, column_bytes#3] + +-tvf=tvf_one_relation_arg_one_descriptor((TABLE, ANY DESCRIPTOR) -> TABLE) + +-signature=(TABLE, ANY DESCRIPTOR) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$query.int64a#1] + | | | +-expr_list= + | | | | +-int64a#1 := Literal(type=INT64, value=42) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-argument_column_list=[$query.int64a#1] + | +-FunctionArgument + | +-descriptor_arg= + | +-Descriptor(descriptor_column_name_list=[test_col]) + +-column_index_list=[0, 1] +== + +# Wrong type for DESCRIPTOR arg. +select 42 as int64a +|> call tvf_one_relation_arg_one_descriptor((select 1)) +-- +ERROR: Table-valued function tvf_one_relation_arg_one_descriptor argument 2 must be a DESCRIPTOR [at 2:45] +|> call tvf_one_relation_arg_one_descriptor((select 1)) + ^ +== + +# Wrong type for DESCRIPTOR arg. +select 42 as int64a +|> call tvf_one_relation_arg_one_descriptor(15) +-- +ERROR: Table-valued function tvf_one_relation_arg_one_descriptor argument 2 must be a DESCRIPTOR [at 2:45] +|> call tvf_one_relation_arg_one_descriptor(15) + ^ +== + +# Wrong type for DESCRIPTOR arg. +select 42 as int64a +|> call tvf_one_relation_arg_one_descriptor(model onedoublemodel) +-- +ERROR: No matching signature for tvf_one_relation_arg_one_descriptor for argument types: pipe_input:TABLE, MODEL. Supported signature: TVF_ONE_RELATION_ARG_ONE_DESCRIPTOR(TABLE, DESCRIPTOR) [at 2:9] +|> call tvf_one_relation_arg_one_descriptor(model onedoublemodel) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_relation_arg_one_descriptor + Argument types: pipe_input:TABLE, MODEL + Signature: TVF_ONE_RELATION_ARG_ONE_DESCRIPTOR(TABLE, DESCRIPTOR) + Argument 2: expected DESCRIPTOR, found MODEL [at 2:9] +|> call tvf_one_relation_arg_one_descriptor(model onedoublemodel) + ^ +== + +# DESCRIPTOR arg with resolved names. +# The `descriptor_column_list` has a column from the pipe input table. +select 42 as int64a +|> call tvf_one_relation_arg_one_descriptor_resolved_names(descriptor(int64a)) +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_one_descriptor_resolved_names.column_bool#2 AS column_bool [BOOL] +| +-tvf_one_relation_arg_one_descriptor_resolved_names.column_bytes#3 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_one_descriptor_resolved_names.[column_bool#2, column_bytes#3] + +-tvf=tvf_one_relation_arg_one_descriptor_resolved_names((TABLE, ANY DESCRIPTOR) -> TABLE) + +-signature=(TABLE, ANY DESCRIPTOR) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$query.int64a#1] + | | | +-expr_list= + | | | | +-int64a#1 := Literal(type=INT64, value=42) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-argument_column_list=[$query.int64a#1] + | +-FunctionArgument + | +-descriptor_arg= + | +-Descriptor(descriptor_column_list=[$query.int64a#1], descriptor_column_name_list=[int64a]) + +-column_index_list=[0, 1] +== + +# DESCRIPTOR arg with missing resolved names. +select 42 as int64a +|> call tvf_one_relation_arg_one_descriptor_resolved_names(descriptor(int64axxx)) +-- +ERROR: DESCRIPTOR specifies int64axxx, which does not exist in the table passed as argument 1 [at 2:71] +...ne_descriptor_resolved_names(descriptor(int64axxx)) + ^ +== + +# MODEL argument +# Java does not support MODELs. +[no_java] +from KeyValue +|> call tvf_one_relation_one_model_arg_with_fixed_output(model onedoublemodel) +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_one_model_arg_with_fixed_output.double#3 AS double [DOUBLE] +| +-tvf_one_relation_one_model_arg_with_fixed_output.string#4 AS string [STRING] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_one_model_arg_with_fixed_output.[double#3, string#4] + +-tvf=tvf_one_relation_one_model_arg_with_fixed_output((TABLE, ANY MODEL) -> TABLE) + +-signature=(TABLE, ANY MODEL) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-model= + | +-Model(model=OneDoubleModel) + +-column_index_list=[0, 1] +== + +# MODEL argument with missing columns. +# Java does not support MODELs. +[no_java] +select 1 a +|> call tvf_one_relation_one_model_arg_with_fixed_output(model onedoublemodel) +-- +ERROR: Function does not allow extra input column named "a" for argument 1 of TVF_ONE_RELATION_ONE_MODEL_ARG_WITH_FIXED_OUTPUT(TABLE, MODEL) [at 2:9] +|> call tvf_one_relation_one_model_arg_with_fixed_output(model onedoublemodel) + ^ +== + +# Calling a TVF with an alias, producing a range variable. +from KeyValue +|> call tvf_one_relation_arg_output_schema_is_input_schema() AS tvf1 +|> WHERE tvf1.key = 4 AND value = 'abc' +|> call tvf_one_relation_arg_output_schema_is_input_schema() tvf2 +|> WHERE tvf2.key = 5 +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.Key#5 AS Key [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.Value#6 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#5, Value#6] + +-input_scan= + | +-TVFScan + | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#5, Value#6] + | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | +-signature=(TABLE) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | +-scan= + | | | +-FilterScan + | | | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + | | | +-input_scan= + | | | | +-TVFScan + | | | | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + | | | | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | | | | +-signature=(TABLE) -> TABLE + | | | | +-argument_list= + | | | | | +-FunctionArgument + | | | | | +-scan= + | | | | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | | | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | | | | +-column_index_list=[0, 1] + | | | | +-alias="tvf1" + | | | +-filter_expr= + | | | +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + | | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | | +-ColumnRef(type=INT64, column=tvf_one_relation_arg_output_schema_is_input_schema.Key#3) + | | | | +-Literal(type=INT64, value=4) + | | | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | | | +-ColumnRef(type=STRING, column=tvf_one_relation_arg_output_schema_is_input_schema.Value#4) + | | | +-Literal(type=STRING, value="abc") + | | +-argument_column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + | +-column_index_list=[0, 1] + | +-alias="tvf2" + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=tvf_one_relation_arg_output_schema_is_input_schema.Key#5) + +-Literal(type=INT64, value=5) +== + +# Range variable from the earlier TVF is not still visible. +from KeyValue +|> call tvf_one_relation_arg_output_schema_is_input_schema() AS tvf1 +|> call tvf_one_relation_arg_output_schema_is_input_schema() tvf2 +|> WHERE tvf1.key = 5 +-- +ERROR: Unrecognized name: tvf1; Did you mean tvf2? [at 4:10] +|> WHERE tvf1.key = 5 + ^ +== + +# JOIN a pipe and regular TVF call using their aliases in an ON clause. +from KeyValue +|> call tvf_one_relation_arg_output_schema_is_input_schema() AS tvf1 +|> JOIN + tvf_one_relation_arg_output_schema_is_input_schema(TABLE KeyValue) AS tvf2 + ON tvf1.key = tvf2.key +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.Key#3 AS Key [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.Value#4 AS Value [STRING] +| +-tvf_one_relation_arg_output_schema_is_input_schema.Key#7 AS Key [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.Value#8 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4, Key#7, Value#8] + +-left_scan= + | +-TVFScan + | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#3, Value#4] + | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | +-signature=(TABLE) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-column_index_list=[0, 1] + | +-alias="tvf1" + +-right_scan= + | +-TVFScan + | +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Key#7, Value#8] + | +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + | +-signature=(TABLE) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#5, Value#6], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#5, Value#6] + | +-column_index_list=[0, 1] + | +-alias="tvf2" + +-join_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=tvf_one_relation_arg_output_schema_is_input_schema.Key#3) + +-ColumnRef(type=INT64, column=tvf_one_relation_arg_output_schema_is_input_schema.Key#7) +== + +# TVF returning a value table, accessed by its alias. +from KeyValue +|> call tvf_one_optional_relation_arg_return_int64_value_table() AS val +|> where val=5 +-- +QueryStmt ++-output_column_list= +| +-tvf_one_optional_relation_arg_return_int64_value_table.$col0#3 AS `$value` [INT64] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[tvf_one_optional_relation_arg_return_int64_value_table.$col0#3] + +-input_scan= + | +-TVFScan + | +-column_list=[tvf_one_optional_relation_arg_return_int64_value_table.$col0#3] + | +-tvf=tvf_one_optional_relation_arg_return_int64_value_table((optional ANY TABLE) -> TABLE) + | +-signature=(TABLE) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-column_index_list=[0] + | +-alias="val" + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=tvf_one_optional_relation_arg_return_int64_value_table.$col0#3) + +-Literal(type=INT64, value=5) +== + +[language_features=PIPES,TABLE_VALUED_FUNCTIONS,NAMED_ARGUMENTS,V_1_3_INLINE_LAMBDA_ARGUMENT] +FROM KeyValue +|> call tvf_relation_arg_scalar_arg_named_lambda(1, named_lambda => e -> e) +-- +ERROR: Lambda arguments are not implemented for TVF [at 2:69] +|> call tvf_relation_arg_scalar_arg_named_lambda(1, named_lambda => e -> e) + ^ +== + +FROM KeyValue +|> CALL tvf_one_relation_arg_one_int64_arg(10) +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_one_int64_arg.column_bool#3 AS column_bool [BOOL] +| +-tvf_one_relation_arg_one_int64_arg.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_one_int64_arg.[column_bool#3, column_bytes#4] + +-tvf=tvf_one_relation_arg_one_int64_arg((ANY TABLE, INT64) -> TABLE) + +-signature=(TABLE, literal INT64) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-expr= + | +-Literal(type=INT64, value=10) + +-column_index_list=[0, 1] +== + +# The TVF in CALL cannot use columns from the input table as arguments. +# The TVF is called once so cannot use references to these columns +# that have different values per row. +FROM KeyValue +|> CALL tvf_one_relation_arg_one_int64_arg(key) +-- +ERROR: Unrecognized name: key [at 2:44] +|> CALL tvf_one_relation_arg_one_int64_arg(key) + ^ +== + +# Referencing columns from the outer query is allowed inside an expression +# subquery. These columns are constant for the CALL, which happens once +# per subquery execution. Columns from the input table inside the subquery +# are not visible. +# +# The alternation `key` shows that the inner `key` column is ignored and does +# not hide the outer `key` column. +FROM KeyValue t1 +|> EXTEND 123 AS outer_column +|> EXTEND + (FROM KeyValue t2 + |> EXTEND 456 AS inner_column + |> CALL tvf_one_relation_arg_one_int64_arg( + {{t1.key|t2.key|key|outer_column|inner_column}}) + |> SELECT key) +-- +ALTERNATION GROUPS: + t1.key + key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_extend.outer_column#3 AS outer_column [INT64] +| +-$pipe_extend.$col1#10 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.outer_column#3, $pipe_extend.$col1#10] + +-expr_list= + | +-$col1#10 := + | +-SubqueryExpr + | +-type=INT64 + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-subquery= + | +-ProjectScan + | +-column_list=[$pipe_select.key#9] + | +-expr_list= + | | +-key#9 := ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) + | +-input_scan= + | +-TVFScan + | +-tvf=tvf_one_relation_arg_one_int64_arg((ANY TABLE, INT64) -> TABLE) + | +-signature=(TABLE, INT64) -> TABLE + | +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[KeyValue.Key#4, KeyValue.Value#5, $pipe_extend.inner_column#6] + | | | +-expr_list= + | | | | +-inner_column#6 := Literal(type=INT64, value=456) + | | | +-input_scan= + | | | +-TableScan(column_list=KeyValue.[Key#4, Value#5], table=KeyValue, column_index_list=[0, 1], alias="t2") + | | +-argument_column_list=[KeyValue.Key#4, KeyValue.Value#5, $pipe_extend.inner_column#6] + | +-FunctionArgument + | +-expr= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.outer_column#3] + +-expr_list= + | +-outer_column#3 := Literal(type=INT64, value=123) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="t1") +-- +ALTERNATION GROUP: t2.key +-- +ERROR: Unrecognized name: t2 [at 7:17] + t2.key) + ^ +-- +ALTERNATION GROUP: outer_column +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_extend.outer_column#3 AS outer_column [INT64] +| +-$pipe_extend.$col1#10 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.outer_column#3, $pipe_extend.$col1#10] + +-expr_list= + | +-$col1#10 := + | +-SubqueryExpr + | +-type=INT64 + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-ColumnRef(type=INT64, column=$pipe_extend.outer_column#3) + | +-subquery= + | +-ProjectScan + | +-column_list=[$pipe_select.key#9] + | +-expr_list= + | | +-key#9 := ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) + | +-input_scan= + | +-TVFScan + | +-tvf=tvf_one_relation_arg_one_int64_arg((ANY TABLE, INT64) -> TABLE) + | +-signature=(TABLE, INT64) -> TABLE + | +-argument_list= + | +-FunctionArgument + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[KeyValue.Key#4, KeyValue.Value#5, $pipe_extend.inner_column#6] + | | | +-expr_list= + | | | | +-inner_column#6 := Literal(type=INT64, value=456) + | | | +-input_scan= + | | | +-TableScan(column_list=KeyValue.[Key#4, Value#5], table=KeyValue, column_index_list=[0, 1], alias="t2") + | | +-argument_column_list=[KeyValue.Key#4, KeyValue.Value#5, $pipe_extend.inner_column#6] + | +-FunctionArgument + | +-expr= + | +-ColumnRef(type=INT64, column=$pipe_extend.outer_column#3, is_correlated=TRUE) + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.outer_column#3] + +-expr_list= + | +-outer_column#3 := Literal(type=INT64, value=123) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="t1") +-- +ALTERNATION GROUP: inner_column +-- +ERROR: Unrecognized name: inner_column [at 7:17] + inner_column) + ^ +== + +# Call a TVF where the table arg is in the second position. +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg({{5|"ab"|TABLE TestTable}}) +-- +ALTERNATION GROUP: 5 +-- +QueryStmt ++-output_column_list= +| +-tvf_one_int64_arg_one_relation_arg.column_bool#3 AS column_bool [BOOL] +| +-tvf_one_int64_arg_one_relation_arg.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_int64_arg_one_relation_arg.[column_bool#3, column_bytes#4] + +-tvf=tvf_one_int64_arg_one_relation_arg((INT64, ANY TABLE) -> TABLE) + +-signature=(literal INT64, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=5) + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0, 1] +-- +ALTERNATION GROUP: "ab" +-- +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg for argument types: STRING, pipe_input:TABLE. Supported signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg("ab") + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg + Argument types: STRING, pipe_input:TABLE + Signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) + Argument 1: Unable to coerce type STRING to expected type INT64 [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg("ab") + ^ +-- +ALTERNATION GROUP: TABLE TestTable +-- +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg for argument types: TABLE, pipe_input:TABLE. Supported signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg(TABLE TestTable) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg + Argument types: TABLE, pipe_input:TABLE + Signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) + Argument 1: expected INT64, found TABLE [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg(TABLE TestTable) + ^ +== + +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg() +-- +ERROR: Table-valued function call in pipe CALL requires at least 1 positional argument because its first table argument is in position 2, but the call has no arguments; Supported signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg() + ^ +== + +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg(0) +-- +QueryStmt ++-output_column_list= +| +-tvf_one_int64_arg_one_relation_arg.column_bool#3 AS column_bool [BOOL] +| +-tvf_one_int64_arg_one_relation_arg.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_int64_arg_one_relation_arg.[column_bool#3, column_bytes#4] + +-tvf=tvf_one_int64_arg_one_relation_arg((INT64, ANY TABLE) -> TABLE) + +-signature=(literal INT64, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=0) + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0, 1] +== + +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg(0,1) +-- +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg for argument types: INT64, pipe_input:TABLE, INT64. Supported signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg(0,1) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg + Argument types: INT64, pipe_input:TABLE, INT64 + Signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) + Signature accepts at most 2 arguments, found 3 arguments [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg(0,1) + ^ +== + +# The first call arg is a scalar subquery, which will match the first +# scalar arg in the signature. +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg((select 1)) +-- +QueryStmt ++-output_column_list= +| +-tvf_one_int64_arg_one_relation_arg.column_bool#4 AS column_bool [BOOL] +| +-tvf_one_int64_arg_one_relation_arg.column_bytes#5 AS column_bytes [BYTES] ++-query= + +-TVFScan + +-column_list=tvf_one_int64_arg_one_relation_arg.[column_bool#4, column_bytes#5] + +-tvf=tvf_one_int64_arg_one_relation_arg((INT64, ANY TABLE) -> TABLE) + +-signature=(INT64, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-SubqueryExpr + | | +-type=INT64 + | | +-subquery_type=SCALAR + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0, 1] +== + +# The first call arg is a scalar subquery, producing the wrong type for the +# scalar arg in the signature. +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg((select 'ab')) +-- +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg for argument types: STRING, pipe_input:TABLE. Supported signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg((select 'ab')) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_one_int64_arg_one_relation_arg + Argument types: STRING, pipe_input:TABLE + Signature: TVF_ONE_INT64_ARG_ONE_RELATION_ARG(INT64, TABLE) + Argument 1: Unable to coerce type STRING to expected type INT64 [at 2:9] +|> CALL tvf_one_int64_arg_one_relation_arg((select 'ab')) + ^ +== + +# The first call arg is a non-scalar subquery, so can't match the first +# scalar arg in the signature. +FROM KeyValue +|> CALL tvf_one_int64_arg_one_relation_arg((select 1, 2)) +-- +ERROR: Scalar subquery cannot have more than one column unless using SELECT AS STRUCT to build STRUCT values [at 2:44] +|> CALL tvf_one_int64_arg_one_relation_arg((select 1, 2)) + ^ +== + +[prepare_database=db] +CREATE TABLE FUNCTION tvf_two_scalars_two_tables( + x INT64, y INT64, t1 ANY TABLE, t2 ANY TABLE) +RETURNS TABLE +-- +CreateTableFunctionStmt(name_path=tvf_two_scalars_two_tables, argument_name_list=[x, y, t1, t2], signature=(INT64 x, INT64 y, ANY TABLE t1, ANY TABLE t2) -> TABLE, has_explicit_return_schema=TRUE, language="UNDECLARED") +== + +[default use_database=db] +FROM KeyValue +|> CALL tvf_two_scalars_two_tables( + {{|1|1,2|1,2,TABLE TestTable|1,TABLE TestTable}}) +-- +ALTERNATION GROUP: +-- +ERROR: Table-valued function call in pipe CALL requires at least 2 positional argument sbecause its first table argument is in position 3, but the call has no arguments; Supported signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +-- +ALTERNATION GROUP: 1 +-- +ERROR: Table-valued function call in pipe CALL requires at least 2 positional argument sbecause its first table argument is in position 3, but the call has only 1 argument; Supported signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +-- +ALTERNATION GROUP: 1,2 +-- +ERROR: No matching signature for tvf_two_scalars_two_tables for argument types: INT64, INT64, pipe_input:TABLE. Supported signature: TVF_TWO_SCALARS_TWO_TABLES(INT64, INT64, TABLE, TABLE) [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_scalars_two_tables + Argument types: INT64, INT64, pipe_input:TABLE + Signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) + Signature requires at least 4 arguments, found 3 arguments [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +-- +ALTERNATION GROUP: 1,2,TABLE TestTable +-- +QueryStmt ++-output_column_list= +| +-tvf_two_scalars_two_tables.z#6 AS z [INT64] ++-query= + +-TVFScan + +-column_list=[tvf_two_scalars_two_tables.z#6] + +-tvf=tvf_two_scalars_two_tables((INT64 x, INT64 y, ANY TABLE t1, ANY TABLE t2) -> TABLE) + +-signature=(literal INT64, literal INT64, TABLE, TABLE, KitchenSink PROTO>) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=1) + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=2) + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=TestTable.[key#3, TestEnum#4, KitchenSink#5], table=TestTable, column_index_list=[0, 1, 2]) + | +-argument_column_list=TestTable.[key#3, TestEnum#4, KitchenSink#5] + +-column_index_list=[0] +-- +ALTERNATION GROUP: 1,TABLE TestTable +-- +ERROR: No matching signature for tvf_two_scalars_two_tables for argument types: INT64, TABLE, pipe_input:TABLE. Supported signature: TVF_TWO_SCALARS_TWO_TABLES(INT64, INT64, TABLE, TABLE) [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_scalars_two_tables + Argument types: INT64, TABLE, pipe_input:TABLE + Signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) + Signature requires at least 4 arguments, found 3 arguments [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +== + +FROM KeyValue +|> CALL tvf_two_scalars_two_tables( + x=>1, y=>2, t2=>(FROM TestTable)) +-- +ERROR: Table-valued function call in pipe CALL requires at least 2 positional arguments before any named arguments because its first table argument is in position 3; Supported signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) [at 3:7] + x=>1, y=>2, t2=>(FROM TestTable)) + ^ +== + +FROM KeyValue +|> CALL tvf_two_scalars_two_tables( + 1, 2, t2=>(FROM TestTable)) +-- +QueryStmt ++-output_column_list= +| +-tvf_two_scalars_two_tables.z#6 AS z [INT64] ++-query= + +-TVFScan + +-column_list=[tvf_two_scalars_two_tables.z#6] + +-tvf=tvf_two_scalars_two_tables((INT64 x, INT64 y, ANY TABLE t1, ANY TABLE t2) -> TABLE) + +-signature=(literal INT64, literal INT64, TABLE, TABLE, KitchenSink PROTO>) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=1) + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=2) + | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-argument_column_list=KeyValue.[Key#1, Value#2] + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=TestTable.[key#3, TestEnum#4, KitchenSink#5], table=TestTable, column_index_list=[0, 1, 2]) + | +-argument_column_list=TestTable.[key#3, TestEnum#4, KitchenSink#5] + +-column_index_list=[0] +== + +# Attempt to pass a table as a named arg that would match the +# pipe input table arg. +FROM KeyValue +|> CALL tvf_two_scalars_two_tables( + 1, 2, t1=>(FROM TestTable)) +-- +ERROR: Named argument `t1` duplicates positional argument 3, which also provides `t1` [at 3:13] + 1, 2, t1=>(FROM TestTable)) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_scalars_two_tables + Argument types: INT64, INT64, pipe_input:TABLE, TABLE + Signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) + Named argument `t1` duplicates positional argument 3, which also provides `t1` [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +== + +FROM KeyValue +|> CALL tvf_two_scalars_two_tables( + 1, (SELECT 5 zz), x=>2) +-- +ERROR: Named argument `x` duplicates positional argument 1, which also provides `x` [at 3:25] + 1, (SELECT 5 zz), x=>2) + ^ +-- +Signature Mismatch Details: +ERROR: No matching signature for tvf_two_scalars_two_tables + Argument types: INT64, INT64, pipe_input:TABLE, INT64 + Signature: TVF_TWO_SCALARS_TWO_TABLES(x => INT64, y => INT64, t1 => TABLE, t2 => TABLE) + Named argument `x` duplicates positional argument 1, which also provides `x` [at 2:9] +|> CALL tvf_two_scalars_two_tables( + ^ +== + +[prepare_database=db] +CREATE TABLE FUNCTION tvf_three_scalars_one_table( + x INT64, y INT64, z INT64, t1 ANY TABLE) +RETURNS TABLE +-- +CreateTableFunctionStmt(name_path=tvf_three_scalars_one_table, argument_name_list=[x, y, z, t1], signature=(INT64 x, INT64 y, INT64 z, ANY TABLE t1) -> TABLE, has_explicit_return_schema=TRUE, language="UNDECLARED") +== + +FROM KeyValue +|> CALL tvf_three_scalars_one_table({{1,2|1,2,3}}) +-- +ALTERNATION GROUP: 1,2 +-- +ERROR: Table-valued function call in pipe CALL requires at least 3 positional argument sbecause its first table argument is in position 4, but the call has only 2 arguments; Supported signature: TVF_THREE_SCALARS_ONE_TABLE(x => INT64, y => INT64, z => INT64, t1 => TABLE) [at 2:9] +|> CALL tvf_three_scalars_one_table(1,2) + ^ +-- +ALTERNATION GROUP: 1,2,3 +-- +QueryStmt ++-output_column_list= +| +-tvf_three_scalars_one_table.z#3 AS z [INT64] ++-query= + +-TVFScan + +-column_list=[tvf_three_scalars_one_table.z#3] + +-tvf=tvf_three_scalars_one_table((INT64 x, INT64 y, INT64 z, ANY TABLE t1) -> TABLE) + +-signature=(literal INT64, literal INT64, literal INT64, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=1) + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=2) + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=3) + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0] +== + +FROM KeyValue +|> CALL tvf_three_scalars_one_table(1,2,z=>3) +-- +ERROR: Table-valued function call in pipe CALL requires at least 3 positional arguments before any named arguments because its first table argument is in position 4; Supported signature: TVF_THREE_SCALARS_ONE_TABLE(x => INT64, y => INT64, z => INT64, t1 => TABLE) [at 2:41] +|> CALL tvf_three_scalars_one_table(1,2,z=>3) + ^ +== + +[prepare_database=db] +CREATE TABLE FUNCTION tvf_one_scalar_one_table( + x INT64, t1 ANY TABLE) +RETURNS TABLE +-- +CreateTableFunctionStmt(name_path=tvf_one_scalar_one_table, argument_name_list=[x, t1], signature=(INT64 x, ANY TABLE t1) -> TABLE, has_explicit_return_schema=TRUE, language="UNDECLARED") +== + +FROM KeyValue +|> CALL tvf_one_scalar_one_table({{x=>3|z=>4|5}}) +-- +ALTERNATION GROUP: x=>3 +-- +ERROR: Table-valued function call in pipe CALL requires at least 1 positional argument before any named arguments because its first table argument is in position 2; Supported signature: TVF_ONE_SCALAR_ONE_TABLE(x => INT64, t1 => TABLE) [at 2:34] +|> CALL tvf_one_scalar_one_table(x=>3) + ^ +-- +ALTERNATION GROUP: z=>4 +-- +ERROR: Table-valued function call in pipe CALL requires at least 1 positional argument before any named arguments because its first table argument is in position 2; Supported signature: TVF_ONE_SCALAR_ONE_TABLE(x => INT64, t1 => TABLE) [at 2:34] +|> CALL tvf_one_scalar_one_table(z=>4) + ^ +-- +ALTERNATION GROUP: 5 +-- +QueryStmt ++-output_column_list= +| +-tvf_one_scalar_one_table.z#3 AS z [INT64] ++-query= + +-TVFScan + +-column_list=[tvf_one_scalar_one_table.z#3] + +-tvf=tvf_one_scalar_one_table((INT64 x, ANY TABLE t1) -> TABLE) + +-signature=(literal INT64, TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | | +-expr= + | | +-Literal(type=INT64, value=5) + | +-FunctionArgument + | +-scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-argument_column_list=KeyValue.[Key#1, Value#2] + +-column_index_list=[0] +== + +[prepare_database=db] +CREATE TABLE FUNCTION tvf_expect_a_b_c(t TABLE) +RETURNS TABLE +-- +CreateTableFunctionStmt(name_path=tvf_expect_a_b_c, argument_name_list=[t], signature=(TABLE t) -> TABLE, has_explicit_return_schema=TRUE, language="UNDECLARED") +== + +# This tests the code that rearranges and coerces columns in the input table to +# match the expected schema. (Most tests above use ANY TABLE.) +SELECT 1 x, 10 c, 2 y, CAST(NULL AS STRING) {{b|b_is_missing}}, + 3 z, CAST(20 AS {{string|int32}}) a +|> CALL tvf_expect_a_b_c() +-- +ALTERNATION GROUPS: + b,string + b_is_missing,string +-- +ERROR: Invalid type STRING for column "a INT64" of argument 1 of TVF_EXPECT_A_B_C(TABLE) [at 3:9] +|> CALL tvf_expect_a_b_c() + ^ +-- +ALTERNATION GROUP: b,int32 +-- +QueryStmt ++-output_column_list= +| +-tvf_expect_a_b_c.z#9 AS z [INT64] ++-query= + +-TVFScan + +-column_list=[tvf_expect_a_b_c.z#9] + +-tvf=tvf_expect_a_b_c((TABLE t) -> TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.a#7, $query.b#4, $subquery1.c#8] + | | +-expr_list= + | | | +-a#7 := + | | | | +-Cast(INT32 -> INT64) + | | | | +-ColumnRef(type=INT32, column=$query.a#6) + | | | +-c#8 := + | | | +-Cast(INT64 -> DOUBLE) + | | | +-ColumnRef(type=INT64, column=$query.c#2) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, c#2, y#3, b#4, z#5, a#6] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-c#2 := Literal(type=INT64, value=10) + | | | +-y#3 := Literal(type=INT64, value=2) + | | | +-b#4 := Literal(type=STRING, value=NULL, has_explicit_type=TRUE) + | | | +-z#5 := Literal(type=INT64, value=3) + | | | +-a#6 := Literal(type=INT32, value=20, has_explicit_type=TRUE) + | | +-input_scan= + | | +-SingleRowScan + | +-argument_column_list=[$subquery1.a#7, $query.b#4, $subquery1.c#8] + +-column_index_list=[0] +-- +ALTERNATION GROUP: b_is_missing,int32 +-- +ERROR: Required column "b" not found in table passed as argument 1 of TVF_EXPECT_A_B_C(TABLE) [at 3:9] +|> CALL tvf_expect_a_b_c() + ^ diff --git a/zetasql/analyzer/testdata/pipe_drop.test b/zetasql/analyzer/testdata/pipe_drop.test new file mode 100644 index 000000000..919cc4c25 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_drop.test @@ -0,0 +1,597 @@ +[default language_features=PIPES,PIPE_STATIC_DESCRIBE] +from KeyValue +|> STATIC_DESCRIBE +|> drop keY +|> STATIC_DESCRIBE +|> where value is null +|> where KeyValue.value is null +|> where KeyValue.key is null +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-FilterScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | | +-StaticDescribeScan + | | | +-column_list=KeyValue.[Key#1, Value#2] + | | | +-describe_text= + | | | | """ + | | | | NameList: + | | | | Value STRING KeyValue.Value#2 + | | | | NameScope: + | | | | Names: + | | | | Value -> STRING (KeyValue.Value#2) (implicit) + | | | | Range variables: + | | | | KeyValue -> RANGE_VARIABLE + | | | | """ + | | | +-input_scan= + | | | +-StaticDescribeScan + | | | +-column_list=KeyValue.[Key#1, Value#2] + | | | +-describe_text= + | | | | """ + | | | | NameList: + | | | | Key INT64 KeyValue.Key#1 + | | | | Value STRING KeyValue.Value#2 + | | | | NameScope: + | | | | Names: + | | | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | | | Value -> STRING (KeyValue.Value#2) (implicit) + | | | | Range variables: + | | | | KeyValue -> RANGE_VARIABLE + | | | | """ + | | | +-input_scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +== + +from KeyValue +|> drop KEy, keY +-- +ERROR: Duplicate column name in pipe DROP: keY [at 2:14] +|> drop KEy, keY + ^ +== + +from KeyValue +|> drop key, value +-- +ERROR: Pipe DROP dropped all columns in the input table [at 2:1] +|> drop key, value +^ +== + +from KeyValue +|> drop key, something, value +-- +ERROR: Column name in pipe DROP not found in input table: something [at 2:14] +|> drop key, something, value + ^ +== + +from KeyValue +|> drop `$abc` +-- +ERROR: Cannot use pipe DROP with internal alias `$abc` [at 2:9] +|> drop `$abc` + ^ +== + +# Drop both copies of 'key', but keep the copies under the +# existing range variables. +FROM KeyValue kv1 CROSS JOIN KeyValue kv2 +|> STATIC_DESCRIBE +|> DROP key +|> STATIC_DESCRIBE +|> WHERE {{key|kv1.key}} = 5 +|> SELECT *, '--', kv1.*, '--', kv2.* +-- +ALTERNATION GROUP: key +-- +ERROR: Unrecognized name: key [at 5:10] +|> WHERE key = 5 + ^ +-- +ALTERNATION GROUP: kv1.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] +| +-$pipe_select.$col2#5 AS `$col2` [STRING] +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_select.$col4#6 AS `$col4` [STRING] +| +-KeyValue.Key#3 AS Key [INT64] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Value#2, KeyValue.Value#4, $pipe_select.$col2#5, KeyValue.Key#1, KeyValue.Value#2, $pipe_select.$col4#6, KeyValue.Key#3, KeyValue.Value#4] + +-expr_list= + | +-$col2#5 := Literal(type=STRING, value="--") + | +-$col4#6 := Literal(type=STRING, value="--") + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | +-describe_text= + | | """ + | | NameList: + | | Value STRING KeyValue.Value#2 + | | Value STRING KeyValue.Value#4 + | | NameScope: + | | Names: + | | Value -> ambiguous + | | Range variables: + | | kv1 -> RANGE_VARIABLE + | | kv2 -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | +-describe_text= + | | """ + | | NameList: + | | Key INT64 KeyValue.Key#1 + | | Value STRING KeyValue.Value#2 + | | Key INT64 KeyValue.Key#3 + | | Value STRING KeyValue.Value#4 + | | NameScope: + | | Names: + | | Key -> ambiguous + | | Value -> ambiguous + | | Range variables: + | | kv1 -> RANGE_VARIABLE + | | kv2 -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=INT64, value=5) +== + +# We can't drop a range variable for a non-value table. +FROM KeyValue kv +|> DROP kv +-- +ERROR: Name in pipe DROP is a table alias; DROP can only drop columns: kv [at 2:9] +|> DROP kv + ^ +== + +# This is allowed because there's a column key. +# The range variable key is also dropped. +FROM KeyValue key +|> STATIC_DESCRIBE +|> DROP key +|> STATIC_DESCRIBE +|> SELECT {{key|key.key|value}} +-- +ALTERNATION GROUP: key +-- +ERROR: Unrecognized name: key [at 5:11] +|> SELECT key + ^ +-- +ALTERNATION GROUP: key.key +-- +ERROR: Unrecognized name: key [at 5:11] +|> SELECT key.key + ^ +-- +ALTERNATION GROUP: value +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#2 AS value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Value#2] + +-input_scan= + +-StaticDescribeScan + +-column_list=[KeyValue.Value#2] + +-describe_text= + | """ + | NameList: + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Value -> STRING (KeyValue.Value#2) (implicit) + | """ + +-input_scan= + +-StaticDescribeScan + +-column_list=[KeyValue.Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | key -> RANGE_VARIABLE + | """ + +-input_scan= + +-TableScan(column_list=[KeyValue.Value#2], table=KeyValue, column_index_list=[1], alias="key") +== + +# Dropping a pseudo-column doesn't work. +FROM EnumTable +|> WHERE Filename is not null +|> DROP Filename +-- +ERROR: Name in pipe DROP is present but is not a column on the pipe input table; DROP can only drop columns: Filename [at 3:9] +|> DROP Filename + ^ +== + +# DROP works because there is a real column Filename. +# The pseudo-column Filename is also dropped. +FROM EnumTable +|> EXTEND 'abc' AS Filename +|> STATIC_DESCRIBE +|> DROP Filename +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] ++-query= + +-StaticDescribeScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, $pipe_extend.Filename#6] + +-describe_text= + | """ + | NameList: + | key INT32 EnumTable.key#1 + | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | NameScope: + | Names: + | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (EnumTable.AnotherTestEnum#3) (implicit) + | RowId -> BYTES (EnumTable.RowId#5) (implicit) (pseudo-column) + | TestEnum -> zetasql_test__.TestEnum (EnumTable.TestEnum#2) (implicit) + | key -> INT32 (EnumTable.key#1) (implicit) + | Range variables: + | EnumTable -> RANGE_VARIABLE + | """ + +-input_scan= + +-StaticDescribeScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, $pipe_extend.Filename#6] + +-describe_text= + | """ + | NameList: + | key INT32 EnumTable.key#1 + | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | Filename STRING $pipe_extend.Filename#6 + | NameScope: + | Names: + | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (EnumTable.AnotherTestEnum#3) (implicit) + | Filename -> ambiguous + | RowId -> BYTES (EnumTable.RowId#5) (implicit) (pseudo-column) + | TestEnum -> zetasql_test__.TestEnum (EnumTable.TestEnum#2) (implicit) + | key -> INT32 (EnumTable.key#1) (implicit) + | Range variables: + | EnumTable -> RANGE_VARIABLE + | """ + +-input_scan= + +-ProjectScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, $pipe_extend.Filename#6] + +-expr_list= + | +-Filename#6 := Literal(type=STRING, value="abc") + +-input_scan= + +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3], table=EnumTable, column_index_list=[0, 1, 2]) +== + +FROM KeyValue, UNNEST([1]) elem WITH OFFSET +|> STATIC_DESCRIBE +|> DROP elem, offset +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | """ + +-input_scan= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | elem INT64 $array.elem#3 (value table) + | offset INT64 $array_offset.offset#4 (value table) + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | elem -> RANGE_VARIABLE<$value> + | offset -> RANGE_VARIABLE<$value> + | Value table columns: + | $array.elem#3 + | $array_offset.offset#4 + | """ + +-input_scan= + +-ArrayScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1]) + +-element_column_list=[$array.elem#3] + +-array_offset_column= + +-ColumnHolder(column=$array_offset.offset#4) +== + +# We can't drop field names or pseudo-columns for the value table +# input. We can drop the value column, but not if it's the +# only column. +FROM TestExtraValueTable vt +|> DROP {{vt|int32_val1|Filename}} +-- +ALTERNATION GROUP: vt +-- +ERROR: Pipe DROP dropped all columns in the input table [at 2:1] +|> DROP vt +^ +-- +ALTERNATION GROUP: int32_val1 +-- +ERROR: Name in pipe DROP is a field inside a value table; DROP can only drop columns: int32_val1 [at 2:9] +|> DROP int32_val1 + ^ +-- +ALTERNATION GROUP: Filename +-- +ERROR: Name in pipe DROP is present but is not a column on the pipe input table; DROP can only drop columns: Filename [at 2:9] +|> DROP Filename + ^ +== + +# We can drop value table columns (which are also range variables). +# The remaining value table then works unambiguously as a value table. +FROM TestExtraValueTable vt1, TestExtraValueTable vt2 +|> STATIC_DESCRIBE +|> DROP vt1 +|> STATIC_DESCRIBE +|> WHERE int32_val1 = 1 +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#4 AS vt2 [PROTO] ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#4] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#4] + | +-describe_text= + | | """ + | | NameList: + | | vt2 zetasql_test__.TestExtraPB TestExtraValueTable.value#4 (value table) (excluded_field_names vt1) + | | NameScope: + | | Names: + | | Filename -> ambiguous + | | RowId -> ambiguous + | | Range variables: + | | vt2 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#4 (excluded_field_names vt1) + | | """ + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#4] + | +-describe_text= + | | """ + | | NameList: + | | vt1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | | vt2 zetasql_test__.TestExtraPB TestExtraValueTable.value#4 (value table) + | | NameScope: + | | Names: + | | Filename -> ambiguous + | | RowId -> ambiguous + | | Range variables: + | | vt1 -> RANGE_VARIABLE<$value> + | | vt2 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 + | | TestExtraValueTable.value#4 + | | """ + | +-input_scan= + | +-JoinScan + | +-column_list=[TestExtraValueTable.value#4] + | +-left_scan= + | | +-TableScan(table=TestExtraValueTable, alias="vt1") + | +-right_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0], alias="vt2") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#4) + | +-field_descriptor=int32_val1 + | +-default_value=0 + +-Literal(type=INT32, value=1) + +== + +# Tricky case where we drop a column that is also a field of a value +# table. This adds an exclusion on the value table so the column +# works as expected after DROP. Before the DROP, reading +# int32_val1 is ambiguous, since it is both a column and a field. +# SELECT * also omits the int32_val1 after the DROP. +FROM TestExtraValueTable vt1 +|> EXTEND vt1.int32_val1 +|> WHERE {{int32_val1=1|true}} +|> STATIC_DESCRIBE +|> DROP int32_val1 +|> STATIC_DESCRIBE +|> WHERE {{int32_val1=2|true}} +|> SELECT * +-- +ALTERNATION GROUPS: + int32_val1=1,int32_val1=2 + int32_val1=1,true +-- +ERROR: Column name int32_val1 is ambiguous [at 3:10] +|> WHERE int32_val1=1 + ^ +-- +ALTERNATION GROUP: true,int32_val1=2 +-- +ERROR: Unrecognized name: int32_val1 [at 7:10] +|> WHERE int32_val1=2 + ^ +-- +ALTERNATION GROUP: true,true +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.int32_val2#5 AS int32_val2 [INT32] +| +-$pipe_select.str_value#6 AS str_value [ARRAY] ++-query= + +-ProjectScan + +-column_list=$pipe_select.[int32_val2#5, str_value#6] + +-expr_list= + | +-int32_val2#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-str_value#6 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | +-describe_text= + | | """ + | | NameList: + | | vt1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) (excluded_field_names int32_val1) + | | NameScope: + | | Names: + | | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | | Range variables: + | | vt1 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 (excluded_field_names int32_val1) + | | """ + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | +-describe_text= + | | """ + | | NameList: + | | vt1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | | int32_val1 INT32 $pipe_extend.int32_val1#4 + | | NameScope: + | | Names: + | | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | | int32_val1 -> INT32 ($pipe_extend.int32_val1#4) + | | Range variables: + | | vt1 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 + | | """ + | +-input_scan= + | +-FilterScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | | +-expr_list= + | | | +-int32_val1#4 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val1 + | | | +-default_value=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt1") + | +-filter_expr= + | +-Literal(type=BOOL, value=true) + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +# Try to use DROP on an outer correlated column. +from KeyValue +|> extend ( + select 1 x + |> DROP key + ) +-- +ERROR: Name in pipe DROP is present but is not a column on the pipe input table; DROP can only drop columns: key [at 4:14] + |> DROP key + ^ +== + +# Try to use DROP on an outer correlated name that is ambiguous. +from KeyValue +|> extend key +|> extend ( + select 1 x + |> DROP key + ) +-- +ERROR: Name in pipe DROP is ambiguous; DROP can only drop columns: key [at 5:14] + |> DROP key + ^ diff --git a/zetasql/analyzer/testdata/pipe_extend.test b/zetasql/analyzer/testdata/pipe_extend.test new file mode 100644 index 000000000..a3e6721dc --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_extend.test @@ -0,0 +1,875 @@ +[default language_features=PIPES,ANALYTIC_FUNCTIONS,V_1_1_SELECT_STAR_EXCEPT_REPLACE] +select 1 x +|> extend 2 y +|> extend y*2 z +|> extend y+z yz, x+1 x1 +|> extend yz+1 yz1 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$pipe_extend.y#2 AS y [INT64] +| +-$pipe_extend.z#3 AS z [INT64] +| +-$pipe_extend.yz#4 AS yz [INT64] +| +-$pipe_extend.x1#5 AS x1 [INT64] +| +-$pipe_extend.yz1#6 AS yz1 [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_extend.y#2, $pipe_extend.z#3, $pipe_extend.yz#4, $pipe_extend.x1#5, $pipe_extend.yz1#6] + +-expr_list= + | +-yz1#6 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$pipe_extend.yz#4) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_extend.y#2, $pipe_extend.z#3, $pipe_extend.yz#4, $pipe_extend.x1#5] + +-expr_list= + | +-yz#4 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$pipe_extend.y#2) + | | +-ColumnRef(type=INT64, column=$pipe_extend.z#3) + | +-x1#5 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_extend.y#2, $pipe_extend.z#3] + +-expr_list= + | +-z#3 := + | +-FunctionCall(ZetaSQL:$multiply(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$pipe_extend.y#2) + | +-Literal(type=INT64, value=2) + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_extend.y#2] + +-expr_list= + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +select 1 x +|> extend x+1 x1, x1+1 x2 +-- +ERROR: Unrecognized name: x1 [at 2:19] +|> extend x+1 x1, x1+1 x2 + ^ +== + +select 1 x +|> extend x+1 x1 +|> extend x1+1 x2 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$pipe_extend.x1#2 AS x1 [INT64] +| +-$pipe_extend.x2#3 AS x2 [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_extend.x1#2, $pipe_extend.x2#3] + +-expr_list= + | +-x2#3 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$pipe_extend.x1#2) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_extend.x1#2] + +-expr_list= + | +-x1#2 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +select 1 x +|> extend 2 x +|> extend x +-- +ERROR: Column name x is ambiguous [at 3:11] +|> extend x + ^ +== + +# EXTEND makes the result no longer a value table. +select as value 1 +|> extend 2 y +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$value_column` [INT64] +| +-$pipe_extend.y#2 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1, $pipe_extend.y#2] + +-expr_list= + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +select as struct * +from (select 1 t |> extend t+1 |> extend t+2) +-- +QueryStmt ++-output_column_list= +| +-$make_struct.$struct#4 AS `$struct` [STRUCT] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_struct.$struct#4] + +-expr_list= + | +-$struct#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$subquery1.t#1) + | +-ColumnRef(type=INT64, column=$pipe_extend.$col1#2) + | +-ColumnRef(type=INT64, column=$pipe_extend.$col1#3) + +-input_scan= + +-ProjectScan + +-column_list=[$subquery1.t#1, $pipe_extend.$col1#2, $pipe_extend.$col1#3] + +-input_scan= + +-ProjectScan + +-column_list=[$subquery1.t#1, $pipe_extend.$col1#2, $pipe_extend.$col1#3] + +-expr_list= + | +-$col1#3 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$subquery1.t#1) + | +-Literal(type=INT64, value=2) + +-input_scan= + +-ProjectScan + +-column_list=[$subquery1.t#1, $pipe_extend.$col1#2] + +-expr_list= + | +-$col1#2 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$subquery1.t#1) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[$subquery1.t#1] + +-expr_list= + | +-t#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +select 1 x +|> extend count(*) +-- +ERROR: Aggregate function COUNT(*) not allowed in pipe EXTEND [at 2:11] +|> extend count(*) + ^ +== + +select 1 x +|> extend sum(sum(x)) OVER () +-- +ERROR: Aggregate function SUM not allowed in pipe EXTEND [at 2:15] +|> extend sum(sum(x)) OVER () + ^ +== + +[language_features=PIPES] +select 1 x +|> extend sum(x) OVER () +-- +ERROR: Analytic functions not supported [at 2:11] +|> extend sum(x) OVER () + ^ +== + +from KeyValue +|> extend sum(key) OVER (), count(*) over () AS cnt +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$analytic.$analytic1#5 AS `$col1` [INT64] +| +-$analytic.cnt#6 AS cnt [INT64] ++-query= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#5, $analytic.cnt#6] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#5 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-cnt#6 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Window functions with expressions before and after the window function. +# Common OVER clauses are not currently combined into shared +# AnalyticFunctionGroup (which doesn't happen for SELECT either). +from KeyValue +|> extend sum(1+key) OVER (order by value), + 2+count(*) over (order by value) AS cnt, + count(*) over (partition by key+3) AS cnt2 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$analytic.$analytic1#6 AS `$col1` [INT64] +| +-$pipe_extend.cnt#8 AS cnt [INT64] +| +-$analytic.cnt2#9 AS cnt2 [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#6, $pipe_extend.cnt#8, $analytic.cnt2#9] + +-expr_list= + | +-cnt#8 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Literal(type=INT64, value=2) + | +-ColumnRef(type=INT64, column=$analytic.$analytic2#7) + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#6, $analytic.$analytic2#7, $analytic.cnt2#9] + +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $partitionby.$partitionbycol1#10] + | +-expr_list= + | | +-$partitionbycol1#10 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=3) + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-function_group_list= + +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-analytic_function_list= + | +-$analytic1#6 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Literal(type=INT64, value=1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-analytic_function_list= + | +-$analytic2#7 := + | +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-AnalyticFunctionGroup + +-partition_by= + | +-WindowPartitioning + | +-partition_by_list= + | +-ColumnRef(type=INT64, column=$partitionby.$partitionbycol1#10) + +-analytic_function_list= + +-cnt2#9 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +from KeyValue +|> extend count(*) OVER w +-- +ERROR: Cannot reference a named window in pipe EXTEND [at 2:25] +|> extend count(*) OVER w + ^ +== + +# EXTEND preserves range variables. +from KeyValue kv1, KeyValue kv2 +|> extend kv1.key AS key1 +|> extend kv2.key AS key2 +|> where kv1.key=1 and key1=key2 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Key#3 AS Key [INT64] +| +-KeyValue.Value#4 AS Value [STRING] +| +-KeyValue.Key#1 AS key1 [INT64] +| +-KeyValue.Key#3 AS key2 [INT64] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-input_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-filter_expr= + +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=1) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=INT64, column=KeyValue.Key#3) +== + +# EXTEND preserves pseudocolumns. +from EnumTable kv1 +|> extend rowid AS c1 +|> extend filename AS c2 +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-EnumTable.RowId#5 AS c1 [BYTES] +| +-EnumTable.Filename#4 AS c2 [STRING] ++-query= + +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4], alias="kv1") +== + +# Note that using EXTEND to materialize a pseudo-column +# without aliasing it ends up creating an ambiguous name +# since the pseudo-column is also still visible. +from EnumTable kv1 +|> where rowid is null +|> extend rowid +|> where rowid is null +-- +ERROR: Column name rowid is ambiguous [at 4:10] +|> where rowid is null + ^ +== + +# Dot-star on a struct. +FROM ComplexTypes +|> SELECT TestStruct +|> EXTEND TestStruct.*, + '--', + TestStruct.d.* +-- +QueryStmt ++-output_column_list= +| +-ComplexTypes.TestStruct#5 AS TestStruct [STRUCT>] +| +-$pipe_extend.c#8 AS c [INT32] +| +-$pipe_extend.d#9 AS d [STRUCT] +| +-$pipe_extend.$col2#10 AS `$col2` [STRING] +| +-$pipe_extend.a#11 AS a [INT32] +| +-$pipe_extend.b#12 AS b [STRING] ++-query= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_extend.c#8, $pipe_extend.d#9, $pipe_extend.$col2#10, $pipe_extend.a#11, $pipe_extend.b#12] + +-expr_list= + | +-c#8 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | | +-field_idx=0 + | +-d#9 := + | | +-GetStructField + | | +-type=STRUCT + | | +-expr= + | | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | | +-field_idx=1 + | +-$col2#10 := Literal(type=STRING, value="--") + | +-a#11 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#7) + | | +-field_idx=0 + | +-b#12 := + | +-GetStructField + | +-type=STRING + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#7) + | +-field_idx=1 + +-input_scan= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $preproject.$struct#7] + +-expr_list= + | +-$struct#7 := + | +-GetStructField + | +-type=STRUCT + | +-expr= + | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | +-field_idx=1 + +-input_scan= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5] + +-input_scan= + +-TableScan(column_list=[ComplexTypes.TestStruct#5], table=ComplexTypes, column_index_list=[4]) +== + +SELECT 1 +|> EXTEND STRUCT(1 AS x, 2, 'abc' AS x, null AS y).* +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [INT64] +| +-$pipe_extend.x#3 AS x [INT64] +| +-$pipe_extend.$field2#4 AS `$field2` [INT64] +| +-$pipe_extend.x#5 AS x [STRING] +| +-$pipe_extend.y#6 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1, $pipe_extend.x#3, $pipe_extend.$field2#4, $pipe_extend.x#5, $pipe_extend.y#6] + +-expr_list= + | +-x#3 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#2) + | | +-field_idx=0 + | +-$field2#4 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#2) + | | +-field_idx=1 + | +-x#5 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#2) + | | +-field_idx=2 + | +-y#6 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#2) + | +-field_idx=3 + +-input_scan= + +-ProjectScan + +-column_list=[$query.$col1#1, $preproject.$struct#2] + +-expr_list= + | +-$struct#2 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-Literal(type=INT64, value=1) + | +-Literal(type=INT64, value=2) + | +-Literal(type=STRING, value="abc") + | +-Literal(type=INT64, value=NULL) + +-input_scan= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +# Dot-star with modifiers. +FROM ComplexTypes +|> SELECT TestStruct +|> EXTEND TestStruct.* EXCEPT (c), + '--', + TestStruct.* REPLACE (TestStruct.c+1 AS c), + '--', + TestStruct.* EXCEPT (c) REPLACE ('abc' AS d), +-- +QueryStmt ++-output_column_list= +| +-ComplexTypes.TestStruct#5 AS TestStruct [STRUCT>] +| +-$pipe_extend.d#7 AS d [STRUCT] +| +-$pipe_extend.$col2#8 AS `$col2` [STRING] +| +-$pipe_extend.c#9 AS c [INT64] +| +-$pipe_extend.d#10 AS d [STRUCT] +| +-$pipe_extend.$col4#11 AS `$col4` [STRING] +| +-$pipe_extend.d#12 AS d [STRING] ++-query= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_extend.d#7, $pipe_extend.$col2#8, $pipe_extend.c#9, $pipe_extend.d#10, $pipe_extend.$col4#11, $pipe_extend.d#12] + +-expr_list= + | +-d#7 := + | | +-GetStructField + | | +-type=STRUCT + | | +-expr= + | | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | | +-field_idx=1 + | +-$col2#8 := Literal(type=STRING, value="--") + | +-c#9 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Cast(INT32 -> INT64) + | | | +-GetStructField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | | | +-field_idx=0 + | | +-Literal(type=INT64, value=1) + | +-d#10 := + | | +-GetStructField + | | +-type=STRUCT + | | +-expr= + | | | +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + | | +-field_idx=1 + | +-$col4#11 := Literal(type=STRING, value="--") + | +-d#12 := Literal(type=STRING, value="abc") + +-input_scan= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5] + +-input_scan= + +-TableScan(column_list=[ComplexTypes.TestStruct#5], table=ComplexTypes, column_index_list=[4]) +== + +FROM TestTable +|> EXTEND STRUCT(1 AS x).* EXCEPT (y) +-- +ERROR: Column y in SELECT * EXCEPT list does not exist [at 2:36] +|> EXTEND STRUCT(1 AS x).* EXCEPT (y) + ^ +== + +FROM TestTable +|> EXTEND STRUCT(1 AS x).* REPLACE (0 AS y) +-- +ERROR: Column y in SELECT * REPLACE list does not exist [at 2:42] +|> EXTEND STRUCT(1 AS x).* REPLACE (0 AS y) + ^ +== + +FROM TestTable +|> EXTEND key.* +-- +ERROR: Dot-star is not supported for type INT32 [at 2:11] +|> EXTEND key.* + ^ +== + +SELECT STRUCT() s +|> EXTEND s.* +-- +ERROR: Star expansion is not allowed on a struct with zero fields [at 2:11] +|> EXTEND s.* + ^ +== + +FROM ComplexTypes +|> SELECT TestStruct +|> EXTEND TestStruct.* EXCEPT (c,d) +-- +ERROR: SELECT * expands to zero columns after applying EXCEPT [at 3:11] +|> EXTEND TestStruct.* EXCEPT (c,d) + ^ +== + +# Dot-star on a range variable. +SELECT 1 x, 2 y +|> AS t +|> EXTEND t.* +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +# Dot-star on a proto. +FROM TestTable +|> EXTEND KitchenSink.nested_with_required_fields.* +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-$pipe_extend.nested_required_value#5 AS nested_required_value [PROTO] +| +-$pipe_extend.nested_int32_val#6 AS nested_int32_val [INT32] +| +-$pipe_extend.nested_value#7 AS nested_value [PROTO] ++-query= + +-ProjectScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $pipe_extend.nested_required_value#5, $pipe_extend.nested_int32_val#6, $pipe_extend.nested_value#7] + +-expr_list= + | +-nested_required_value#5 := + | | +-GetProtoField + | | +-type=PROTO + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$preproject.$proto#4) + | | +-field_descriptor=nested_required_value + | +-nested_int32_val#6 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$preproject.$proto#4) + | | +-field_descriptor=nested_int32_val + | | +-default_value=0 + | +-nested_value#7 := + | +-GetProtoField + | +-type=PROTO + | +-expr= + | | +-ColumnRef(type=PROTO, column=$preproject.$proto#4) + | +-field_descriptor=nested_value + | +-default_value=NULL + +-input_scan= + +-ProjectScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $preproject.$proto#4] + +-expr_list= + | +-$proto#4 := + | +-GetProtoField + | +-type=PROTO + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-field_descriptor=nested_with_required_fields + | +-default_value=NULL + +-input_scan= + +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) +== + +# Dot-star after a window function. +FROM ComplexTypes +|> SELECT TestStruct, 1 pos +|> EXTEND (FIRST_VALUE(TestStruct) OVER (ORDER BY pos)).* +-- +QueryStmt ++-output_column_list= +| +-ComplexTypes.TestStruct#5 AS TestStruct [STRUCT>] +| +-$pipe_select.pos#7 AS pos [INT64] +| +-$pipe_extend.c#9 AS c [INT32] +| +-$pipe_extend.d#10 AS d [STRUCT] ++-query= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_select.pos#7, $pipe_extend.c#9, $pipe_extend.d#10] + +-expr_list= + | +-c#9 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT>, column=$analytic.$analytic1#8) + | | +-field_idx=0 + | +-d#10 := + | +-GetStructField + | +-type=STRUCT + | +-expr= + | | +-ColumnRef(type=STRUCT>, column=$analytic.$analytic1#8) + | +-field_idx=1 + +-input_scan= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_select.pos#7, $analytic.$analytic1#11, $analytic.$analytic1#8] + +-expr_list= + | +-$analytic1#8 := ColumnRef(type=STRUCT>, column=$analytic.$analytic1#11) + +-input_scan= + +-AnalyticScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_select.pos#7, $analytic.$analytic1#11] + +-input_scan= + | +-ProjectScan + | +-column_list=[ComplexTypes.TestStruct#5, $pipe_select.pos#7] + | +-expr_list= + | | +-pos#7 := Literal(type=INT64, value=1) + | +-input_scan= + | +-TableScan(column_list=[ComplexTypes.TestStruct#5], table=ComplexTypes, column_index_list=[4]) + +-function_group_list= + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=INT64, column=$pipe_select.pos#7) + +-analytic_function_list= + +-$analytic1#11 := + +-AnalyticFunctionCall(ZetaSQL:first_value(STRUCT>) -> STRUCT>) + +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + +-window_frame= + +-WindowFrame(frame_unit=RANGE) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=CURRENT ROW) +== + +# Dot-star EXCEPT on a window function. +FROM KeyValue +|> EXTEND ANY_VALUE(STRUCT(key, value, 1+key AS key1)) OVER ().* EXCEPT(key) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_extend.value#4 AS value [STRING] +| +-$pipe_extend.key1#5 AS key1 [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.value#4, $pipe_extend.key1#5] + +-expr_list= + | +-value#4 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$analytic.$analytic1#3) + | | +-field_idx=1 + | +-key1#5 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$analytic.$analytic1#3) + | +-field_idx=2 + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#6, $analytic.$analytic1#3] + +-expr_list= + | +-$analytic1#3 := ColumnRef(type=STRUCT, column=$analytic.$analytic1#6) + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#6] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#6 := + +-AnalyticFunctionCall(ZetaSQL:any_value(STRUCT) -> STRUCT) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-Literal(type=INT64, value=1) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Window function in the .* REPLACE is not allowed. +FROM KeyValue +|> EXTEND ANY_VALUE(STRUCT(key, value)) OVER ().* + REPLACE(COUNT(*) OVER () AS value) +-- +ERROR: Cannot use analytic functions inside SELECT * REPLACE [at 3:14] + REPLACE(COUNT(*) OVER () AS value) + ^ +== + +# If collation is enabled, collation propagates to output columns the same way +# for SELECT and EXTEND. +# TODO Enable java support for collation. +[no_java] +[language_features=PIPES{{|,V_1_3_ANNOTATION_FRAMEWORK|,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT}}] +FROM CollatedTable +|> SELECT string_ci, CONCAT(string_ci, "xxx") AS newcol1 +|> EXTEND CONCAT(string_ci, "yyy") AS newcol2 +-- +ALTERNATION GROUPS: + + ,V_1_3_ANNOTATION_FRAMEWORK +-- +QueryStmt ++-output_column_list= +| +-CollatedTable.string_ci#1{Collation:"und:ci"} AS string_ci [STRING] +| +-$pipe_select.newcol1#5 AS newcol1 [STRING] +| +-$pipe_extend.newcol2#6 AS newcol2 [STRING] ++-query= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5, $pipe_extend.newcol2#6] + +-expr_list= + | +-newcol2#6 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | +-Literal(type=STRING, value="yyy") + +-input_scan= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5] + +-expr_list= + | +-newcol1#5 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | +-Literal(type=STRING, value="xxx") + +-input_scan= + +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) +-- +ALTERNATION GROUP: ,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT +-- +QueryStmt ++-output_column_list= +| +-CollatedTable.string_ci#1{Collation:"und:ci"} AS string_ci [STRING] +| +-$pipe_select.newcol1#5{Collation:"und:ci"} AS newcol1 [STRING] +| +-$pipe_extend.newcol2#6{Collation:"und:ci"} AS newcol2 [STRING] ++-query= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5{Collation:"und:ci"}, $pipe_extend.newcol2#6{Collation:"und:ci"}] + +-expr_list= + | +-newcol2#6 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-type_annotation_map={Collation:"und:ci"} + | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | +-Literal(type=STRING, value="yyy") + +-input_scan= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5{Collation:"und:ci"}] + +-expr_list= + | +-newcol1#5 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-type_annotation_map={Collation:"und:ci"} + | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | +-Literal(type=STRING, value="xxx") + +-input_scan= + +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) diff --git a/zetasql/analyzer/testdata/pipe_from_query.test b/zetasql/analyzer/testdata/pipe_from_query.test new file mode 100644 index 000000000..bf8720f3a --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_from_query.test @@ -0,0 +1,2229 @@ +from TestTable +-- +ERROR: Syntax error: Unexpected FROM [at 1:1] +from TestTable +^ +== + +select (from TestTable) +-- +ERROR: Syntax error: Unexpected FROM [at 1:9] +select (from TestTable) + ^ +== + +select EXISTS(from TestTable) +-- +ERROR: Syntax error: Unexpected FROM [at 1:15] +select EXISTS(from TestTable) + ^ +== + +select ARRAY(from TestTable) +-- +ERROR: Syntax error: Unexpected FROM [at 1:14] +select ARRAY(from TestTable) + ^ +== + +[default language_features=PIPES,PIPE_STATIC_DESCRIBE] +from TestTable +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] ++-query= + +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) +== + +from (from TestTable) +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] ++-query= + +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) +== + +from unnest([1,2,3]) +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-$array.$unnest1#1 AS `$unnest1` [INT64] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[$array.$unnest1#1] + +-describe_text= + | """ + | NameList (is_value_table = true): + | INT64 $array.$unnest1#1 (value table) + | NameScope: + | Value table columns: + | $array.$unnest1#1 + | """ + +-input_scan= + +-ArrayScan + +-column_list=[$array.$unnest1#1] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2, 3]) + +-element_column_list=[$array.$unnest1#1] +== + +from TestTable, TestTable t2 +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-TestTable.key#4 AS key [INT32] +| +-TestTable.TestEnum#5 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#6 AS KitchenSink [PROTO] ++-query= + +-StaticDescribeScan + +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3, key#4, TestEnum#5, KitchenSink#6] + +-describe_text= + | """ + | NameList: + | key INT32 TestTable.key#1 + | TestEnum zetasql_test__.TestEnum TestTable.TestEnum#2 + | KitchenSink zetasql_test__.KitchenSinkPB TestTable.KitchenSink#3 + | key INT32 TestTable.key#4 + | TestEnum zetasql_test__.TestEnum TestTable.TestEnum#5 + | KitchenSink zetasql_test__.KitchenSinkPB TestTable.KitchenSink#6 + | NameScope: + | Names: + | KitchenSink -> ambiguous + | TestEnum -> ambiguous + | key -> ambiguous + | Range variables: + | TestTable -> RANGE_VARIABLE + | t2 -> RANGE_VARIABLE + | """ + +-input_scan= + +-JoinScan + +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3, key#4, TestEnum#5, KitchenSink#6] + +-left_scan= + | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + +-right_scan= + +-TableScan(column_list=TestTable.[key#4, TestEnum#5, KitchenSink#6], table=TestTable, column_index_list=[0, 1, 2], alias="t2") +== + +# FROM of a value table produces an output value table (is_value_table=true). +from TestExtraValueTable +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +# Still get a value table from a subquery. +from (from TestExtraValueTable) +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +# Value table subquery, with or without a name. +from (select as value new zetasql_test__.TestExtraPB()){{ named|}} +|> STATIC_DESCRIBE +-- +ALTERNATION GROUP: named +-- +QueryStmt ++-output_column_list= +| +-named.$col1#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[named.$col1#1] + +-describe_text= + | """ + | NameList (is_value_table = true): + | named zetasql_test__.TestExtraPB named.$col1#1 (value table) + | NameScope: + | Range variables: + | named -> RANGE_VARIABLE<$value> + | Value table columns: + | named.$col1#1 + | """ + +-input_scan= + +-ProjectScan + +-column_list=[named.$col1#1] + +-expr_list= + | +-$col1#1 := MakeProto(type=PROTO) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$subquery1.$col1#1 AS `$value_column` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[$subquery1.$col1#1] + +-describe_text= + | """ + | NameList (is_value_table = true): + | zetasql_test__.TestExtraPB $subquery1.$col1#1 (value table) + | NameScope: + | Value table columns: + | $subquery1.$col1#1 + | """ + +-input_scan= + +-ProjectScan + +-column_list=[$subquery1.$col1#1] + +-expr_list= + | +-$col1#1 := MakeProto(type=PROTO) + +-input_scan= + +-SingleRowScan +== + +# Struct value table subquery, with or without a name. +from (select as struct 1 x, 3){{ named|}} +-- +ALTERNATION GROUP: named +-- +QueryStmt ++-output_column_list= +| +-$make_struct.$struct#3 AS `$value` [STRUCT] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_struct.$struct#3] + +-expr_list= + | +-$struct#3 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=named.x#1) + | +-ColumnRef(type=INT64, column=named.$col2#2) + +-input_scan= + +-ProjectScan + +-column_list=named.[x#1, $col2#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-$col2#2 := Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$make_struct.$struct#3 AS `$struct` [STRUCT] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_struct.$struct#3] + +-expr_list= + | +-$struct#3 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$subquery1.x#1) + | +-ColumnRef(type=INT64, column=$subquery1.$col2#2) + +-input_scan= + +-ProjectScan + +-column_list=$subquery1.[x#1, $col2#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-$col2#2 := Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +== + +# Value table inputs, but the output with two columns is not a value table. +from TestExtraValueTable, unnest([1]) ar +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS TestExtraValueTable [PROTO] +| +-$array.ar#4 AS ar [INT64] ++-query= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1, $array.ar#4] + +-describe_text= + | """ + | NameList: + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | ar INT64 $array.ar#4 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | ar -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | $array.ar#4 + | """ + +-input_scan= + +-ArrayScan + +-column_list=[TestExtraValueTable.value#1, $array.ar#4] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1]) + +-element_column_list=[$array.ar#4] +== + +from Int32ArrayValueTable t +-- +QueryStmt ++-output_column_list= +| +-Int32ArrayValueTable.value#1 AS `$value` [ARRAY] ++-is_value_table=TRUE ++-query= + +-TableScan(column_list=[Int32ArrayValueTable.value#1], table=Int32ArrayValueTable, column_index_list=[0], alias="t") +== + +from Int32ArrayValueTable t, t +-- +ERROR: Table not found: t (Unqualified identifiers in a FROM clause are always resolved as tables. Identifier t is in scope but unqualified names cannot be resolved here.) [at 1:30] +from Int32ArrayValueTable t, t + ^ +== + +from Int32ArrayValueTable t, unnest(t) tt +-- +QueryStmt ++-output_column_list= +| +-Int32ArrayValueTable.value#1 AS t [ARRAY] +| +-$array.tt#2 AS tt [INT32] ++-query= + +-ArrayScan + +-column_list=[Int32ArrayValueTable.value#1, $array.tt#2] + +-input_scan= + | +-TableScan(column_list=[Int32ArrayValueTable.value#1], table=Int32ArrayValueTable, column_index_list=[0], alias="t") + +-array_expr_list= + | +-ColumnRef(type=ARRAY, column=Int32ArrayValueTable.value#1) + +-element_column_list=[$array.tt#2] +== + +# Pseudo-columns from value table inputs are visible after the FROM. +# The output is still a value table. +from TestExtraValueTable +|> STATIC_DESCRIBE +|> where filename='' +|> where TestExtraValueTable.filename='' +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-input_scan= + | +-FilterScan + | +-column_list=TestExtraValueTable.[value#1, Filename#2] + | +-input_scan= + | | +-StaticDescribeScan + | | +-column_list=TestExtraValueTable.[value#1, Filename#2] + | | +-describe_text= + | | | """ + | | | NameList (is_value_table = true): + | | | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | | | NameScope: + | | | Names: + | | | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | | | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | | | Range variables: + | | | TestExtraValueTable -> RANGE_VARIABLE<$value> + | | | Value table columns: + | | | TestExtraValueTable.value#1 + | | | """ + | | +-input_scan= + | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + | +-Literal(type=STRING, value="") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="") +== + +# Read a pseudo-column from a single-table FROM with an alias. +from TestExtraValueTable v +|> where filename='' +|> where v.filename='' +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-input_scan= + | +-FilterScan + | +-column_list=TestExtraValueTable.[value#1, Filename#2] + | +-input_scan= + | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="v") + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + | +-Literal(type=STRING, value="") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="") +== + +# The table name is not visible from underneath its alias. +from TestExtraValueTable v +|> where TestExtraValueTable.filename='' +-- +ERROR: Unrecognized name: TestExtraValueTable [at 2:10] +|> where TestExtraValueTable.filename='' + ^ +== + +# SELECT * after FROM does not produce the pseudo-columns. +from TestExtraValueTable +|> select * +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.int32_val1#4 AS int32_val1 [INT32] +| +-$pipe_select.int32_val2#5 AS int32_val2 [INT32] +| +-$pipe_select.str_value#6 AS str_value [ARRAY] ++-query= + +-ProjectScan + +-column_list=$pipe_select.[int32_val1#4, int32_val2#5, str_value#6] + +-expr_list= + | +-int32_val1#4 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-int32_val2#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-str_value#6 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +# Projecting an extra column makes the result no longer a value table. +# The output column from the value table still has its name. +from TestExtraValueTable +|> STATIC_DESCRIBE +|> extend 1 x +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS TestExtraValueTable [PROTO] +| +-$pipe_extend.x#4 AS x [INT64] ++-query= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1, $pipe_extend.x#4] + +-describe_text= + | """ + | NameList: + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | x INT64 $pipe_extend.x#4 + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | x -> INT64 ($pipe_extend.x#4) + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-ProjectScan + +-column_list=[TestExtraValueTable.value#1, $pipe_extend.x#4] + +-expr_list= + | +-x#4 := Literal(type=INT64, value=1) + +-input_scan= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1] + +-describe_text= + | """ + | NameList (is_value_table = true): + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +# Projecting an extra column makes the result not a value table. +# Since the value table had no alias, its output column is anonymous. +from (select as struct 1 y, 2 z) +|> extend 1 x +-- +QueryStmt ++-output_column_list= +| +-$make_struct.$struct#3 AS `$struct` [STRUCT] +| +-$pipe_extend.x#4 AS x [INT64] ++-query= + +-ProjectScan + +-column_list=[$make_struct.$struct#3, $pipe_extend.x#4] + +-expr_list= + | +-x#4 := Literal(type=INT64, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[$make_struct.$struct#3] + +-expr_list= + | +-$struct#3 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$subquery1.y#1) + | +-ColumnRef(type=INT64, column=$subquery1.z#2) + +-input_scan= + +-ProjectScan + +-column_list=$subquery1.[y#1, z#2] + +-expr_list= + | +-y#1 := Literal(type=INT64, value=1) + | +-z#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +# Read pseudo-columns from a multi-table FROM. +from TestExtraValueTable v1, TestExtraValueTable v2 +|> where v1.filename = v2.filename +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS v1 [PROTO] +| +-TestExtraValueTable.value#4 AS v2 [PROTO] ++-query= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2, value#4, Filename#5] + +-input_scan= + | +-JoinScan + | +-column_list=TestExtraValueTable.[value#1, Filename#2, value#4, Filename#5] + | +-left_scan= + | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="v1") + | +-right_scan= + | +-TableScan(column_list=TestExtraValueTable.[value#4, Filename#5], table=TestExtraValueTable, column_index_list=[0, 1], alias="v2") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#5) +== + +from TestExtraValueTable v1, TestExtraValueTable v2 +|> where v1.filename = '' +|> where filename = '' +-- +ERROR: Column name filename is ambiguous [at 3:10] +|> where filename = '' + ^ +== + +# All of these value table columns keep their names in the output, +# except the last one, which is an anonymous value table. +from TestExtraValueTable v1, + (from TestExtraValueTable), + (from TestExtraValueTable) AS row, + (from UNNEST([1])) +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS v1 [PROTO] +| +-TestExtraValueTable.value#4 AS TestExtraValueTable [PROTO] +| +-TestExtraValueTable.value#7 AS `row` [PROTO] +| +-$array.$unnest1#10 AS `$unnest1` [INT64] ++-query= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.value#4, TestExtraValueTable.value#7, $array.$unnest1#10] + +-describe_text= + | """ + | NameList: + | v1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#4 (value table) + | `row` zetasql_test__.TestExtraPB TestExtraValueTable.value#7 (value table) + | INT64 $array.$unnest1#10 (value table) + | NameScope: + | Names: + | Filename -> ambiguous + | RowId -> ambiguous + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | row -> RANGE_VARIABLE<$value> + | v1 -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | TestExtraValueTable.value#4 + | TestExtraValueTable.value#7 + | $array.$unnest1#10 + | """ + +-input_scan= + +-JoinScan + +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.value#4, TestExtraValueTable.value#7, $array.$unnest1#10] + +-left_scan= + | +-JoinScan + | +-column_list=TestExtraValueTable.[value#1, value#4, value#7] + | +-left_scan= + | | +-JoinScan + | | +-column_list=TestExtraValueTable.[value#1, value#4] + | | +-left_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="v1") + | | +-right_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + | +-right_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#7], table=TestExtraValueTable, column_index_list=[0]) + +-right_scan= + +-ArrayScan + +-column_list=[$array.$unnest1#10] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1]) + +-element_column_list=[$array.$unnest1#10] +== + +from TestExtraValueTable v1, (from TestExtraValueTable) +|> where v1.filename = '' +|> where TestExtraValueTable.filename = '' +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS v1 [PROTO] +| +-TestExtraValueTable.value#4 AS TestExtraValueTable [PROTO] ++-query= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2, value#4, Filename#5] + +-input_scan= + | +-FilterScan + | +-column_list=TestExtraValueTable.[value#1, Filename#2, value#4, Filename#5] + | +-input_scan= + | | +-JoinScan + | | +-column_list=TestExtraValueTable.[value#1, Filename#2, value#4, Filename#5] + | | +-left_scan= + | | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="v1") + | | +-right_scan= + | | +-TableScan(column_list=TestExtraValueTable.[value#4, Filename#5], table=TestExtraValueTable, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + | +-Literal(type=STRING, value="") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#5) + +-Literal(type=STRING, value="") +== + +# Both tables have the `filename` pseudo-column, so it's ambiguous +# when unqualified. +from TestExtraValueTable v1, (from TestExtraValueTable) +|> STATIC_DESCRIBE +|> where v1.filename = '' +|> where filename = '' +-- +ERROR: Column name filename is ambiguous [at 4:10] +|> where filename = '' + ^ +== + +WITH q1 AS (from KeyValue) +FROM q1 +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-q1.Key#3 AS Key [INT64] +| +-q1.Value#4 AS Value [STRING] ++-query= + +-WithScan + +-column_list=q1.[Key#3, Value#4] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q1" + | +-with_subquery= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-query= + +-StaticDescribeScan + +-column_list=q1.[Key#3, Value#4] + +-describe_text= + | """ + | NameList: + | Key INT64 q1.Key#3 + | Value STRING q1.Value#4 + | NameScope: + | Names: + | Key -> INT64 (q1.Key#3) (implicit) + | Value -> STRING (q1.Value#4) (implicit) + | Range variables: + | q1 -> RANGE_VARIABLE + | """ + +-input_scan= + +-WithRefScan(column_list=q1.[Key#3, Value#4], with_query_name="q1") +== + +# FROM queries in both places in a WITH clause. +WITH q1 AS (from KeyValue), + q2 AS (select 5 key, "abc" abc) +FROM q1 JOIN q2 USING (key) +-- +QueryStmt ++-output_column_list= +| +-q1.Key#5 AS key [INT64] +| +-q1.Value#6 AS Value [STRING] +| +-q2.abc#8 AS abc [STRING] ++-query= + +-WithScan + +-column_list=[q1.Key#5, q1.Value#6, q2.key#7, q2.abc#8] + +-with_entry_list= + | +-WithEntry + | | +-with_query_name="q1" + | | +-with_subquery= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-WithEntry + | +-with_query_name="q2" + | +-with_subquery= + | +-ProjectScan + | +-column_list=q2.[key#3, abc#4] + | +-expr_list= + | | +-key#3 := Literal(type=INT64, value=5) + | | +-abc#4 := Literal(type=STRING, value="abc") + | +-input_scan= + | +-SingleRowScan + +-query= + +-JoinScan + +-column_list=[q1.Key#5, q1.Value#6, q2.key#7, q2.abc#8] + +-left_scan= + | +-WithRefScan(column_list=q1.[Key#5, Value#6], with_query_name="q1") + +-right_scan= + | +-WithRefScan(column_list=q2.[key#7, abc#8], with_query_name="q2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=q1.Key#5) + | +-ColumnRef(type=INT64, column=q2.key#7) + +-has_using=TRUE +== + +# FROM queries with pipes around a WITH. +with q1 as (from KeyValue |> where key=5) +from q1 join q1 q1b using (key) +|> where q1.value = q1b.value +-- +QueryStmt ++-output_column_list= +| +-q1.Key#3 AS key [INT64] +| +-q1.Value#4 AS Value [STRING] +| +-q1.Value#6 AS Value [STRING] ++-query= + +-WithScan + +-column_list=q1.[Key#3, Value#4, Key#5, Value#6] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q1" + | +-with_subquery= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=5) + +-query= + +-FilterScan + +-column_list=q1.[Key#3, Value#4, Key#5, Value#6] + +-input_scan= + | +-JoinScan + | +-column_list=q1.[Key#3, Value#4, Key#5, Value#6] + | +-left_scan= + | | +-WithRefScan(column_list=q1.[Key#3, Value#4], with_query_name="q1") + | +-right_scan= + | | +-WithRefScan(column_list=q1.[Key#5, Value#6], with_query_name="q1") + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=q1.Key#3) + | | +-ColumnRef(type=INT64, column=q1.Key#5) + | +-has_using=TRUE + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=q1.Value#4) + +-ColumnRef(type=STRING, column=q1.Value#6) +== + +# Test FROM query with hinted value table. +FROM TestExtraValueTable @{hint=1} +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-TableScan + +-column_list=[TestExtraValueTable.value#1] + +-hint_list= + | +-hint := Literal(type=INT64, value=1) + +-table=TestExtraValueTable + +-column_index_list=[0] +== + +[language_features=PIPES,TABLESAMPLE] +# Test FROM query with TABLESAMPLE on a value table. +FROM TestExtraValueTable TABLESAMPLE bernoulli(5 ROWS) +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-SampleScan + +-column_list=[TestExtraValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=5) + +-unit=ROWS +== + +[language_features=PIPES,V_1_3_PIVOT] +# Test FROM query with PIVOT postfix operator. +FROM KeyValue PIVOT(sum(key) FOR value IN ('x')) +-- +QueryStmt ++-output_column_list= +| +-$pivot.x#4 AS x [INT64] ++-query= + +-PivotScan + +-column_list=[$pivot.x#4] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-pivot_expr_list= + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-parse_location=67-75 + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-for_expr= + | +-ColumnRef(parse_location=80-85, type=STRING, column=KeyValue.Value#2) + +-pivot_value_list= + | +-Literal(type=STRING, value="x", preserve_in_literal_remover=TRUE) + +-pivot_column_list= + +-PivotColumn(column=$pivot.x#4, pivot_expr_index=0, pivot_value_index=0) +== + +# CTAS is another statement with a different code path for handling +# output_column_list and is_value_table. +create table T as +from TestExtraValueTable +|> where true +-- +CreateTableAsSelectStmt ++-name_path=T ++-column_definition_list= +| +-ColumnDefinition(name="$value", type=PROTO, column=T.TestExtraValueTable#4) ++-is_value_table=TRUE ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +# CTAS with a column list is a different case, but does not support +# value tables. +[language_features=PIPES,CREATE_TABLE_AS_SELECT_COLUMN_LIST] +create table T (col zetasql_test__.TestExtraPB) as +from TestExtraValueTable v +|> select v +-- +CreateTableAsSelectStmt ++-name_path=T ++-column_definition_list= +| +-ColumnDefinition(name="col", type=PROTO, column=T.col#1) ++-output_column_list= +| +-TestExtraValueTable.value#2 AS col [PROTO] ++-query= + +-ProjectScan + +-column_list=[TestExtraValueTable.value#2] + +-input_scan= + +-TableScan(column_list=[TestExtraValueTable.value#2], table=TestExtraValueTable, column_index_list=[0], alias="v") +== + +# CREATE VIEW is another statement with a different code path for handling +# output_column_list and is_value_table. +create view V as +from TestExtraValueTable +|> where true +-- +CreateViewStmt ++-name_path=V ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-query= +| +-FilterScan +| +-column_list=[TestExtraValueTable.value#1] +| +-input_scan= +| | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +| +-filter_expr= +| +-Literal(type=BOOL, value=true) ++-sql="from TestExtraValueTable\n|> where true" ++-is_value_table=TRUE ++-column_definition_list= + +-ColumnDefinition(name="$value", type=PROTO, column=V.TestExtraValueTable#4) +== + +# EXPORT DATA is another statement with a different code path for handling +# output_column_list and is_value_table. +export data options() as +from TestExtraValueTable +|> where true +-- +ExportDataStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +# CREATE MODEL is another statement with a different code path for handling +# output_column_list. It does not record is_value_table. +create model M as +from TestExtraValueTable +|> where true +-- +CreateModelStmt ++-name_path=M ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +# CREATE TVF is another statement with a different code path for handling +# output_column_list and is_value_table. +[language_features=PIPES,CREATE_TABLE_FUNCTION] +create table function TVF() AS ( + from TestExtraValueTable + |> where true +) +-- +CreateTableFunctionStmt ++-name_path=TVF ++-signature=() -> TABLE> ++-language="SQL" ++-code="from TestExtraValueTable\n |> where true" ++-query= +| +-FilterScan +| +-column_list=[TestExtraValueTable.value#1] +| +-input_scan= +| | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +| +-filter_expr= +| +-Literal(type=BOOL, value=true) ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE +== + +# CREATE TVF with a non-value-table. +[language_features=PIPES,CREATE_TABLE_FUNCTION] +create table function TVF() AS ( + from KeyValue + |> where true +) +-- +CreateTableFunctionStmt ++-name_path=TVF ++-signature=() -> TABLE ++-language="SQL" ++-code="from KeyValue\n |> where true" ++-query= +| +-FilterScan +| +-column_list=KeyValue.[Key#1, Value#2] +| +-input_scan= +| | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +| +-filter_expr= +| +-Literal(type=BOOL, value=true) ++-output_column_list= + +-KeyValue.Key#1 AS Key [INT64] + +-KeyValue.Value#2 AS Value [STRING] +== + +# There's no way for range variables to escape a table subquery +# with SELECT, since SELECT removes them. +SELECT kv.key FROM (SELECT * FROM KeyValue kv) +-- +ERROR: Unrecognized name: kv [at 1:8] +SELECT kv.key FROM (SELECT * FROM KeyValue kv) + ^ +== + +# With FROM in a table subquery, the range variable escapes. +SELECT key, kv.key FROM (FROM KeyValue kv) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Key#1] + +-input_scan= + +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0], alias="kv") +== + +# Inner range variable is hidden if we have an outer one. +SELECT {{key, kv2.key, kv2|kv.key|kv}} FROM (FROM KeyValue kv) kv2 +-- +ALTERNATION GROUP: key, kv2.key, kv2 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#1 AS key [INT64] +| +-$query.kv2#4 AS kv2 [STRUCT] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Key#1, $query.kv2#4] + +-expr_list= + | +-kv2#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") +-- +ALTERNATION GROUP: kv.key +-- +ERROR: Unrecognized name: kv [at 1:8] +SELECT kv.key FROM (FROM KeyValue kv) kv2 + ^ +-- +ALTERNATION GROUP: kv +-- +ERROR: Unrecognized name: kv [at 1:8] +SELECT kv FROM (FROM KeyValue kv) kv2 + ^ +== + +# Nested range variable does not work. +SELECT kv2.key, {{kv2.kv|kv2.kv.key}} FROM (FROM KeyValue kv) kv2 +-- +ALTERNATION GROUP: kv2.kv +-- +ERROR: Name kv not found inside kv2 [at 1:21] +SELECT kv2.key, kv2.kv FROM (FROM KeyValue kv) kv2 + ^ +-- +ALTERNATION GROUP: kv2.kv.key +-- +ERROR: Name kv not found inside kv2 [at 1:21] +SELECT kv2.key, kv2.kv.key FROM (FROM KeyValue kv) kv2 + ^ +== + +# Range variables come out even if we have multiple tables. +SELECT kv1.key, kv2.key +FROM (FROM KeyValue kv1, KeyValue kv2) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#3 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Key#3] + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Key#3] + +-left_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0], alias="kv1") + +-right_scan= + +-TableScan(column_list=[KeyValue.Key#3], table=KeyValue, column_index_list=[0], alias="kv2") +== + +# Same thing visible in pipe operators. +(FROM KeyValue kv1, KeyValue kv2) +|> WHERE kv1.key = kv2.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Key#3 AS Key [INT64] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-input_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=INT64, column=KeyValue.Key#3) +== + +# Case with inner range variable, an outer range variable, and +# pseudo-columns (`Filename`). +SELECT row.value, value, filename, row.filename +FROM (FROM EnumTable t1, KeyValue kv) row +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#7 AS value [STRING] +| +-KeyValue.Value#7 AS value [STRING] +| +-EnumTable.Filename#4 AS filename [STRING] +| +-EnumTable.Filename#4 AS filename [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Value#7, KeyValue.Value#7, EnumTable.Filename#4, EnumTable.Filename#4] + +-input_scan= + +-JoinScan + +-column_list=[EnumTable.Filename#4, KeyValue.Value#7] + +-left_scan= + | +-TableScan(column_list=[EnumTable.Filename#4], table=EnumTable, column_index_list=[3], alias="t1") + +-right_scan= + +-TableScan(column_list=[KeyValue.Value#7], table=KeyValue, column_index_list=[1], alias="kv") +== + +# Same thing in pipe syntax. +FROM (FROM EnumTable t1, KeyValue kv) row +|> SELECT row.value, value, filename, row.filename +-- +[SAME AS PREVIOUS] +== + +# Error cases from above query. +SELECT {{t1.filename|kv.value}} +FROM (FROM EnumTable t1, KeyValue kv) row +-- +ALTERNATION GROUP: t1.filename +-- +ERROR: Unrecognized name: t1 [at 1:8] +SELECT t1.filename + ^ +-- +ALTERNATION GROUP: kv.value +-- +ERROR: Unrecognized name: kv [at 1:8] +SELECT kv.value + ^ +== + +# Same error cases in pipe syntax. +FROM (FROM EnumTable t1, KeyValue kv) row +|> SELECT {{t1.filename|kv.value}} +-- +ALTERNATION GROUP: t1.filename +-- +ERROR: Unrecognized name: t1 [at 2:11] +|> SELECT t1.filename + ^ +-- +ALTERNATION GROUP: kv.value +-- +ERROR: Unrecognized name: kv [at 2:11] +|> SELECT kv.value + ^ +== + +# Test for ExtractTableNames, that it identifies all of these +# range variables correctly as array references rather than tables. +FROM (FROM TestTable tt, (FROM ArrayTypes ar)) +|> JOIN tt.KitchenSink.repeated_bool_val b1 +|> JOIN ar.Int32Array i1 +|> SELECT b1, i1, tt.key, ar.FloatArray +-- +QueryStmt ++-output_column_list= +| +-$array.b1#24 AS b1 [BOOL] +| +-$array.i1#25 AS i1 [INT32] +| +-TestTable.key#1 AS key [INT32] +| +-ArrayTypes.FloatArray#11 AS FloatArray [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$array.b1#24, $array.i1#25, TestTable.key#1, ArrayTypes.FloatArray#11] + +-input_scan= + +-ArrayScan + +-column_list=[TestTable.key#1, TestTable.KitchenSink#3, ArrayTypes.Int32Array#4, ArrayTypes.FloatArray#11, $array.b1#24, $array.i1#25] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.key#1, TestTable.KitchenSink#3, ArrayTypes.Int32Array#4, ArrayTypes.FloatArray#11, $array.b1#24] + | +-input_scan= + | | +-JoinScan + | | +-column_list=[TestTable.key#1, TestTable.KitchenSink#3, ArrayTypes.Int32Array#4, ArrayTypes.FloatArray#11] + | | +-left_scan= + | | | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2], alias="tt") + | | +-right_scan= + | | +-TableScan(column_list=ArrayTypes.[Int32Array#4, FloatArray#11], table=ArrayTypes, column_index_list=[0, 7], alias="ar") + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bool_val + | | +-default_value=[] + | +-element_column_list=[$array.b1#24] + +-array_expr_list= + | +-ColumnRef(type=ARRAY, column=ArrayTypes.Int32Array#4) + +-element_column_list=[$array.i1#25] +== + +# Same thing, but with a table alias. +FROM (FROM TestTable tt, (FROM ArrayTypes ar)) AS row +|> JOIN row.KitchenSink.repeated_bool_val b1 +|> JOIN row.Int32Array i1 +|> SELECT b1, i1, row.key, row.FloatArray +-- +QueryStmt ++-output_column_list= +| +-$array.b1#24 AS b1 [BOOL] +| +-$array.i1#25 AS i1 [INT32] +| +-TestTable.key#1 AS key [INT32] +| +-ArrayTypes.FloatArray#11 AS FloatArray [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$array.b1#24, $array.i1#25, TestTable.key#1, ArrayTypes.FloatArray#11] + +-input_scan= + +-ArrayScan + +-column_list=[TestTable.key#1, TestTable.KitchenSink#3, ArrayTypes.Int32Array#4, ArrayTypes.FloatArray#11, $array.b1#24, $array.i1#25] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.key#1, TestTable.KitchenSink#3, ArrayTypes.Int32Array#4, ArrayTypes.FloatArray#11, $array.b1#24] + | +-input_scan= + | | +-JoinScan + | | +-column_list=[TestTable.key#1, TestTable.KitchenSink#3, ArrayTypes.Int32Array#4, ArrayTypes.FloatArray#11] + | | +-left_scan= + | | | +-TableScan(column_list=TestTable.[key#1, KitchenSink#3], table=TestTable, column_index_list=[0, 2], alias="tt") + | | +-right_scan= + | | +-TableScan(column_list=ArrayTypes.[Int32Array#4, FloatArray#11], table=ArrayTypes, column_index_list=[0, 7], alias="ar") + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bool_val + | | +-default_value=[] + | +-element_column_list=[$array.b1#24] + +-array_expr_list= + | +-ColumnRef(type=ARRAY, column=ArrayTypes.Int32Array#4) + +-element_column_list=[$array.i1#25] +== + +# Test for ExtractTableNames, that it realizes the nested_catalog +# inner range variable is dropped, so nested_catalog in the JOIN +# is actually a catalog name. +FROM (FROM TestTable nested_catalog, KeyValue kv) row +|> CROSS JOIN nested_catalog.KeyValueNested kvn +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-KeyValue.Key#4 AS Key [INT64] +| +-KeyValue.Value#5 AS Value [STRING] +| +-KeyValueNested.Key#6 AS Key [INT64] +| +-KeyValueNested.Value#7 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, KeyValue.Key#4, KeyValue.Value#5, KeyValueNested.Key#6, KeyValueNested.Value#7] + +-left_scan= + | +-JoinScan + | +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, KeyValue.Key#4, KeyValue.Value#5] + | +-left_scan= + | | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2], alias="nested_catalog") + | +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#4, Value#5], table=KeyValue, column_index_list=[0, 1], alias="kv") + +-right_scan= + +-TableScan(column_list=KeyValueNested.[Key#6, Value#7], table=nested_catalog.KeyValueNested, column_index_list=[0, 1], alias="kvn") +== + +# Case with ambiguous columns and pseudo-columns. +SELECT {{key|filename|row.key|row.filename|t1.key}} +FROM (FROM EnumTable t1, EnumTable t2) row +-- +ALTERNATION GROUP: key +-- +ERROR: Column name key is ambiguous [at 1:8] +SELECT key + ^ +-- +ALTERNATION GROUP: filename +-- +ERROR: Column name filename is ambiguous [at 1:8] +SELECT filename + ^ +-- +ALTERNATION GROUP: row.key +-- +ERROR: Name key is ambiguous inside row [at 1:12] +SELECT row.key + ^ +-- +ALTERNATION GROUP: row.filename +-- +ERROR: Name filename is ambiguous inside row [at 1:12] +SELECT row.filename + ^ +-- +ALTERNATION GROUP: t1.key +-- +ERROR: Unrecognized name: t1 [at 1:8] +SELECT t1.key + ^ +== + +# Case with duplicate range variable coming from two subqueries. +{{SELECT *|}} +FROM (FROM EnumTable), (FROM EnumTable) +-- +ALTERNATION GROUP: SELECT * +-- +ERROR: Duplicate table alias EnumTable in the same FROM clause [at 2:24] +FROM (FROM EnumTable), (FROM EnumTable) + ^ +-- +ALTERNATION GROUP: +-- +ERROR: Duplicate table alias EnumTable in the same FROM clause [at 1:24] +FROM (FROM EnumTable), (FROM EnumTable) + ^ +== + +# The inner range variables propagate out of the subqueries, and cause +# a collision. +{{SELECT *|}} FROM + (FROM EnumTable), (FROM EnumTable) +-- +ERROR: Duplicate table alias EnumTable in the same FROM clause [at 2:21] + (FROM EnumTable), (FROM EnumTable) + ^ +== + +# The inner range variables are replaced by new aliases that are different, +# so there is no collison. +FROM (FROM EnumTable) row1, (FROM EnumTable) row2 +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-EnumTable.key#6 AS key [INT32] +| +-EnumTable.TestEnum#7 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#8 AS AnotherTestEnum [ENUM] ++-query= + +-StaticDescribeScan + +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, key#6, TestEnum#7, AnotherTestEnum#8] + +-describe_text= + | """ + | NameList: + | key INT32 EnumTable.key#1 + | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | key INT32 EnumTable.key#6 + | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#7 + | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#8 + | NameScope: + | Names: + | AnotherTestEnum -> ambiguous + | Filename -> ambiguous + | RowId -> ambiguous + | TestEnum -> ambiguous + | key -> ambiguous + | Range variables: + | row1 -> RANGE_VARIABLE + | row2 -> RANGE_VARIABLE + | """ + +-input_scan= + +-JoinScan + +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, key#6, TestEnum#7, AnotherTestEnum#8] + +-left_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3], table=EnumTable, column_index_list=[0, 1, 2]) + +-right_scan= + +-TableScan(column_list=EnumTable.[key#6, TestEnum#7, AnotherTestEnum#8], table=EnumTable, column_index_list=[0, 1, 2]) +== + +# Those collisions always happen for value tables, since the value table name +# always comes out as a range variable, even with a new alias for the subquery. +# This passes if either the value table is re-aliased, or the row is aliases, +# which makes the value table name a column name rather than a range variable. +FROM (FROM KeyValue kv1, TestExtraValueTable), + (FROM KeyValue kv2, TestExtraValueTable {{|AS vt2}}){{| AS row}} +|> STATIC_DESCRIBE +-- +ALTERNATION GROUP: +-- +ERROR: Duplicate table alias TestExtraValueTable in the same FROM clause [at 2:6] + (FROM KeyValue kv2, TestExtraValueTable ) + ^ +-- +ALTERNATION GROUP: AS row +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-TestExtraValueTable.value#3 AS TestExtraValueTable [PROTO] +| +-KeyValue.Key#6 AS Key [INT64] +| +-KeyValue.Value#7 AS Value [STRING] +| +-TestExtraValueTable.value#8 AS TestExtraValueTable [PROTO] ++-query= + +-StaticDescribeScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3, KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#3 (value table) + | Key INT64 KeyValue.Key#6 + | Value STRING KeyValue.Value#7 + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#8 (value table) + | NameScope: + | Names: + | Filename -> ambiguous + | Key -> ambiguous + | RowId -> ambiguous + | Value -> ambiguous + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | kv1 -> RANGE_VARIABLE + | row -> RANGE_VARIABLE + | Value table columns: + | TestExtraValueTable.value#3 + | TestExtraValueTable.value#8 + | """ + +-input_scan= + +-JoinScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3, KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-left_scan= + | +-JoinScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | +-right_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#3], table=TestExtraValueTable, column_index_list=[0]) + +-right_scan= + +-JoinScan + +-column_list=[KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#6, Value#7], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-right_scan= + +-TableScan(column_list=[TestExtraValueTable.value#8], table=TestExtraValueTable, column_index_list=[0]) +-- +ALTERNATION GROUP: AS vt2, +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-TestExtraValueTable.value#3 AS TestExtraValueTable [PROTO] +| +-KeyValue.Key#6 AS Key [INT64] +| +-KeyValue.Value#7 AS Value [STRING] +| +-TestExtraValueTable.value#8 AS vt2 [PROTO] ++-query= + +-StaticDescribeScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3, KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#3 (value table) + | Key INT64 KeyValue.Key#6 + | Value STRING KeyValue.Value#7 + | vt2 zetasql_test__.TestExtraPB TestExtraValueTable.value#8 (value table) + | NameScope: + | Names: + | Filename -> ambiguous + | Key -> ambiguous + | RowId -> ambiguous + | Value -> ambiguous + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | kv1 -> RANGE_VARIABLE + | kv2 -> RANGE_VARIABLE + | vt2 -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#3 + | TestExtraValueTable.value#8 + | """ + +-input_scan= + +-JoinScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3, KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-left_scan= + | +-JoinScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | +-right_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#3], table=TestExtraValueTable, column_index_list=[0]) + +-right_scan= + +-JoinScan + +-column_list=[KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#6, Value#7], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-right_scan= + +-TableScan(column_list=[TestExtraValueTable.value#8], table=TestExtraValueTable, column_index_list=[0], alias="vt2") +-- +ALTERNATION GROUP: AS vt2, AS row +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-TestExtraValueTable.value#3 AS TestExtraValueTable [PROTO] +| +-KeyValue.Key#6 AS Key [INT64] +| +-KeyValue.Value#7 AS Value [STRING] +| +-TestExtraValueTable.value#8 AS vt2 [PROTO] ++-query= + +-StaticDescribeScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3, KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#3 (value table) + | Key INT64 KeyValue.Key#6 + | Value STRING KeyValue.Value#7 + | vt2 zetasql_test__.TestExtraPB TestExtraValueTable.value#8 (value table) + | NameScope: + | Names: + | Filename -> ambiguous + | Key -> ambiguous + | RowId -> ambiguous + | Value -> ambiguous + | vt2 -> zetasql_test__.TestExtraPB (TestExtraValueTable.value#8) + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | kv1 -> RANGE_VARIABLE + | row -> RANGE_VARIABLE + | Value table columns: + | TestExtraValueTable.value#3 + | TestExtraValueTable.value#8 + | """ + +-input_scan= + +-JoinScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3, KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-left_scan= + | +-JoinScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, TestExtraValueTable.value#3] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | +-right_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#3], table=TestExtraValueTable, column_index_list=[0]) + +-right_scan= + +-JoinScan + +-column_list=[KeyValue.Key#6, KeyValue.Value#7, TestExtraValueTable.value#8] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#6, Value#7], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-right_scan= + +-TableScan(column_list=[TestExtraValueTable.value#8], table=TestExtraValueTable, column_index_list=[0], alias="vt2") +== + +# Multi-table FROM inside a WITH clause. +# Column names are visible outside. `key` is ambiguous. +WITH q AS (FROM EnumTable et, KeyValue kv) +SELECT TestEnum, value, q.TestEnum, q.value{{|,key|,q.key}} +FROM q +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-q.TestEnum#9 AS TestEnum [ENUM] +| +-q.Value#12 AS value [STRING] +| +-q.TestEnum#9 AS TestEnum [ENUM] +| +-q.Value#12 AS value [STRING] ++-query= + +-WithScan + +-column_list=q.[TestEnum#9, Value#12, TestEnum#9, Value#12] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q" + | +-with_subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, KeyValue.Key#6, KeyValue.Value#7] + | +-input_scan= + | +-JoinScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, KeyValue.Key#6, KeyValue.Value#7] + | +-left_scan= + | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3], table=EnumTable, column_index_list=[0, 1, 2], alias="et") + | +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#6, Value#7], table=KeyValue, column_index_list=[0, 1], alias="kv") + +-query= + +-ProjectScan + +-column_list=q.[TestEnum#9, Value#12, TestEnum#9, Value#12] + +-input_scan= + +-WithRefScan(column_list=q.[key#8, TestEnum#9, AnotherTestEnum#10, Key#11, Value#12], with_query_name="q") +-- +ALTERNATION GROUP: ,key +-- +ERROR: Column name key is ambiguous [at 2:45] +SELECT TestEnum, value, q.TestEnum, q.value,key + ^ +-- +ALTERNATION GROUP: ,q.key +-- +ERROR: Name key is ambiguous inside q [at 2:47] +SELECT TestEnum, value, q.TestEnum, q.value,q.key + ^ +== + +# Range variables from inside the WITH subquery do not escape it. +WITH q AS (FROM EnumTable et, KeyValue kv) +SELECT TestEnum, value, {{et.TestEnum|kv.key|et|kv}} +FROM q +-- +ALTERNATION GROUP: et.TestEnum +-- +ERROR: Unrecognized name: et [at 2:25] +SELECT TestEnum, value, et.TestEnum + ^ +-- +ALTERNATION GROUP: kv.key +-- +ERROR: Unrecognized name: kv [at 2:25] +SELECT TestEnum, value, kv.key + ^ +-- +ALTERNATION GROUP: et +-- +ERROR: Unrecognized name: et [at 2:25] +SELECT TestEnum, value, et + ^ +-- +ALTERNATION GROUP: kv +-- +ERROR: Unrecognized name: kv [at 2:25] +SELECT TestEnum, value, kv + ^ +== + +# A FROM query in WITH may produce pseudo-columns (`Filename`), but they +# don't escape the WITH. +WITH q AS (FROM EnumTable et, KeyValue kv + |> WHERE Filename='abc') +SELECT value, q.TestEnum, {{Filename|et.Filename|q.Filename}} +FROM q +-- +ALTERNATION GROUP: Filename +-- +ERROR: Unrecognized name: Filename [at 3:27] +SELECT value, q.TestEnum, Filename + ^ +-- +ALTERNATION GROUP: et.Filename +-- +ERROR: Unrecognized name: et [at 3:27] +SELECT value, q.TestEnum, et.Filename + ^ +-- +ALTERNATION GROUP: q.Filename +-- +ERROR: Name Filename not found inside q [at 3:29] +SELECT value, q.TestEnum, q.Filename + ^ +== + +# This shows the full NameList produced by the WITH ref, when the inner query +# had a pseudo-column. The pseudo-column does not come out. +# Note the ProjectScan added inside the WITH subquery to drop +# the pseudo-columns from the column_list. +WITH q AS (FROM EnumTable et + |> STATIC_DESCRIBE + |> WHERE filename='') +FROM q +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-q.key#6 AS key [INT32] +| +-q.TestEnum#7 AS TestEnum [ENUM] +| +-q.AnotherTestEnum#8 AS AnotherTestEnum [ENUM] ++-query= + +-WithScan + +-column_list=q.[key#6, TestEnum#7, AnotherTestEnum#8] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q" + | +-with_subquery= + | +-ProjectScan + | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3] + | +-input_scan= + | +-FilterScan + | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4] + | +-input_scan= + | | +-StaticDescribeScan + | | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4] + | | +-describe_text= + | | | """ + | | | NameList: + | | | key INT32 EnumTable.key#1 + | | | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | | | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | | | NameScope: + | | | Names: + | | | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (EnumTable.AnotherTestEnum#3) (implicit) + | | | Filename -> STRING (EnumTable.Filename#4) (implicit) (pseudo-column) + | | | RowId -> BYTES (EnumTable.RowId#5) (implicit) (pseudo-column) + | | | TestEnum -> zetasql_test__.TestEnum (EnumTable.TestEnum#2) (implicit) + | | | key -> INT32 (EnumTable.key#1) (implicit) + | | | Range variables: + | | | et -> RANGE_VARIABLE + | | | """ + | | +-input_scan= + | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4], table=EnumTable, column_index_list=[0, 1, 2, 3], alias="et") + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=EnumTable.Filename#4) + | +-Literal(type=STRING, value="") + +-query= + +-StaticDescribeScan + +-column_list=q.[key#6, TestEnum#7, AnotherTestEnum#8] + +-describe_text= + | """ + | NameList: + | key INT32 q.key#6 + | TestEnum zetasql_test__.TestEnum q.TestEnum#7 + | AnotherTestEnum zetasql_test__.AnotherTestEnum q.AnotherTestEnum#8 + | NameScope: + | Names: + | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (q.AnotherTestEnum#8) (implicit) + | TestEnum -> zetasql_test__.TestEnum (q.TestEnum#7) (implicit) + | key -> INT32 (q.key#6) (implicit) + | Range variables: + | q -> RANGE_VARIABLE + | """ + +-input_scan= + +-WithRefScan(column_list=q.[key#6, TestEnum#7, AnotherTestEnum#8], with_query_name="q") +== + +# Test a WITH RECURSIVE case with pseudo-columns, which has a different code +# path for making the SetOperationItems. +# The pseudo-columns are pruned from the column_lists. +[language_features=PIPES,V_1_3_WITH_RECURSIVE,PIPE_STATIC_DESCRIBE] +WITH RECURSIVE q AS ((FROM EnumTable + |> WHERE Filename='x') + UNION ALL + (FROM q + |> WHERE true)) +FROM q +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-q.key#12 AS key [INT32] +| +-q.TestEnum#13 AS TestEnum [ENUM] +| +-q.AnotherTestEnum#14 AS AnotherTestEnum [ENUM] ++-query= + +-WithScan + +-column_list=q.[key#12, TestEnum#13, AnotherTestEnum#14] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q" + | +-with_subquery= + | +-RecursiveScan + | +-column_list=$union_all.[key#6, TestEnum#7, AnotherTestEnum#8] + | +-op_type=UNION_ALL + | +-non_recursive_term= + | | +-SetOperationItem + | | +-scan= + | | | +-FilterScan + | | | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4], table=EnumTable, column_index_list=[0, 1, 2, 3]) + | | | +-filter_expr= + | | | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | | | +-ColumnRef(type=STRING, column=EnumTable.Filename#4) + | | | +-Literal(type=STRING, value="x") + | | +-output_column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3] + | +-recursive_term= + | +-SetOperationItem + | +-scan= + | | +-FilterScan + | | +-column_list=q.[key#9, TestEnum#10, AnotherTestEnum#11] + | | +-input_scan= + | | | +-RecursiveRefScan(column_list=q.[key#9, TestEnum#10, AnotherTestEnum#11]) + | | +-filter_expr= + | | +-Literal(type=BOOL, value=true) + | +-output_column_list=q.[key#9, TestEnum#10, AnotherTestEnum#11] + +-query= + | +-StaticDescribeScan + | +-column_list=q.[key#12, TestEnum#13, AnotherTestEnum#14] + | +-describe_text= + | | """ + | | NameList: + | | key INT32 q.key#12 + | | TestEnum zetasql_test__.TestEnum q.TestEnum#13 + | | AnotherTestEnum zetasql_test__.AnotherTestEnum q.AnotherTestEnum#14 + | | NameScope: + | | Names: + | | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (q.AnotherTestEnum#14) (implicit) + | | TestEnum -> zetasql_test__.TestEnum (q.TestEnum#13) (implicit) + | | key -> INT32 (q.key#12) (implicit) + | | Range variables: + | | q -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-WithRefScan(column_list=q.[key#12, TestEnum#13, AnotherTestEnum#14], with_query_name="q") + +-recursive=TRUE +== + +# FROM doing an array scan of column and field path on a table. +[language_features=PIPES,V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,PIPE_STATIC_DESCRIBE] +from TestTable.KitchenSink.nested_value.nested_repeated_int64 +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-$array.nested_repeated_int64#4 AS `$value` [INT64] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[$array.nested_repeated_int64#4] + +-describe_text= + | """ + | NameList (is_value_table = true): + | nested_repeated_int64 INT64 $array.nested_repeated_int64#4 (value table) + | NameScope: + | Range variables: + | nested_repeated_int64 -> RANGE_VARIABLE<$value> + | Value table columns: + | $array.nested_repeated_int64#4 + | """ + +-input_scan= + +-ArrayScan + +-column_list=[$array.nested_repeated_int64#4] + +-node_source="single_table_array_name_path" + +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-array_expr_list= + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-GetProtoField + | | +-type=PROTO + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=nested_value + | | +-default_value=NULL + | +-field_descriptor=nested_repeated_int64 + | +-default_value=[] + +-element_column_list=[$array.nested_repeated_int64#4] +== + +# FROM doing an array scan of column and field path on a table, with flattening. +[language_features=PIPES,V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,V_1_3_UNNEST_AND_FLATTEN_ARRAYS,PIPE_STATIC_DESCRIBE] +from TestTable.KitchenSink.nested_repeated_value.nested_repeated_int64 +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-$array.nested_repeated_int64#4 AS `$value` [INT64] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[$array.nested_repeated_int64#4] + +-describe_text= + | """ + | NameList (is_value_table = true): + | nested_repeated_int64 INT64 $array.nested_repeated_int64#4 (value table) + | NameScope: + | Range variables: + | nested_repeated_int64 -> RANGE_VARIABLE<$value> + | Value table columns: + | $array.nested_repeated_int64#4 + | """ + +-input_scan= + +-ArrayScan + +-column_list=[$array.nested_repeated_int64#4] + +-node_source="single_table_array_name_path" + +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-array_expr_list= + | +-Flatten + | +-type=ARRAY + | +-expr= + | | +-GetProtoField + | | +-type=ARRAY> + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=nested_repeated_value + | | +-default_value=[] + | +-get_field_list= + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-FlattenedArg(type=PROTO) + | +-field_descriptor=nested_repeated_int64 + | +-default_value=[] + +-element_column_list=[$array.nested_repeated_int64#4] +== + +# Set operation over FROM queries must preserve their column_lists. +# Pseudo-columns don't make it through UNION ALL. +(FROM EnumTable) +UNION ALL +(FROM EnumTable) +|> STATIC_DESCRIBE +|> WHERE {{key|filename}} IS NULL +-- +ALTERNATION GROUP: key +-- +QueryStmt ++-output_column_list= +| +-$union_all.key#11 AS key [INT32] +| +-$union_all.TestEnum#12 AS TestEnum [ENUM] +| +-$union_all.AnotherTestEnum#13 AS AnotherTestEnum [ENUM] ++-query= + +-FilterScan + +-column_list=$union_all.[key#11, TestEnum#12, AnotherTestEnum#13] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=$union_all.[key#11, TestEnum#12, AnotherTestEnum#13] + | +-describe_text= + | | """ + | | NameList: + | | key INT32 $union_all.key#11 + | | TestEnum zetasql_test__.TestEnum $union_all.TestEnum#12 + | | AnotherTestEnum zetasql_test__.AnotherTestEnum $union_all.AnotherTestEnum#13 + | | NameScope: + | | Names: + | | AnotherTestEnum -> zetasql_test__.AnotherTestEnum ($union_all.AnotherTestEnum#13) (implicit) + | | TestEnum -> zetasql_test__.TestEnum ($union_all.TestEnum#12) (implicit) + | | key -> INT32 ($union_all.key#11) (implicit) + | | """ + | +-input_scan= + | +-SetOperationScan + | +-column_list=$union_all.[key#11, TestEnum#12, AnotherTestEnum#13] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3], table=EnumTable, column_index_list=[0, 1, 2]) + | | +-output_column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3] + | +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=EnumTable.[key#6, TestEnum#7, AnotherTestEnum#8], table=EnumTable, column_index_list=[0, 1, 2]) + | +-output_column_list=EnumTable.[key#6, TestEnum#7, AnotherTestEnum#8] + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(INT32) -> BOOL) + +-ColumnRef(type=INT32, column=$union_all.key#11) +-- +ALTERNATION GROUP: filename +-- +ERROR: Unrecognized name: filename [at 5:10] +|> WHERE filename IS NULL + ^ +== + +FROM UNNEST([1,2]) AS elem WITH OFFSET +|> STATIC_DESCRIBE +|> WHERE elem=2 +|> WHERE offset=3 +-- +QueryStmt ++-output_column_list= +| +-$array.elem#1 AS elem [INT64] +| +-$array_offset.offset#2 AS offset [INT64] ++-query= + +-FilterScan + +-column_list=[$array.elem#1, $array_offset.offset#2] + +-input_scan= + | +-FilterScan + | +-column_list=[$array.elem#1, $array_offset.offset#2] + | +-input_scan= + | | +-StaticDescribeScan + | | +-column_list=[$array.elem#1, $array_offset.offset#2] + | | +-describe_text= + | | | """ + | | | NameList: + | | | elem INT64 $array.elem#1 (value table) + | | | offset INT64 $array_offset.offset#2 (value table) + | | | NameScope: + | | | Range variables: + | | | elem -> RANGE_VARIABLE<$value> + | | | offset -> RANGE_VARIABLE<$value> + | | | Value table columns: + | | | $array.elem#1 + | | | $array_offset.offset#2 + | | | """ + | | +-input_scan= + | | +-ArrayScan + | | +-column_list=[$array.elem#1, $array_offset.offset#2] + | | +-array_expr_list= + | | | +-Literal(type=ARRAY, value=[1, 2]) + | | +-element_column_list=[$array.elem#1] + | | +-array_offset_column= + | | +-ColumnHolder(column=$array_offset.offset#2) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$array.elem#1) + | +-Literal(type=INT64, value=2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$array_offset.offset#2) + +-Literal(type=INT64, value=3) +== + +# Here, row ends up being the value table name, and the elem name is dropped. +SELECT {{elem|row.elem|row}} +FROM + (FROM UNNEST([1,2]) AS elem) AS row +-- +ALTERNATION GROUP: elem +-- +ERROR: Unrecognized name: elem [at 1:8] +SELECT elem + ^ +-- +ALTERNATION GROUP: row.elem +-- +ERROR: Cannot access field elem on a value with type INT64 [at 1:12] +SELECT row.elem + ^ +-- +ALTERNATION GROUP: row +-- +QueryStmt ++-output_column_list= +| +-$array.elem#1 AS `row` [INT64] ++-query= + +-ProjectScan + +-column_list=[$array.elem#1] + +-input_scan= + +-ArrayScan + +-column_list=[$array.elem#1] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.elem#1] +== + +# This shows an aliased table subquery with UNNEST. +SELECT key, row.key, elem, row.elem, row +FROM + (FROM KeyValue, UNNEST([1,2]) AS elem) AS row +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#1 AS key [INT64] +| +-$array.elem#3 AS elem [INT64] +| +-$array.elem#3 AS elem [INT64] +| +-$query.row#5 AS `row` [STRUCT] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Key#1, $array.elem#3, $array.elem#3, $query.row#5] + +-expr_list= + | +-row#5 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-ColumnRef(type=INT64, column=$array.elem#3) + +-input_scan= + +-ArrayScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.elem#3] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.elem#3] +== + +SELECT {{KeyValue|row.KeyValue|KeyValue.key|row.KeyValue.key}} +FROM + (FROM KeyValue, UNNEST([1,2]) AS elem) AS row +-- +ALTERNATION GROUP: KeyValue +-- +ERROR: Unrecognized name: KeyValue; Did you mean Value? [at 1:8] +SELECT KeyValue + ^ +-- +ALTERNATION GROUP: row.KeyValue +-- +ERROR: Name KeyValue not found inside row [at 1:12] +SELECT row.KeyValue + ^ +-- +ALTERNATION GROUP: KeyValue.key +-- +ERROR: Unrecognized name: KeyValue; Did you mean Value? [at 1:8] +SELECT KeyValue.key + ^ +-- +ALTERNATION GROUP: row.KeyValue.key +-- +ERROR: Name KeyValue not found inside row [at 1:12] +SELECT row.KeyValue.key + ^ +== + +# We have a value table, wrapped in a FROM query twice, with or without +# aliases at each level. +# The final output is always still a value table. +# Pseudo-columns work even if aliases are added, since the aliases become the +# new alias of the value table. +from (from (from TestExtraValueTable vt) {{|AS row1}}){{| AS row2}} +|> where filename = '' +|> STATIC_DESCRIBE +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-describe_text= + | """ + | NameList (is_value_table = true): + | vt zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | vt -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-input_scan= + | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="vt") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="") +-- +ALTERNATION GROUPS: + AS row2 + AS row1, AS row2 +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-describe_text= + | """ + | NameList (is_value_table = true): + | row2 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | row2 -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-input_scan= + | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="vt") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="") +-- +ALTERNATION GROUP: AS row1, +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-describe_text= + | """ + | NameList (is_value_table = true): + | row1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | row1 -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-input_scan= + | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="vt") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="") diff --git a/zetasql/analyzer/testdata/pipe_join.test b/zetasql/analyzer/testdata/pipe_join.test new file mode 100644 index 000000000..669555f19 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_join.test @@ -0,0 +1,1163 @@ +[default language_features=PIPES,TABLE_VALUED_FUNCTIONS,TABLESAMPLE,TABLESAMPLE_FROM_TABLE_VALUED_FUNCTIONS] +# At least one of these needs an alias, same as in a regular join, since we get +# a range variable for each. +from KeyValue +|> JOIN KeyValue USING (key) +-- +ERROR: Duplicate table alias KeyValue in the same FROM clause [at 4:18] +|> JOIN KeyValue USING (key) + ^ +== + +from KeyValue +|> JOIN KeyValue k2 +-- +ERROR: INNER JOIN must have an immediately following ON or USING clause [at 2:4] +|> JOIN KeyValue k2 + ^ +== + +# The table from the pipe input is not visible as a table name in the rhs. +from KeyValue kv +|> CROSS JOIN (SELECT * FROM kv) +-- +ERROR: Table not found: kv [at 2:30] +|> CROSS JOIN (SELECT * FROM kv) + ^ +== + +# We have a range variable for both lhs and rhs inputs in the output. +from KeyValue +|> JOIN KeyValue kv2 USING (key) +|> WHERE KeyValue.key = kv2.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-input_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | +-left_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-right_scan= + | | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + | +-has_using=TRUE + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=INT64, column=KeyValue.Key#3) +== + +from KeyValue +|> JOIN KeyValue USING (KeyValue) +-- +ERROR: Name KeyValue in USING clause is a table alias, not a column name, on left side of join [at 2:25] +|> JOIN KeyValue USING (KeyValue) + ^ +== + +# With successive joins, we still propagate all the the range variables from +# earlier lhs inputs. +# SELECT * produces the columns as expected from JOIN USING. +from KeyValue kv1 +|> JOIN KeyValue kv2 USING (key) +|> JOIN KeyValue kv3 USING (key) +|> WHERE kv1.key + kv2.key = kv3.key +|> SELECT * +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] +| +-KeyValue.Value#6 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Value#2, Value#4, Value#6] + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4, Key#5, Value#6] + +-input_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4, Key#5, Value#6] + | +-left_scan= + | | +-JoinScan + | | +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + | | +-left_scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + | | +-right_scan= + | | | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + | | +-join_expr= + | | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + | | +-has_using=TRUE + | +-right_scan= + | | +-TableScan(column_list=KeyValue.[Key#5, Value#6], table=KeyValue, column_index_list=[0, 1], alias="kv3") + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) + | +-has_using=TRUE + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-ColumnRef(type=INT64, column=KeyValue.Key#5) +== + +from KeyValue kv1 +|> JOIN KeyValue kv2 ON kv1.key = kv2.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Key#3 AS Key [INT64] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=INT64, column=KeyValue.Key#3) +== + +# JOIN with two anonymous input tables works with JOIN USING. +(select 1 x, 2 y) +|> JOIN (select 3 x, 4 z) USING (x) +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$subquery1.z#4 AS z [INT64] ++-query= + +-JoinScan + +-column_list=[$query.x#1, $query.y#2, $subquery1.x#3, $subquery1.z#4] + +-left_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-right_scan= + | +-ProjectScan + | +-column_list=$subquery1.[x#3, z#4] + | +-expr_list= + | | +-x#3 := Literal(type=INT64, value=3) + | | +-z#4 := Literal(type=INT64, value=4) + | +-input_scan= + | +-SingleRowScan + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-ColumnRef(type=INT64, column=$subquery1.x#3) + +-has_using=TRUE +== + +# With an anonymous lhs table, we can't write JOIN ON since referencing an lhs +# column would be ambiguous. So we should give an alias to the lhs table, see +# the next case below. +(select 1 x, 2 y) +|> JOIN (select 3 x, 4 z) t2 ON t2.x=x +-- +ERROR: Column name x is ambiguous [at 2:38] +|> JOIN (select 3 x, 4 z) t2 ON t2.x=x + ^ +== + +(select 1 x, 2 y) +|> AS t1 +|> JOIN (select 3 x, 4 z) t2 ON t2.x=t1.x; +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-t2.x#3 AS x [INT64] +| +-t2.z#4 AS z [INT64] ++-query= + +-JoinScan + +-column_list=[$query.x#1, $query.y#2, t2.x#3, t2.z#4] + +-left_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-right_scan= + | +-ProjectScan + | +-column_list=t2.[x#3, z#4] + | +-expr_list= + | | +-x#3 := Literal(type=INT64, value=3) + | | +-z#4 := Literal(type=INT64, value=4) + | +-input_scan= + | +-SingleRowScan + +-join_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=t2.x#3) + +-ColumnRef(type=INT64, column=$query.x#1) +== + +# This error comes from regular JOIN. +from TestTable +|> join KitchenSink.repeated_int32_val +-- +ERROR: Aliases referenced in the from clause must refer to preceding scans, and cannot refer to columns on those scans. KitchenSink refers to a column and must be qualified with a table name. [at 2:9] +|> join KitchenSink.repeated_int32_val + ^ +== + +# Join to unnest an array, with various forms. +from TestTable tt +|> join tt.KitchenSink.repeated_int32_val +|> left join tt.KitchenSink.repeated_bytes_val WITH OFFSET off +|> join UNNEST(KitchenSink.repeated_string_val) WITH OFFSET +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-$array.repeated_int32_val#4 AS repeated_int32_val [INT32] +| +-$array.repeated_bytes_val#5 AS repeated_bytes_val [BYTES] +| +-$array_offset.off#6 AS off [INT64] +| +-$array.$unnest1#7 AS `$unnest1` [STRING] +| +-$array_offset.offset#8 AS offset [INT64] ++-query= + +-ArrayScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.repeated_int32_val#4, $array.repeated_bytes_val#5, $array_offset.off#6, $array.$unnest1#7, $array_offset.offset#8] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.repeated_int32_val#4, $array.repeated_bytes_val#5, $array_offset.off#6] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.repeated_int32_val#4] + | | +-input_scan= + | | | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2], alias="tt") + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_int32_val + | | | +-default_value=[] + | | +-element_column_list=[$array.repeated_int32_val#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bytes_val + | | +-default_value=[] + | +-element_column_list=[$array.repeated_bytes_val#5] + | +-array_offset_column= + | | +-ColumnHolder(column=$array_offset.off#6) + | +-is_outer=TRUE + +-array_expr_list= + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-field_descriptor=repeated_string_val + | +-default_value=[] + +-element_column_list=[$array.$unnest1#7] + +-array_offset_column= + +-ColumnHolder(column=$array_offset.offset#8) +== + +# Multiple layers, unnesting protos. +from TestTable +|> join TestTable.KitchenSink.nested_repeated_value +|> join nested_repeated_value.value +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-$array.nested_repeated_value#4 AS nested_repeated_value [PROTO] +| +-$array.value#5 AS value [INT32] ++-query= + +-ArrayScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.nested_repeated_value#4, $array.value#5] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.nested_repeated_value#4] + | +-input_scan= + | | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY> + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=nested_repeated_value + | | +-default_value=[] + | +-element_column_list=[$array.nested_repeated_value#4] + +-array_expr_list= + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=$array.nested_repeated_value#4) + | +-field_descriptor=value + | +-default_value=[] + +-element_column_list=[$array.value#5] +== + +# Unnesting multiple layers of a proto in one step, with implicit flattening. +from TestTable +|> join TestTable.KitchenSink.nested_repeated_value.value +-- +ERROR: Cannot access field value on a value with type ARRAY [at 2:53] +|> join TestTable.KitchenSink.nested_repeated_value.value + ^ +== + +[language_features=PIPES,V_1_3_UNNEST_AND_FLATTEN_ARRAYS] +from TestTable +|> join TestTable.KitchenSink.nested_repeated_value.value +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-$array.value#4 AS value [INT32] ++-query= + +-ArrayScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.value#4] + +-input_scan= + | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + +-array_expr_list= + | +-Flatten + | +-type=ARRAY + | +-expr= + | | +-GetProtoField + | | +-type=ARRAY> + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=nested_repeated_value + | | +-default_value=[] + | +-get_field_list= + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-FlattenedArg(type=PROTO) + | +-field_descriptor=value + | +-default_value=[] + +-element_column_list=[$array.value#4] +== + +# Test various join type modifiers. +from KeyValue +|> {{LEFT|RIGHT|INNER|HASH|LOOKUP|CROSS|NATURAL}} JOIN + (from KeyValue kv2) USING (key) +-- +ALTERNATION GROUP: LEFT +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-join_type=LEFT + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +-- +ALTERNATION GROUP: RIGHT +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#3 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-join_type=RIGHT + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +-- +ALTERNATION GROUP: INNER +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +-- +ALTERNATION GROUP: HASH +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-hint_list= + | +-join_type := Literal(type=STRING, value="HASH_JOIN") + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +-- +ALTERNATION GROUP: LOOKUP +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-hint_list= + | +-join_type := Literal(type=STRING, value="LOOKUP_JOIN") + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +-- +ALTERNATION GROUP: CROSS +-- +ERROR: USING clause cannot be used with CROSS JOIN [at 3:24] + (from KeyValue kv2) USING (key) + ^ +-- +ALTERNATION GROUP: NATURAL +-- +ERROR: Natural join not supported [at 2:4] +|> NATURAL JOIN + ^ +== + +from KeyValue +|> CROSS JOIN KeyValue kv2 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Key#3 AS Key [INT64] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") +== + +# Special COALESCE column for FULL JOIN USING works. +# Stars expand to expected columns. +FROM KeyValue kv1 +|> FULL OUTER JOIN KeyValue kv2 USING (key) +|> SELECT kv1.key, kv2.key, key, 'xx', *, 'yy', kv1.* +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#3 AS key [INT64] +| +-$full_join.key#5 AS key [INT64] +| +-$pipe_select.$col4#6 AS `$col4` [STRING] +| +-$full_join.key#5 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] +| +-$pipe_select.$col6#7 AS `$col6` [STRING] +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Key#3, $full_join.key#5, $pipe_select.$col4#6, $full_join.key#5, KeyValue.Value#2, KeyValue.Value#4, $pipe_select.$col6#7, KeyValue.Key#1, KeyValue.Value#2] + +-expr_list= + | +-$col4#6 := Literal(type=STRING, value="xx") + | +-$col6#7 := Literal(type=STRING, value="yy") + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, KeyValue.Key#3, KeyValue.Value#4, $full_join.key#5] + +-expr_list= + | +-key#5 := + | +-FunctionCall(ZetaSQL:coalesce(repeated(2) INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-join_type=FULL + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +== + +# Join with a hint. +from KeyValue +|> join @{hint=1} KeyValue kv2 using(value) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#2 AS value [STRING] +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Key#3 AS Key [INT64] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-hint_list= + | +-hint := Literal(type=INT64, value=1) + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-ColumnRef(type=STRING, column=KeyValue.Value#4) + +-has_using=TRUE +== + +# JOIN USING with value tables. +# The second join lhs is the extracted join column from the first join, +# not another GetField. +FROM TestExtraValueTable t1 +|> JOIN TestExtraValueTable t2 USING (int32_val1, int32_val2) +|> JOIN TestExtraValueTable t3 USING (int32_val1) +-- +QueryStmt ++-output_column_list= +| +-$join_left.int32_val1#7 AS int32_val1 [INT32] +| +-$join_left.int32_val2#9 AS int32_val2 [INT32] +| +-TestExtraValueTable.value#1 AS t1 [PROTO] +| +-TestExtraValueTable.value#4 AS t2 [PROTO] +| +-TestExtraValueTable.value#11 AS t3 [PROTO] ++-query= + +-JoinScan + +-column_list=[TestExtraValueTable.value#1, $join_left.int32_val1#7, $join_left.int32_val2#9, TestExtraValueTable.value#4, $join_right.int32_val1#8, $join_right.int32_val2#10, TestExtraValueTable.value#11, $join_right.int32_val1#14] + +-left_scan= + | +-JoinScan + | +-column_list=[TestExtraValueTable.value#1, $join_left.int32_val1#7, $join_left.int32_val2#9, TestExtraValueTable.value#4, $join_right.int32_val1#8, $join_right.int32_val2#10] + | +-left_scan= + | | +-ProjectScan + | | +-column_list=[TestExtraValueTable.value#1, $join_left.int32_val1#7, $join_left.int32_val2#9] + | | +-expr_list= + | | | +-int32_val1#7 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-int32_val2#9 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val2 + | | | +-default_value=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="t1") + | +-right_scan= + | | +-ProjectScan + | | +-column_list=[TestExtraValueTable.value#4, $join_right.int32_val1#8, $join_right.int32_val2#10] + | | +-expr_list= + | | | +-int32_val1#8 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#4) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-int32_val2#10 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#4) + | | | +-field_descriptor=int32_val2 + | | | +-default_value=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0], alias="t2") + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + | | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | | | +-ColumnRef(type=INT32, column=$join_left.int32_val1#7) + | | | +-ColumnRef(type=INT32, column=$join_right.int32_val1#8) + | | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | | +-ColumnRef(type=INT32, column=$join_left.int32_val2#9) + | | +-ColumnRef(type=INT32, column=$join_right.int32_val2#10) + | +-has_using=TRUE + +-right_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#11, $join_right.int32_val1#14] + | +-expr_list= + | | +-int32_val1#14 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#11) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#11], table=TestExtraValueTable, column_index_list=[0], alias="t3") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$join_left.int32_val1#7) + | +-ColumnRef(type=INT32, column=$join_right.int32_val1#14) + +-has_using=TRUE +== + +# FULL JOIN USING between two struct value tables. This is interesting because +# it adds extra ProjectScans both before and after the join. +FROM TestExtraValueTable t1 +|> FULL JOIN TestExtraValueTable t2 USING (int32_val1) +-- +QueryStmt ++-output_column_list= +| +-$full_join.int32_val1#9 AS int32_val1 [INT32] +| +-TestExtraValueTable.value#1 AS t1 [PROTO] +| +-TestExtraValueTable.value#4 AS t2 [PROTO] ++-query= + +-ProjectScan + +-column_list=[TestExtraValueTable.value#1, $join_left.int32_val1#7, TestExtraValueTable.value#4, $join_right.int32_val1#8, $full_join.int32_val1#9] + +-expr_list= + | +-int32_val1#9 := + | +-FunctionCall(ZetaSQL:coalesce(repeated(2) INT32) -> INT32) + | +-ColumnRef(type=INT32, column=$join_left.int32_val1#7) + | +-ColumnRef(type=INT32, column=$join_right.int32_val1#8) + +-input_scan= + +-JoinScan + +-column_list=[TestExtraValueTable.value#1, $join_left.int32_val1#7, TestExtraValueTable.value#4, $join_right.int32_val1#8] + +-join_type=FULL + +-left_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1, $join_left.int32_val1#7] + | +-expr_list= + | | +-int32_val1#7 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="t1") + +-right_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#4, $join_right.int32_val1#8] + | +-expr_list= + | | +-int32_val1#8 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#4) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0], alias="t2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$join_left.int32_val1#7) + | +-ColumnRef(type=INT32, column=$join_right.int32_val1#8) + +-has_using=TRUE +== + +# JOIN USING with a scalar value table. +# We don't need to realias here because there's only one merged column, +# no other collisions. +from Int32ValueTable +|> join Int32ValueTable using (Int32ValueTable) +|> where Int32ValueTable = 1 +-- +QueryStmt ++-output_column_list= +| +-Int32ValueTable.value#1 AS Int32ValueTable [INT32] ++-query= + +-FilterScan + +-column_list=Int32ValueTable.[value#1, value#2] + +-input_scan= + | +-JoinScan + | +-column_list=Int32ValueTable.[value#1, value#2] + | +-left_scan= + | | +-TableScan(column_list=[Int32ValueTable.value#1], table=Int32ValueTable, column_index_list=[0]) + | +-right_scan= + | | +-TableScan(column_list=[Int32ValueTable.value#2], table=Int32ValueTable, column_index_list=[0]) + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | | +-ColumnRef(type=INT32, column=Int32ValueTable.value#1) + | | +-ColumnRef(type=INT32, column=Int32ValueTable.value#2) + | +-has_using=TRUE + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-ColumnRef(type=INT32, column=Int32ValueTable.value#1) + +-Literal(type=INT32, value=1) +== + +# JOIN USING between UNNESTS (value tables) with int32/uint32 supertyping. +# Output column from USING is the (original) lhs input column. +from unnest([cast(1 as int32)]) val +|> join unnest([cast(2 as uint32)]) val using (val) +|> select val, * +-- +QueryStmt ++-output_column_list= +| +-$array.val#1 AS val [INT32] +| +-$array.val#1 AS val [INT32] ++-query= + +-ProjectScan + +-column_list=$array.[val#1, val#1] + +-input_scan= + +-ArrayScan + +-column_list=$array.[val#1, val#2] + +-input_scan= + | +-ArrayScan + | +-column_list=[$array.val#1] + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=[1], has_explicit_type=TRUE) + | +-element_column_list=[$array.val#1] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[2], has_explicit_type=TRUE) + +-element_column_list=[$array.val#2] + +-join_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, UINT64) -> BOOL) + +-Cast(INT32 -> INT64) + | +-ColumnRef(type=INT32, column=$array.val#1) + +-Cast(UINT32 -> UINT64) + +-ColumnRef(type=UINT32, column=$array.val#2) +== + +# Parenthesized join on the rhs. Range variables are visible. +from KeyValue kv1 +|> join (KeyValue kv2 join KeyValue kv3 using (key)) using (key) +|> select key, kv1.key, kv2.key, kv3.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#3 AS key [INT64] +| +-KeyValue.Key#5 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Key#1, Key#3, Key#5] + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Key#3, Key#5] + +-left_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0], alias="kv1") + +-right_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#3, Key#5] + | +-left_scan= + | | +-TableScan(column_list=[KeyValue.Key#3], table=KeyValue, column_index_list=[0], alias="kv2") + | +-right_scan= + | | +-TableScan(column_list=[KeyValue.Key#5], table=KeyValue, column_index_list=[0], alias="kv3") + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) + | +-has_using=TRUE + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +== + +# But range variables would not be visible if we gave a name to the +# parenthesized join. We don't let this parse though. +from KeyValue k1 +|> join (KeyValue kv2 join KeyValue kv3 using (key)) AS j using (key) +|> select key, kv1.key, kv2.key, kv3.key +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 2:54] +|> join (KeyValue kv2 join KeyValue kv3 using (key)) AS j using (key) + ^ +== + +# Multiple layers of nested parenthesized joins. +from KeyValue kv1 +|> join (KeyValue kv2 join + (KeyValue kv3 join KeyValue kv4 using (key)) + on kv2.key=kv4.key) + on kv1.key = kv3.key +|> select kv1.key, kv2.key, kv3.key, kv4.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#3 AS key [INT64] +| +-KeyValue.Key#5 AS key [INT64] +| +-KeyValue.Key#7 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Key#3, Key#5, Key#7] + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Key#3, Key#5, Key#7] + +-left_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0], alias="kv1") + +-right_scan= + | +-JoinScan + | +-column_list=KeyValue.[Key#3, Key#5, Key#7] + | +-left_scan= + | | +-TableScan(column_list=[KeyValue.Key#3], table=KeyValue, column_index_list=[0], alias="kv2") + | +-right_scan= + | | +-JoinScan + | | +-column_list=KeyValue.[Key#5, Key#7] + | | +-left_scan= + | | | +-TableScan(column_list=[KeyValue.Key#5], table=KeyValue, column_index_list=[0], alias="kv3") + | | +-right_scan= + | | | +-TableScan(column_list=[KeyValue.Key#7], table=KeyValue, column_index_list=[0], alias="kv4") + | | +-join_expr= + | | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#7) + | | +-has_using=TRUE + | +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + | +-ColumnRef(type=INT64, column=KeyValue.Key#7) + +-join_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=INT64, column=KeyValue.Key#5) +== + +# Alias from input not visible inside parenthesized join. +from KeyValue k1 +|> cross join (KeyValue k2 JOIN KeyValue k3 ON k2.key=k1.key) +-- +ERROR: Unrecognized name: k1 [at 2:55] +|> cross join (KeyValue k2 JOIN KeyValue k3 ON k2.key=k1.key) + ^ +== + +# Join two arrays coming from correlated references to an outer query +# using UNNEST. +# We get an ArrayScan of an ArrayScan, rather than a join of two ArrayScans. +# Either plan would work here because the arrays are uncorrelated to each +# other, but the resolver always builds joins to arrays this way. +# (Copied from array_join.test, and pipefied.) +from TestTable t +|> extend ARRAY( + from UNNEST(t.KitchenSink.repeated_date) d1 + |> join UNNEST(t.KitchenSink.repeated_date) d2 on d1=d2 + |> select d1) +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-$pipe_extend.$col1#6 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $pipe_extend.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=ARRAY + | +-parameter_list= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-subquery= + | +-ProjectScan + | +-column_list=[$array.d1#4] + | +-input_scan= + | +-ArrayScan + | +-column_list=$array.[d1#4, d2#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[$array.d1#4] + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3, is_correlated=TRUE) + | | | +-field_descriptor=repeated_date + | | | +-default_value=[] + | | | +-format=DATE + | | +-element_column_list=[$array.d1#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3, is_correlated=TRUE) + | | +-field_descriptor=repeated_date + | | +-default_value=[] + | | +-format=DATE + | +-element_column_list=[$array.d2#5] + | +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(DATE, DATE) -> BOOL) + | +-ColumnRef(type=DATE, column=$array.d1#4) + | +-ColumnRef(type=DATE, column=$array.d2#5) + +-input_scan= + +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2], alias="t") +== + +# TVF in a join. +from KeyValue k +|> join tvf_one_relation_arg_with_fixed_output(table TestTable) + on key = length(column_bytes) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-tvf_one_relation_arg_with_fixed_output.column_bool#6 AS column_bool [BOOL] +| +-tvf_one_relation_arg_with_fixed_output.column_bytes#7 AS column_bytes [BYTES] ++-query= + +-JoinScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, tvf_one_relation_arg_with_fixed_output.column_bool#6, tvf_one_relation_arg_with_fixed_output.column_bytes#7] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="k") + +-right_scan= + | +-TVFScan + | +-column_list=tvf_one_relation_arg_with_fixed_output.[column_bool#6, column_bytes#7] + | +-tvf=tvf_one_relation_arg_with_fixed_output((ANY TABLE) -> TABLE) + | +-signature=(TABLE, KitchenSink PROTO>) -> TABLE + | +-argument_list= + | | +-FunctionArgument + | | +-scan= + | | | +-TableScan(column_list=TestTable.[key#3, TestEnum#4, KitchenSink#5], table=TestTable, column_index_list=[0, 1, 2]) + | | +-argument_column_list=TestTable.[key#3, TestEnum#4, KitchenSink#5] + | +-column_index_list=[0, 1] + +-join_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-FunctionCall(ZetaSQL:length(BYTES) -> INT64) + +-ColumnRef(type=BYTES, column=tvf_one_relation_arg_with_fixed_output.column_bytes#7) +== + +# The TVF attempts to use the table from the lhs input as its input. +from KeyValue k +|> join tvf_one_relation_arg_with_fixed_output(table k) + on key = length(column_bytes) +-- +ERROR: Table not found: k [at 2:54] +|> join tvf_one_relation_arg_with_fixed_output(table k) + ^ +== + +# The TVF tries to pass correlated references from lhs input as its input args. +from KeyValue k +|> cross join tvf_one_relation_arg_one_int64_arg((select value), key) +-- +ERROR: Unrecognized name: value [at 2:58] +|> cross join tvf_one_relation_arg_one_int64_arg((select value), key) + ^ +== + +# For comparsion with previous, the same failure with normal syntax. +select * +from KeyValue k + cross join tvf_one_relation_arg_one_int64_arg((select value), key) +-- +ERROR: Unrecognized name: value [at 3:59] + cross join tvf_one_relation_arg_one_int64_arg((select value), key) + ^ +== + +from KeyValue k +|> cross join tvf_one_relation_arg_one_int64_arg(table k, key) +-- +ERROR: Table not found: k [at 2:56] +|> cross join tvf_one_relation_arg_one_int64_arg(table k, key) + ^ +== + +# TABLESAMPLE is allowed on tables in join. +from KeyValue k +|> join KeyValue k2 TABLESAMPLE bernoulli(5 percent) using (key) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="k") + +-right_scan= + | +-SampleScan + | +-column_list=KeyValue.[Key#3, Value#4] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="k2") + | +-method="bernoulli" + | +-size= + | | +-Literal(type=INT64, value=5) + | +-unit=PERCENT + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +== + +# TABLESAMPLE is allowed on subqueries in join. +from KeyValue k +|> join (select 5 key) TABLESAMPLE bernoulli(5 percent) using (key) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $subquery1.key#3] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="k") + +-right_scan= + | +-SampleScan + | +-column_list=[$subquery1.key#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.key#3] + | | +-expr_list= + | | | +-key#3 := Literal(type=INT64, value=5) + | | +-input_scan= + | | +-SingleRowScan + | +-method="bernoulli" + | +-size= + | | +-Literal(type=INT64, value=5) + | +-unit=PERCENT + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=$subquery1.key#3) + +-has_using=TRUE +== + +# TABLESAMPLE is allowed on TVFs in join. +from KeyValue k +|> cross join tvf_no_args() TABLESAMPLE bernoulli(5 percent)-- +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-tvf_no_args.column_bool#3 AS column_bool [BOOL] +| +-tvf_no_args.column_bytes#4 AS column_bytes [BYTES] ++-query= + +-JoinScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, tvf_no_args.column_bool#3, tvf_no_args.column_bytes#4] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="k") + +-right_scan= + +-SampleScan + +-column_list=tvf_no_args.[column_bool#3, column_bytes#4] + +-input_scan= + | +-TVFScan(column_list=tvf_no_args.[column_bool#3, column_bytes#4], tvf=tvf_no_args(() -> TABLE), signature=() -> TABLE, column_index_list=[0, 1]) + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=5) + +-unit=PERCENT +== + +# TABLESAMPLE is allowed on parenthesized joins in join. +from KeyValue k +|> join (KeyValue k2 join KeyValue k3 using (key)) + TABLESAMPLE bernoulli(5 percent) using (key) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] +| +-KeyValue.Value#6 AS Value [STRING] ++-query= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4, Key#5, Value#6] + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="k") + +-right_scan= + | +-SampleScan + | +-column_list=KeyValue.[Key#3, Value#4, Key#5, Value#6] + | +-input_scan= + | | +-JoinScan + | | +-column_list=KeyValue.[Key#3, Value#4, Key#5, Value#6] + | | +-left_scan= + | | | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="k2") + | | +-right_scan= + | | | +-TableScan(column_list=KeyValue.[Key#5, Value#6], table=KeyValue, column_index_list=[0, 1], alias="k3") + | | +-join_expr= + | | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) + | | +-has_using=TRUE + | +-method="bernoulli" + | +-size= + | | +-Literal(type=INT64, value=5) + | +-unit=PERCENT + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE diff --git a/zetasql/analyzer/testdata/pipe_order_by.test b/zetasql/analyzer/testdata/pipe_order_by.test new file mode 100644 index 000000000..54f7f2d86 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_order_by.test @@ -0,0 +1,427 @@ +[default language_features=PIPES,PIPE_STATIC_DESCRIBE,ANALYTIC_FUNCTIONS] +select 1 x, 2 y +|> ORDER BY x, y DESC, x*2 +|> ORDER @{hint=1} BY x +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] ++-query= + +-OrderByScan + +-column_list=$query.[x#1, y#2] + +-hint_list= + | +-hint := Literal(type=INT64, value=1) + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=$query.[x#1, y#2] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$query.x#1, $query.y#2, $orderby.$orderbycol3#3] + | | +-expr_list= + | | | +-$orderbycol3#3 := + | | | +-FunctionCall(ZetaSQL:$multiply(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$query.x#1) + | | | +-Literal(type=INT64, value=2) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | | +-parse_location=28-29 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$query.x#1) + | +-OrderByItem + | | +-parse_location=31-37 + | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$query.y#2) + | | +-is_descending=TRUE + | +-OrderByItem + | +-parse_location=39-42 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol3#3) + +-order_by_item_list= + +-OrderByItem + +-parse_location=65-66 + +-column_ref= + +-ColumnRef(type=INT64, column=$query.x#1) +== + +[language_features=PIPES,V_1_1_ORDER_BY_COLLATE,V_1_3_NULLS_FIRST_LAST_IN_ORDER_BY] +select "abc" x, 2 y +|> ORDER BY x COLLATE "abc" +|> ORDER BY y DESC NULLS LAST +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [STRING] +| +-$query.y#2 AS y [INT64] ++-query= + +-OrderByScan + +-column_list=$query.[x#1, y#2] + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=$query.[x#1, y#2] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=STRING, value="abc") + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=32-47 + | +-column_ref= + | | +-ColumnRef(type=STRING, column=$query.x#1) + | +-collation_name= + | +-Literal(type=STRING, value="abc") + +-order_by_item_list= + +-OrderByItem + +-parse_location=60-77 + +-column_ref= + | +-ColumnRef(type=INT64, column=$query.y#2) + +-is_descending=TRUE + +-null_order=NULLS_LAST +== + +# ORDER BY ordinals +select 1 x, 2 y +|> ORDER BY x, 2 +|> ORDER BY 2, 1 DESC +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] ++-query= + +-OrderByScan + +-column_list=$query.[x#1, y#2] + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=$query.[x#1, y#2] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | | +-parse_location=28-29 + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$query.x#1) + | +-OrderByItem + | +-parse_location=31-32 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.y#2) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=45-46 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.y#2) + +-OrderByItem + +-parse_location=48-54 + +-column_ref= + | +-ColumnRef(type=INT64, column=$query.x#1) + +-is_descending=TRUE +== + +select 1 x, 2 y +|> ORDER BY 3 +-- +ERROR: ORDER BY column number exceeds input table column count: 3 vs 2 [at 2:13] +|> ORDER BY 3 + ^ +== + +select 1 x, 2 y +|> ORDER BY -1 +-- +ERROR: ORDER BY column number item is out of range. Column numbers must be greater than or equal to one. Found : -1 [at 2:13] +|> ORDER BY -1 + ^ +== + +select 1 x, 2 y +|> ORDER BY sum(x) +-- +ERROR: Aggregate function SUM not allowed in pipe ORDER BY clause [at 2:13] +|> ORDER BY sum(x) + ^ +== + +[language_features=PIPES{{|,ANALYTIC_FUNCTIONS}}] +select 1 x, 2 y +|> ORDER BY sum(x) OVER () +-- +ALTERNATION GROUP: +-- +ERROR: Analytic functions not supported [at 2:13] +|> ORDER BY sum(x) OVER () + ^ +-- +ALTERNATION GROUP: ,ANALYTIC_FUNCTIONS +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.x#1, $query.y#2, $analytic.$analytic1#3] + +-is_ordered=TRUE + +-input_scan= + | +-AnalyticScan + | +-column_list=[$query.x#1, $query.y#2, $analytic.$analytic1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-analytic_function_list= + | +-$analytic1#3 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-order_by_item_list= + +-OrderByItem + +-parse_location=28-42 + +-column_ref= + +-ColumnRef(type=INT64, column=$analytic.$analytic1#3) +== + +# ORDER BY with a mix of expressions before and after the window function. +select 1 x, 2 y, 3 z +|> ORDER BY x, + x+1, + 1+sum(x) OVER (), + avg(y+1) OVER (order by x), + max(x) OVER (partition by x+1) +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.z#3 AS z [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#4, $analytic.$analytic2#5, $analytic.$analytic3#6] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#4, $analytic.$analytic2#5, $analytic.$analytic3#6, $orderby.$orderbycol2#8, $orderby.$orderbycol3#9] + | +-expr_list= + | | +-$orderbycol2#8 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$query.x#1) + | | | +-Literal(type=INT64, value=1) + | | +-$orderbycol3#9 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=INT64, column=$analytic.$analytic1#4) + | +-input_scan= + | +-AnalyticScan + | +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#4, $analytic.$analytic2#5, $analytic.$analytic3#6] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$query.x#1, $query.y#2, $query.z#3, $partitionby.$partitionbycol1#7] + | | +-expr_list= + | | | +-$partitionbycol1#7 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$query.x#1) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2, z#3] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | | +-z#3 := Literal(type=INT64, value=3) + | | +-input_scan= + | | +-SingleRowScan + | +-function_group_list= + | +-AnalyticFunctionGroup + | | +-analytic_function_list= + | | +-$analytic1#4 := + | | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.x#1) + | | +-window_frame= + | | +-WindowFrame(frame_unit=ROWS) + | | +-start_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | +-end_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + | +-AnalyticFunctionGroup + | | +-order_by= + | | | +-WindowOrdering + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$query.x#1) + | | +-analytic_function_list= + | | +-$analytic2#5 := + | | +-AnalyticFunctionCall(ZetaSQL:avg(INT64) -> DOUBLE) + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.y#2) + | | +-Literal(type=INT64, value=1) + | | +-window_frame= + | | +-WindowFrame(frame_unit=RANGE) + | | +-start_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | +-end_expr= + | | +-WindowFrameExpr(boundary_type=CURRENT ROW) + | +-AnalyticFunctionGroup + | +-partition_by= + | | +-WindowPartitioning + | | +-partition_by_list= + | | +-ColumnRef(type=INT64, column=$partitionby.$partitionbycol1#7) + | +-analytic_function_list= + | +-$analytic3#6 := + | +-AnalyticFunctionCall(ZetaSQL:max(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-order_by_item_list= + +-OrderByItem + | +-parse_location=33-34 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.x#1) + +-OrderByItem + | +-parse_location=48-51 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol2#8) + +-OrderByItem + | +-parse_location=65-81 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol3#9) + +-OrderByItem + | +-parse_location=95-121 + | +-column_ref= + | +-ColumnRef(type=DOUBLE, column=$analytic.$analytic2#5) + +-OrderByItem + +-parse_location=135-165 + +-column_ref= + +-ColumnRef(type=INT64, column=$analytic.$analytic3#6) +== + +# ORDER BY with a window function over a value table, preserving +# the value table. +from TestExtraValueTable +|> ORDER BY sum(int32_val1) OVER (order by int32_val2) +|> STATIC_DESCRIBE +|> WHERE Filename != "" +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $analytic.$analytic1#4] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $analytic.$analytic1#4] + | +-is_ordered=TRUE + | +-describe_text= + | | """ + | | NameList (is_value_table = true): + | | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | | NameScope: + | | Names: + | | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | | Range variables: + | | TestExtraValueTable -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 + | | """ + | +-input_scan= + | +-OrderByScan + | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $analytic.$analytic1#4] + | +-is_ordered=TRUE + | +-input_scan= + | | +-AnalyticScan + | | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $analytic.$analytic1#4] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $orderby.int32_val2#5] + | | | +-expr_list= + | | | | +-int32_val2#5 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val2 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1]) + | | +-function_group_list= + | | +-AnalyticFunctionGroup + | | +-order_by= + | | | +-WindowOrdering + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-column_ref= + | | | +-ColumnRef(type=INT32, column=$orderby.int32_val2#5) + | | +-analytic_function_list= + | | +-$analytic1#4 := + | | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-Cast(INT32 -> INT64) + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | | +-window_frame= + | | +-WindowFrame(frame_unit=RANGE) + | | +-start_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | +-end_expr= + | | +-WindowFrameExpr(boundary_type=CURRENT ROW) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=37-79 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$analytic.$analytic1#4) + +-filter_expr= + +-FunctionCall(ZetaSQL:$not_equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="") +== + +from KeyValue +|> ORDER BY count(*) OVER w +-- +ERROR: Cannot reference a named window in pipe ORDER BY clause [at 2:27] +|> ORDER BY count(*) OVER w + ^ diff --git a/zetasql/analyzer/testdata/pipe_order_preservation.test b/zetasql/analyzer/testdata/pipe_order_preservation.test new file mode 100644 index 000000000..1de27f164 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_order_preservation.test @@ -0,0 +1,591 @@ +[default language_features=PIPES,ANALYTIC_FUNCTIONS,PIPE_STATIC_DESCRIBE] +# Result scan with ORDER BY has is_ordered. +select 1 x +|> ORDER BY x +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.x#1] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-order_by_item_list= + +-OrderByItem + +-parse_location=67-68 + +-column_ref= + +-ColumnRef(type=INT64, column=$query.x#1) +== + +# This shows order preservation through LIMIT. +select 1 x +|> ORDER BY x +|> LIMIT 10 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-LimitOffsetScan + +-column_list=[$query.x#1] + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=[$query.x#1] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$query.x#1] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=23-24 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.x#1) + +-limit= + +-Literal(type=INT64, value=10) +== + +# This shows order preservation through LIMIT when the +# ordering came from an initial query, not a pipe ORDER BY. +select * from TestTable +order by key +|> LIMIT 10 +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] ++-query= + +-LimitOffsetScan + +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + | +-is_ordered=TRUE + | +-input_scan= + | | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=33-36 + | +-column_ref= + | +-ColumnRef(type=INT32, column=TestTable.key#1) + +-limit= + +-Literal(type=INT64, value=10) +== + +# This shows other operators that do preserve order. +# These all have is_ordered=true on the final Scan. +from KeyValue +|> ORDER BY key +|> {{SELECT key+1|EXTEND key+1|SET key=key+1|DROP key|STATIC_DESCRIBE}} +-- +ALTERNATION GROUP: SELECT key+1 +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.$col1#3 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.$col1#3] + +-is_ordered=TRUE + +-expr_list= + | +-$col1#3 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-OrderByScan + +-column_list=[KeyValue.Key#1] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=26-29 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: EXTEND key+1 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_extend.$col1#3 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.$col1#3] + +-is_ordered=TRUE + +-expr_list= + | +-$col1#3 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-OrderByScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=26-29 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: SET key=key+1 +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.key#3 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.key#3] + +-is_ordered=TRUE + +-expr_list= + | +-key#3 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=1) + +-input_scan= + +-OrderByScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=26-29 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: DROP key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-OrderByScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=26-29 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-- +ALTERNATION GROUP: STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | """ + +-input_scan= + +-OrderByScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=26-29 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +== + +# This shows other operators that could potentially preserve order, and +# that none of them currently do. +# None of these have is_ordered on the final Scan. +select 1 x +|> ORDER BY x +|> {{WHERE x=1|WINDOW count(*) OVER ()}} +-- +ALTERNATION GROUP: WHERE x=1 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$query.x#1] + +-input_scan= + | +-OrderByScan + | +-column_list=[$query.x#1] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$query.x#1] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=23-24 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.x#1) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$query.x#1) + +-Literal(type=INT64, value=1) +-- +ALTERNATION GROUP: WINDOW count(*) OVER () +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$analytic.$analytic1#3 AS `$col1` [INT64] ++-query= + +-AnalyticScan + +-column_list=[$query.x#1, $analytic.$analytic1#3] + +-input_scan= + | +-OrderByScan + | +-column_list=[$query.x#1] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$query.x#1] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=23-24 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.x#1) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#3 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Pipe AS preserves order of the input table. +select 1 x +|> ORDER BY x +|> AS t; +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-OrderByScan + +-column_list=[$query.x#1] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-order_by_item_list= + +-OrderByItem + +-parse_location=23-24 + +-column_ref= + +-ColumnRef(type=INT64, column=$query.x#1) +== + +# Pipe SELECT and EXTEND, if calling a window function, do not preserve order. +# is_ordered is present until that window function but not after. +FROM KeyValue +|> ORDER BY 1 +|> SELECT key +|> EXTEND key+1 +|> {{SELECT|EXTEND}} count(*) OVER () c +-- +ALTERNATION GROUP: SELECT +-- +QueryStmt ++-output_column_list= +| +-$analytic.c#5 AS c [INT64] ++-query= + +-ProjectScan + +-column_list=[$analytic.c#5] + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, $pipe_extend.$col1#3, $analytic.c#5] + +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, $pipe_extend.$col1#3] + | +-is_ordered=TRUE + | +-expr_list= + | | +-$col1#3 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1] + | +-is_ordered=TRUE + | +-input_scan= + | +-OrderByScan + | +-column_list=[KeyValue.Key#1] + | +-is_ordered=TRUE + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=26-27 + | +-column_ref= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-c#5 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +-- +ALTERNATION GROUP: EXTEND +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-$pipe_extend.$col1#3 AS `$col1` [INT64] +| +-$analytic.c#5 AS c [INT64] ++-query= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, $pipe_extend.$col1#3, $analytic.c#5] + +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, $pipe_extend.$col1#3] + | +-is_ordered=TRUE + | +-expr_list= + | | +-$col1#3 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1] + | +-is_ordered=TRUE + | +-input_scan= + | +-OrderByScan + | +-column_list=[KeyValue.Key#1] + | +-is_ordered=TRUE + | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=26-27 + | +-column_ref= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-c#5 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Pipe SET, if calling a window function, does not preserve order. +# is_ordered is present until that window function but not after. +FROM KeyValue +|> ORDER BY 1 +|> SET value = value || 'x' +|> SET key = count(*) OVER () +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.key#5 AS Key [INT64] +| +-$pipe_set.value#3 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.value#3, $analytic.key#4, $pipe_set.key#5] + +-expr_list= + | +-key#5 := ColumnRef(type=INT64, column=$analytic.key#4) + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.value#3, $analytic.key#4] + +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.value#3] + | +-is_ordered=TRUE + | +-expr_list= + | | +-value#3 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-Literal(type=STRING, value="x") + | +-input_scan= + | +-OrderByScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-is_ordered=TRUE + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=26-27 + | +-column_ref= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-key#4 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Table subqueries never preserve order, even with pipes. +# So we don't have is_ordered on the outer Scan. +from (from KeyValue |> order by Key) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-OrderByScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-order_by_item_list= + +-OrderByItem + +-parse_location=32-35 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +== + +# ARRAY subqueries do preserve order. +# We have is_ordered on the Scan input to SubqueryExpr. +select 1 +|> extend ARRAY(from KeyValue |> select Key |> order by key) +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [INT64] +| +-$pipe_extend.$col1#4 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1, $pipe_extend.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=ARRAY + | +-subquery= + | +-OrderByScan + | +-column_list=[KeyValue.Key#2] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#2] + | | +-input_scan= + | | +-TableScan(column_list=[KeyValue.Key#2], table=KeyValue, column_index_list=[0]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=65-68 + | +-column_ref= + | +-ColumnRef(type=INT64, column=KeyValue.Key#2) + +-input_scan= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +# This ARRAY subquery is preserving order when it has SELECT AS STRUCT after +# ORDER BY. +select 1 +|> extend ARRAY(from KeyValue |> order by key |> select AS STRUCT *) +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [INT64] +| +-$pipe_extend.$col1#5 AS `$col1` [ARRAY>] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1, $pipe_extend.$col1#5] + +-expr_list= + | +-$col1#5 := + | +-SubqueryExpr + | +-type=ARRAY> + | +-subquery_type=ARRAY + | +-subquery= + | +-ProjectScan + | +-column_list=[$make_struct.$struct#4] + | +-is_ordered=TRUE + | +-expr_list= + | | +-$struct#4 := + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#2) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#3) + | +-input_scan= + | +-ProjectScan + | +-column_list=KeyValue.[Key#2, Value#3] + | +-is_ordered=TRUE + | +-input_scan= + | +-OrderByScan + | +-column_list=KeyValue.[Key#2, Value#3] + | +-is_ordered=TRUE + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#2, Value#3], table=KeyValue, column_index_list=[0, 1]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=51-54 + | +-column_ref= + | +-ColumnRef(type=INT64, column=KeyValue.Key#2) + +-input_scan= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan diff --git a/zetasql/analyzer/testdata/pipe_parenthesized_query_alias.test b/zetasql/analyzer/testdata/pipe_parenthesized_query_alias.test new file mode 100644 index 000000000..4ab2edf17 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_parenthesized_query_alias.test @@ -0,0 +1,602 @@ +[default language_features=PIPES,PIPE_STATIC_DESCRIBE] + +(SELECT * FROM KeyValue) AS kv +|> STATIC_DESCRIBE +|> WHERE key is null +|> WHERE kv.value is null +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-StaticDescribeScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-describe_text= + | | | """ + | | | NameList: + | | | Key INT64 KeyValue.Key#1 + | | | Value STRING KeyValue.Value#2 + | | | NameScope: + | | | Names: + | | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | | Value -> STRING (KeyValue.Value#2) (implicit) + | | | Range variables: + | | | kv -> RANGE_VARIABLE + | | | """ + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$is_null(INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + +-ColumnRef(type=STRING, column=KeyValue.Value#2) +== + +(FROM KeyValue) AS kv +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | kv -> RANGE_VARIABLE + | """ + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +(select 1 x + union all + select 2 y) AS t +|> STATIC_DESCRIBE +|> where x = 5 and t.x = 5 +-- +QueryStmt ++-output_column_list= +| +-$union_all.x#3 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$union_all.x#3] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[$union_all.x#3] + | +-describe_text= + | | """ + | | NameList: + | | x INT64 $union_all.x#3 + | | NameScope: + | | Names: + | | x -> INT64 ($union_all.x#3) + | | Range variables: + | | t -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.x#3] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$union_all1.x#1] + | | | +-expr_list= + | | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-output_column_list=[$union_all1.x#1] + | +-SetOperationItem + | +-scan= + | | +-ProjectScan + | | +-column_list=[$union_all2.y#2] + | | +-expr_list= + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-output_column_list=[$union_all2.y#2] + +-filter_expr= + +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$union_all.x#3) + | +-Literal(type=INT64, value=5) + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$union_all.x#3) + +-Literal(type=INT64, value=5) +== + +((select * + from KeyValue + ) + order by key + limit 10) AS qq +|> static_describe +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | qq -> RANGE_VARIABLE + | """ + +-input_scan= + +-LimitOffsetScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-input_scan= + | +-OrderByScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=KeyValue.[Key#1, Value#2] + | | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=40-43 + | +-column_ref= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-limit= + +-Literal(type=INT64, value=10) +== + +# Value table +(FROM TestExtraValueTable) AS vt +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1] + +-describe_text= + | """ + | NameList (is_value_table = true): + | vt zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | vt -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +# Table with psuedo-columns. +(FROM EnumTable) AS t +|> STATIC_DESCRIBE +|> WHERE RowId is NULL +|> SELECT * +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] ++-query= + +-ProjectScan + +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3] + +-input_scan= + +-FilterScan + +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, RowId#5] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, RowId#5] + | +-describe_text= + | | """ + | | NameList: + | | key INT32 EnumTable.key#1 + | | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | | NameScope: + | | Names: + | | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (EnumTable.AnotherTestEnum#3) (implicit) + | | Filename -> STRING (EnumTable.Filename#4) (implicit) (pseudo-column) + | | RowId -> BYTES (EnumTable.RowId#5) (implicit) (pseudo-column) + | | TestEnum -> zetasql_test__.TestEnum (EnumTable.TestEnum#2) (implicit) + | | key -> INT32 (EnumTable.key#1) (implicit) + | | Range variables: + | | t -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 4]) + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(BYTES) -> BOOL) + +-ColumnRef(type=BYTES, column=EnumTable.RowId#5) +== + +# Multiple layers. The outer layers don't affect the aliases. +(((SELECT 1 x, 2 y) AS t)) +|> STATIC_DESCRIBE +|> SELECT x, t.y +-- +QueryStmt ++-output_column_list= +| +-t.x#1 AS x [INT64] +| +-t.y#2 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=t.[x#1, y#2] + +-input_scan= + +-StaticDescribeScan + +-column_list=t.[x#1, y#2] + +-describe_text= + | """ + | NameList: + | x INT64 t.x#1 + | y INT64 t.y#2 + | NameScope: + | Names: + | x -> INT64 (t.x#1) + | y -> INT64 (t.y#2) + | Range variables: + | t -> RANGE_VARIABLE + | """ + +-input_scan= + +-ProjectScan + +-column_list=t.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +# Multiple layers. The outer layers hide the inner layers. +(((SELECT 1 x, 2 y) AS t1) AS t2) AS t3 +|> STATIC_DESCRIBE +|> SELECT x, {{t3|t2|t1}}.y +-- +ALTERNATION GROUP: t3 +-- +QueryStmt ++-output_column_list= +| +-t1.x#1 AS x [INT64] +| +-t1.y#2 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=t1.[x#1, y#2] + +-input_scan= + +-StaticDescribeScan + +-column_list=t1.[x#1, y#2] + +-describe_text= + | """ + | NameList: + | x INT64 t1.x#1 + | y INT64 t1.y#2 + | NameScope: + | Names: + | x -> INT64 (t1.x#1) + | y -> INT64 (t1.y#2) + | Range variables: + | t3 -> RANGE_VARIABLE + | """ + +-input_scan= + +-ProjectScan + +-column_list=t1.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: t2 +-- +ERROR: Unrecognized name: t2 [at 3:14] +|> SELECT x, t2.y + ^ +-- +ALTERNATION GROUP: t1 +-- +ERROR: Unrecognized name: t1 [at 3:14] +|> SELECT x, t1.y + ^ +== + +# Existing table aliases are removed. +(FROM KeyValue kv1 JOIN KeyValue kv2 USING (key)) AS t +|> STATIC_DESCRIBE +|> SELECT key, {{t.key|t.value|value|kv1.value|kv1.key}} +-- +ALTERNATION GROUP: t.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=KeyValue.[Key#1, Key#1] + +-input_scan= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Key#3] + +-describe_text= + | """ + | NameList: + | key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | Value STRING KeyValue.Value#4 + | NameScope: + | Names: + | Value -> ambiguous + | key -> INT64 (KeyValue.Key#1) + | Range variables: + | t -> RANGE_VARIABLE + | """ + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Key#3] + +-left_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0], alias="kv1") + +-right_scan= + | +-TableScan(column_list=[KeyValue.Key#3], table=KeyValue, column_index_list=[0], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +-- +ALTERNATION GROUP: t.value +-- +ERROR: Name value is ambiguous inside t [at 3:18] +|> SELECT key, t.value + ^ +-- +ALTERNATION GROUP: value +-- +ERROR: Column name value is ambiguous [at 3:16] +|> SELECT key, value + ^ +-- +ALTERNATION GROUP: kv1.value +-- +ERROR: Unrecognized name: kv1 [at 3:16] +|> SELECT key, kv1.value + ^ +-- +ALTERNATION GROUP: kv1.key +-- +ERROR: Unrecognized name: kv1 [at 3:16] +|> SELECT key, kv1.key + ^ +== + +# The user-requested pattern where a pipe join can be written, +# and both input queries can be aliased with AS. +(SELECT 1 x, 2 y) AS t1 +|> JOIN + (SELECT 1 x, 3 z) AS t2 + USING (x) +|> STATIC_DESCRIBE +|> SELECT x, t1.x, t2.z +-- +QueryStmt ++-output_column_list= +| +-t1.x#1 AS x [INT64] +| +-t1.x#1 AS x [INT64] +| +-t2.z#4 AS z [INT64] ++-query= + +-ProjectScan + +-column_list=[t1.x#1, t1.x#1, t2.z#4] + +-input_scan= + +-StaticDescribeScan + +-column_list=[t1.x#1, t1.y#2, t2.x#3, t2.z#4] + +-describe_text= + | """ + | NameList: + | x INT64 t1.x#1 + | y INT64 t1.y#2 + | z INT64 t2.z#4 + | NameScope: + | Names: + | x -> INT64 (t1.x#1) + | y -> INT64 (t1.y#2) + | z -> INT64 (t2.z#4) + | Range variables: + | t1 -> RANGE_VARIABLE + | t2 -> RANGE_VARIABLE + | """ + +-input_scan= + +-JoinScan + +-column_list=[t1.x#1, t1.y#2, t2.x#3, t2.z#4] + +-left_scan= + | +-ProjectScan + | +-column_list=t1.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-right_scan= + | +-ProjectScan + | +-column_list=t2.[x#3, z#4] + | +-expr_list= + | | +-x#3 := Literal(type=INT64, value=1) + | | +-z#4 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=t1.x#1) + | +-ColumnRef(type=INT64, column=t2.x#3) + +-has_using=TRUE +== + +# Alias on a standard syntax query expression before LIMIT. +# The alias survives. +(select 1 x, 2 y) AS q +LIMIT 10 +|> STATIC_DESCRIBE +|> SELECT x, q.y +-- +QueryStmt ++-output_column_list= +| +-q.x#1 AS x [INT64] +| +-q.y#2 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=q.[x#1, y#2] + +-input_scan= + +-StaticDescribeScan + +-column_list=q.[x#1, y#2] + +-describe_text= + | """ + | NameList: + | x INT64 q.x#1 + | y INT64 q.y#2 + | NameScope: + | Names: + | x -> INT64 (q.x#1) + | y -> INT64 (q.y#2) + | Range variables: + | q -> RANGE_VARIABLE + | """ + +-input_scan= + +-LimitOffsetScan + +-column_list=q.[x#1, y#2] + +-input_scan= + | +-ProjectScan + | +-column_list=q.[x#1, y#2] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-limit= + +-Literal(type=INT64, value=10) +== + +# Aliases on standard syntax query expression with UNION. +# The input table aliases do not survive. +(select 1 x, 2 y) AS q1 +UNION ALL +(select 3 z, 4 zz) AS q2 +|> STATIC_DESCRIBE +|> SELECT x, {{y|q1.y|q2.y}} +-- +ALTERNATION GROUP: y +-- +QueryStmt ++-output_column_list= +| +-$union_all.x#5 AS x [INT64] +| +-$union_all.y#6 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=$union_all.[x#5, y#6] + +-input_scan= + +-StaticDescribeScan + +-column_list=$union_all.[x#5, y#6] + +-describe_text= + | """ + | NameList: + | x INT64 $union_all.x#5 + | y INT64 $union_all.y#6 + | NameScope: + | Names: + | x -> INT64 ($union_all.x#5) + | y -> INT64 ($union_all.y#6) + | """ + +-input_scan= + +-SetOperationScan + +-column_list=$union_all.[x#5, y#6] + +-op_type=UNION_ALL + +-input_item_list= + +-SetOperationItem + | +-scan= + | | +-ProjectScan + | | +-column_list=q1.[x#1, y#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-output_column_list=q1.[x#1, y#2] + +-SetOperationItem + +-scan= + | +-ProjectScan + | +-column_list=q2.[z#3, zz#4] + | +-expr_list= + | | +-z#3 := Literal(type=INT64, value=3) + | | +-zz#4 := Literal(type=INT64, value=4) + | +-input_scan= + | +-SingleRowScan + +-output_column_list=q2.[z#3, zz#4] +-- +ALTERNATION GROUP: q1.y +-- +ERROR: Unrecognized name: q1 [at 5:14] +|> SELECT x, q1.y + ^ +-- +ALTERNATION GROUP: q2.y +-- +ERROR: Unrecognized name: q2 [at 5:14] +|> SELECT x, q2.y + ^ +== + +# Test that ExtractTableNames understands tt is a range variable. +(FROM TestTable) AS tt +|> JOIN tt.KitchenSink.repeated_int32_val +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-$array.repeated_int32_val#4 AS repeated_int32_val [INT32] ++-query= + +-ArrayScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, $array.repeated_int32_val#4] + +-input_scan= + | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + +-array_expr_list= + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | +-field_descriptor=repeated_int32_val + | +-default_value=[] + +-element_column_list=[$array.repeated_int32_val#4] diff --git a/zetasql/analyzer/testdata/pipe_pivot.test b/zetasql/analyzer/testdata/pipe_pivot.test new file mode 100644 index 000000000..6bfef3431 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_pivot.test @@ -0,0 +1,237 @@ +# Tests for pipe PIVOT include a subset of tests from pivot.test. +# +# The resolver code is all shared so this doesn't duplicate exhaustive tests +# for the full behavior. +# Since Resolved ASTs are the same, this also doesn't include rewriter output. +# See pivot.test for those details. +# +# Unlike standard PIVOT, here it is unnecessary to test all combinations with +# tables, subqueries, TVFs, UNNEST, etc, since pipe operators are orthogonal +# and independent of where the input table came from. +# +# When PIVOT is used on a single table FROM clause, the behavior is identical +# for the standard syntax suffix operator and the pipe operator. Several tests +# show this by using an alternation {{ | \|> }} that includes a pipe or not. +[default language_features=PIPES,PIPE_STATIC_DESCRIBE,V_1_3_PIVOT] + +# Show a pivot, either producing a range variable `pt` or not. +FROM KeyValue +|> PIVOT(COUNT(Value) FOR Key IN (0 AS zero, 1 AS one, 33)){{| AS pt}} +|> STATIC_DESCRIBE +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$pivot.zero#4 AS zero [INT64] +| +-$pivot.one#5 AS one [INT64] +| +-$pivot._33#6 AS _33 [INT64] ++-query= + +-StaticDescribeScan + +-column_list=$pivot.[zero#4, one#5, _33#6] + +-describe_text= + | """ + | NameList: + | zero INT64 $pivot.zero#4 + | one INT64 $pivot.one#5 + | _33 INT64 $pivot._33#6 + | NameScope: + | Names: + | _33 -> INT64 ($pivot._33#6) + | one -> INT64 ($pivot.one#5) + | zero -> INT64 ($pivot.zero#4) + | """ + +-input_scan= + +-PivotScan + +-column_list=$pivot.[zero#4, one#5, _33#6] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-pivot_expr_list= + | +-AggregateFunctionCall(ZetaSQL:count(STRING) -> INT64) + | +-parse_location=86-98 + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-for_expr= + | +-ColumnRef(parse_location=103-106, type=INT64, column=KeyValue.Key#1) + +-pivot_value_list= + | +-Literal(type=INT64, value=0) + | +-Literal(type=INT64, value=1) + | +-Literal(type=INT64, value=33, preserve_in_literal_remover=TRUE) + +-pivot_column_list= + +-PivotColumn(column=$pivot.zero#4, pivot_expr_index=0, pivot_value_index=0) + +-PivotColumn(column=$pivot.one#5, pivot_expr_index=0, pivot_value_index=1) + +-PivotColumn(column=$pivot._33#6, pivot_expr_index=0, pivot_value_index=2) +-- +ALTERNATION GROUP: AS pt +-- +QueryStmt ++-output_column_list= +| +-$pivot.zero#4 AS zero [INT64] +| +-$pivot.one#5 AS one [INT64] +| +-$pivot._33#6 AS _33 [INT64] ++-query= + +-StaticDescribeScan + +-column_list=$pivot.[zero#4, one#5, _33#6] + +-describe_text= + | """ + | NameList: + | zero INT64 $pivot.zero#4 + | one INT64 $pivot.one#5 + | _33 INT64 $pivot._33#6 + | NameScope: + | Names: + | _33 -> INT64 ($pivot._33#6) + | one -> INT64 ($pivot.one#5) + | zero -> INT64 ($pivot.zero#4) + | Range variables: + | pt -> RANGE_VARIABLE + | """ + +-input_scan= + +-PivotScan + +-column_list=$pivot.[zero#4, one#5, _33#6] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-pivot_expr_list= + | +-AggregateFunctionCall(ZetaSQL:count(STRING) -> INT64) + | +-parse_location=86-98 + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-for_expr= + | +-ColumnRef(parse_location=103-106, type=INT64, column=KeyValue.Key#1) + +-pivot_value_list= + | +-Literal(type=INT64, value=0) + | +-Literal(type=INT64, value=1) + | +-Literal(type=INT64, value=33, preserve_in_literal_remover=TRUE) + +-pivot_column_list= + +-PivotColumn(column=$pivot.zero#4, pivot_expr_index=0, pivot_value_index=0) + +-PivotColumn(column=$pivot.one#5, pivot_expr_index=0, pivot_value_index=1) + +-PivotColumn(column=$pivot._33#6, pivot_expr_index=0, pivot_value_index=2) +== + +# In more typical usage, there will be SELECTs or other operators before +# the PIVOT to set up columns, corresponding to a an input subquery in +# standard syntax. +FROM KeyValue +|> SELECT Key, Value +|> PIVOT(COUNT(Value) FOR Key IN (0 AS zero, 1 AS one)) AS pt +|> SELECT zero, pt.one +-- +QueryStmt ++-output_column_list= +| +-$pivot.zero#4 AS zero [INT64] +| +-$pivot.one#5 AS one [INT64] ++-query= + +-ProjectScan + +-column_list=$pivot.[zero#4, one#5] + +-input_scan= + +-PivotScan + +-column_list=$pivot.[zero#4, one#5] + +-input_scan= + | +-ProjectScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-pivot_expr_list= + | +-AggregateFunctionCall(ZetaSQL:count(STRING) -> INT64) + | +-parse_location=44-56 + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-for_expr= + | +-ColumnRef(parse_location=61-64, type=INT64, column=KeyValue.Key#1) + +-pivot_value_list= + | +-Literal(type=INT64, value=0) + | +-Literal(type=INT64, value=1) + +-pivot_column_list= + +-PivotColumn(column=$pivot.zero#4, pivot_expr_index=0, pivot_value_index=0) + +-PivotColumn(column=$pivot.one#5, pivot_expr_index=0, pivot_value_index=1) +== + +# PIVOT on a standard syntax query, feeding output into of a TVF. +# Also has an expression in the aggregate function input. +[language_features=PIPES,TABLE_VALUED_FUNCTIONS,V_1_3_PIVOT] +SELECT Key, Value +FROM KeyValue +GROUP BY Key, Value +|> PIVOT(COUNT(1+Key) FOR Value IN ('x' AS x, 'y' AS y)) +|> CALL tvf_one_relation_arg_output_schema_is_input_schema() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.x#8 AS x [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.y#9 AS y [INT64] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[x#8, y#9] + +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-PivotScan + | | +-column_list=$pivot.[x#6, y#7] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$groupby.[Key#3, Value#4] + | | | +-input_scan= + | | | +-AggregateScan + | | | +-column_list=$groupby.[Key#3, Value#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | | +-group_by_list= + | | | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-pivot_expr_list= + | | | +-AggregateFunctionCall(ZetaSQL:count(INT64) -> INT64) + | | | +-parse_location=61-73 + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=INT64, column=$groupby.Key#3) + | | +-for_expr= + | | | +-ColumnRef(parse_location=78-83, type=STRING, column=$groupby.Value#4) + | | +-pivot_value_list= + | | | +-Literal(type=STRING, value="x") + | | | +-Literal(type=STRING, value="y") + | | +-pivot_column_list= + | | +-PivotColumn(column=$pivot.x#6, pivot_expr_index=0, pivot_value_index=0) + | | +-PivotColumn(column=$pivot.y#7, pivot_expr_index=0, pivot_value_index=1) + | +-argument_column_list=$pivot.[x#6, y#7] + +-column_index_list=[0, 1] +== + +# PIVOT query with all columns pruned. +SELECT 1 w, 2 x, 3 y, 4 z +|> PIVOT(SUM(x) AS s1, SUM(x) AS s2 FOR y IN (0 AS zero, 1 AS one)) +|> SELECT 123 +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.$col1#13 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.$col1#13] + +-expr_list= + | +-$col1#13 := Literal(type=INT64, value=123) + +-input_scan= + +-PivotScan + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[w#1, x#2, y#3, z#4] + | +-expr_list= + | | +-w#1 := Literal(type=INT64, value=1) + | | +-x#2 := Literal(type=INT64, value=2) + | | +-y#3 := Literal(type=INT64, value=3) + | | +-z#4 := Literal(type=INT64, value=4) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + | +-w#7 := ColumnRef(type=INT64, column=$query.w#1) + | +-z#8 := ColumnRef(type=INT64, column=$query.z#4) + +-pivot_expr_list= + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-parse_location=35-47 + | | +-ColumnRef(type=INT64, column=$query.x#2) + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-parse_location=49-61 + | +-ColumnRef(type=INT64, column=$query.x#2) + +-for_expr= + | +-ColumnRef(parse_location=66-67, type=INT64, column=$query.y#3) + +-pivot_value_list= + +-Literal(type=INT64, value=0) + +-Literal(type=INT64, value=1) diff --git a/zetasql/analyzer/testdata/pipe_query.test b/zetasql/analyzer/testdata/pipe_query.test new file mode 100644 index 000000000..b0bc5d9e9 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_query.test @@ -0,0 +1,1167 @@ +select 1,2,3 +|> where true; +-- +ERROR: Pipe query syntax not supported [at 2:1] +|> where true; +^ +== + +[default language_features=PIPES,PIPE_STATIC_DESCRIBE] +select 1,2,3 +|> where true; +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [INT64] +| +-$query.$col2#2 AS `$col2` [INT64] +| +-$query.$col3#3 AS `$col3` [INT64] ++-query= + +-FilterScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[$col1#1, $col2#2, $col3#3] + | +-expr_list= + | | +-$col1#1 := Literal(type=INT64, value=1) + | | +-$col2#2 := Literal(type=INT64, value=2) + | | +-$col3#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +select 1 x, 2 y +|> where x=1 +|> where y=2 +|> where x=y; +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] ++-query= + +-FilterScan + +-column_list=$query.[x#1, y#2] + +-input_scan= + | +-FilterScan + | +-column_list=$query.[x#1, y#2] + | +-input_scan= + | | +-FilterScan + | | +-column_list=$query.[x#1, y#2] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$query.[x#1, y#2] + | | | +-expr_list= + | | | | +-x#1 := Literal(type=INT64, value=1) + | | | | +-y#2 := Literal(type=INT64, value=2) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=$query.x#1) + | | +-Literal(type=INT64, value=1) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-Literal(type=INT64, value=2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$query.x#1) + +-ColumnRef(type=INT64, column=$query.y#2) +== + +select 5 +from ( + select 1 + |> where true +) +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#2 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#2] + +-expr_list= + | +-$col1#2 := Literal(type=INT64, value=5) + +-input_scan= + +-FilterScan + +-column_list=[$subquery1.$col1#1] + +-input_scan= + | +-ProjectScan + | +-column_list=[$subquery1.$col1#1] + | +-expr_list= + | | +-$col1#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-filter_expr= + +-Literal(type=BOOL, value=true) +== + +select (select 1 |> where true) +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#2 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#2] + +-expr_list= + | +-$col1#2 := + | +-SubqueryExpr + | +-type=INT64 + | +-subquery_type=SCALAR + | +-subquery= + | +-FilterScan + | +-column_list=[$expr_subquery.$col1#1] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$expr_subquery.$col1#1] + | | +-expr_list= + | | | +-$col1#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-filter_expr= + | +-Literal(type=BOOL, value=true) + +-input_scan= + +-SingleRowScan +== + +# Expression in a pipe operator referencing a table. +select 1 +|> where (select count(*) from KeyValue) > 1 +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [INT64] ++-query= + +-FilterScan + +-column_list=[$query.$col1#1] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.$col1#1] + | +-expr_list= + | | +-$col1#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + +-SubqueryExpr + | +-type=INT64 + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[$aggregate.$agg1#4] + | +-input_scan= + | +-AggregateScan + | +-column_list=[$aggregate.$agg1#4] + | +-input_scan= + | | +-TableScan(table=KeyValue) + | +-aggregate_list= + | +-$agg1#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-Literal(type=INT64, value=1) +== + +# The pipe operator binds inside the WithScan, as part of the final query, +# so the WITH alias is visible in the pipe operator. +WITH q1 AS (select 1 x |> where x=1) +select * from q1 +|> where x=(select x from q1) +-- +QueryStmt ++-output_column_list= +| +-q1.x#2 AS x [INT64] ++-query= + +-WithScan + +-column_list=[q1.x#2] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q1" + | +-with_subquery= + | +-FilterScan + | +-column_list=[q1.x#1] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[q1.x#1] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=q1.x#1) + | +-Literal(type=INT64, value=1) + +-query= + +-FilterScan + +-column_list=[q1.x#2] + +-input_scan= + | +-ProjectScan + | +-column_list=[q1.x#2] + | +-input_scan= + | +-WithRefScan(column_list=[q1.x#2], with_query_name="q1") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=q1.x#2) + +-SubqueryExpr + +-type=INT64 + +-subquery_type=SCALAR + +-subquery= + +-ProjectScan + +-column_list=[q1.x#3] + +-input_scan= + +-WithRefScan(column_list=[q1.x#3], with_query_name="q1") +== + +# With a WITH query inside parentheses, the WITH alias +# is not visible to the following pipe operator. +[language_features=PIPES,V_1_1_WITH_ON_SUBQUERY] +( + WITH q1 AS (select 1 x |> where x=1) + select * from q1 + |> where x=(select x from q1) +) +|> where x=(select x from q1); +-- +ERROR: Table not found: q1 [at 6:27] +|> where x=(select x from q1); + ^ +== + +# Value table in a WITH clause works as a value table. +WITH q AS (FROM TestExtraValueTable) +FROM q +|> WHERE {{q.|}}int32_val1 = 4 +-- +QueryStmt ++-output_column_list= +| +-q.TestExtraValueTable#4 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-WithScan + +-column_list=[q.TestExtraValueTable#4] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="q" + | +-with_subquery= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1] + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-query= + +-FilterScan + +-column_list=[q.TestExtraValueTable#4] + +-input_scan= + | +-WithRefScan(column_list=[q.TestExtraValueTable#4], with_query_name="q") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=q.TestExtraValueTable#4) + | +-field_descriptor=int32_val1 + | +-default_value=0 + +-Literal(type=INT32, value=4) +== + +# Pipe after a parenthesized query. +( + select 1 x +) +|> where x=1 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$query.x#1] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$query.x#1) + +-Literal(type=INT64, value=1) +== + +# The pipe binds to the whole UNION query, so x is visible. +select 1 x +UNION ALL +select 2 y +|> where x=3 +-- +QueryStmt ++-output_column_list= +| +-$union_all.x#3 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$union_all.x#3] + +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.x#3] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$union_all1.x#1] + | | | +-expr_list= + | | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-output_column_list=[$union_all1.x#1] + | +-SetOperationItem + | +-scan= + | | +-ProjectScan + | | +-column_list=[$union_all2.y#2] + | | +-expr_list= + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-output_column_list=[$union_all2.y#2] + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$union_all.x#3) + +-Literal(type=INT64, value=3) +== + +# The pipe binds to the whole UNION query, so y is not visible. +select 1 x +UNION ALL +select 2 y +|> where y=3 +-- +ERROR: Unrecognized name: y [at 4:10] +|> where y=3 + ^ +== + +( + select 1 x + UNION ALL + select 2 y + |> where x=2 +) +|> where x=3 +-- +QueryStmt ++-output_column_list= +| +-$union_all.x#3 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$union_all.x#3] + +-input_scan= + | +-FilterScan + | +-column_list=[$union_all.x#3] + | +-input_scan= + | | +-SetOperationScan + | | +-column_list=[$union_all.x#3] + | | +-op_type=UNION_ALL + | | +-input_item_list= + | | +-SetOperationItem + | | | +-scan= + | | | | +-ProjectScan + | | | | +-column_list=[$union_all1.x#1] + | | | | +-expr_list= + | | | | | +-x#1 := Literal(type=INT64, value=1) + | | | | +-input_scan= + | | | | +-SingleRowScan + | | | +-output_column_list=[$union_all1.x#1] + | | +-SetOperationItem + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$union_all2.y#2] + | | | +-expr_list= + | | | | +-y#2 := Literal(type=INT64, value=2) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-output_column_list=[$union_all2.y#2] + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$union_all.x#3) + | +-Literal(type=INT64, value=2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$union_all.x#3) + +-Literal(type=INT64, value=3) +== + +# Pipe after a query with LIMIT. +select 1 x +limit 10 +|> where x=1 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$query.x#1] + +-input_scan= + | +-LimitOffsetScan + | +-column_list=[$query.x#1] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$query.x#1] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-limit= + | +-Literal(type=INT64, value=10) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$query.x#1) + +-Literal(type=INT64, value=1) +== + +# Pipe after a query with ORDER BY. +select * +from (select 1 x) +order by x +|> where x=1 +-- +QueryStmt ++-output_column_list= +| +-$subquery1.x#1 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$subquery1.x#1] + +-input_scan= + | +-OrderByScan + | +-column_list=[$subquery1.x#1] + | +-is_ordered=TRUE + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$subquery1.x#1] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | +-input_scan= + | | +-SingleRowScan + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=36-37 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$subquery1.x#1) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$subquery1.x#1) + +-Literal(type=INT64, value=1) +== + +# Test with SELECTs and EXTENDs computing expressions that never get used. +# This is checking a case where the SQLBuilder was failing to include those. +[show_unparsed] +from KeyValue +|> extend key+1 AS k1, 123 AS k2, sqrt(key) AS k3 +|> extend k3+1 AS k4, 'abc' AS k5 +|> select k1, 'xxx' AS k6, k4+1 AS k7 +|> select k1 +-- +QueryStmt ++-output_column_list= +| +-$pipe_extend.k1#3 AS k1 [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_extend.k1#3] + +-input_scan= + +-ProjectScan + +-column_list=[$pipe_extend.k1#3, $pipe_select.k6#8, $pipe_select.k7#9] + +-expr_list= + | +-k6#8 := Literal(type=STRING, value="xxx") + | +-k7#9 := + | +-FunctionCall(ZetaSQL:$add(DOUBLE, DOUBLE) -> DOUBLE) + | +-ColumnRef(type=DOUBLE, column=$pipe_extend.k4#6) + | +-Literal(type=DOUBLE, value=1) + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, $pipe_extend.k1#3, $pipe_extend.k2#4, $pipe_extend.k3#5, $pipe_extend.k4#6, $pipe_extend.k5#7] + +-expr_list= + | +-k4#6 := + | | +-FunctionCall(ZetaSQL:$add(DOUBLE, DOUBLE) -> DOUBLE) + | | +-ColumnRef(type=DOUBLE, column=$pipe_extend.k3#5) + | | +-Literal(type=DOUBLE, value=1) + | +-k5#7 := Literal(type=STRING, value="abc") + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, $pipe_extend.k1#3, $pipe_extend.k2#4, $pipe_extend.k3#5] + +-expr_list= + | +-k1#3 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-k2#4 := Literal(type=INT64, value=123) + | +-k3#5 := + | +-FunctionCall(ZetaSQL:sqrt(DOUBLE) -> DOUBLE) + | +-Cast(INT64 -> DOUBLE) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-input_scan= + +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +[UNPARSED_SQL] +SELECT + projectscan_12.a_3 AS k1 +FROM + ( + SELECT + projectscan_9.a_3 AS a_3, + "xxx" AS a_10, + (projectscan_9.a_7) + 1.0 AS a_11 + FROM + ( + SELECT + projectscan_6.a_1 AS a_1, + projectscan_6.a_3 AS a_3, + projectscan_6.a_4 AS a_4, + projectscan_6.a_5 AS a_5, + (projectscan_6.a_5) + 1.0 AS a_7, + "abc" AS a_8 + FROM + ( + SELECT + keyvalue_2.a_1 AS a_1, + (keyvalue_2.a_1) + 1 AS a_3, + 123 AS a_4, + SQRT(CAST(keyvalue_2.a_1 AS DOUBLE)) AS a_5 + FROM + ( + SELECT + KeyValue.Key AS a_1 + FROM + KeyValue + ) AS keyvalue_2 + ) AS projectscan_6 + ) AS projectscan_9 + ) AS projectscan_12; +== + +select 1 x +|> limit 10 +|> limit 10 offset 20 +|> limit @test_param_int64 +|> where x=1 +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-FilterScan + +-column_list=[$query.x#1] + +-input_scan= + | +-LimitOffsetScan + | +-column_list=[$query.x#1] + | +-input_scan= + | | +-LimitOffsetScan + | | +-column_list=[$query.x#1] + | | +-input_scan= + | | | +-LimitOffsetScan + | | | +-column_list=[$query.x#1] + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=[$query.x#1] + | | | | +-expr_list= + | | | | | +-x#1 := Literal(type=INT64, value=1) + | | | | +-input_scan= + | | | | +-SingleRowScan + | | | +-limit= + | | | +-Literal(type=INT64, value=10) + | | +-limit= + | | | +-Literal(type=INT64, value=10) + | | +-offset= + | | +-Literal(type=INT64, value=20) + | +-limit= + | +-Parameter(type=INT64, name="test_param_int64") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$query.x#1) + +-Literal(type=INT64, value=1) +== + +select 1 x +|> limit @test_param_int32 offset @test_param_bool +-- +ERROR: OFFSET expects an integer literal or parameter [at 2:35] +|> limit @test_param_int32 offset @test_param_bool + ^ +== + +# The table from the pipe input is not visible as a table name in +# correlated expressions subqueries in pipe operators. +from KeyValue kv +|> where (select count(*) from kv) > 1 +-- +ERROR: Table not found: kv (Unqualified identifiers in a FROM clause are always resolved as tables. Identifier kv is in scope but unqualified names cannot be resolved here.) [at 2:32] +|> where (select count(*) from kv) > 1 + ^ +== + +# Columns from the pipe input are visible in correlated expressions subqueries +# in pipe operators. +from KeyValue kv +|> where (select key) > 1 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + +-SubqueryExpr + | +-type=INT64 + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-subquery= + | +-ProjectScan + | +-column_list=[$expr_subquery.key#3] + | +-expr_list= + | | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) + | +-input_scan= + | +-SingleRowScan + +-Literal(type=INT64, value=1) +== + +from TestTable +|> DISTINCT +-- +ERROR: Pipe DISTINCT operator not supported yet; Try using |> AGGREGATE GROUP BY ... or |> SELECT DISTINCT ... [at 2:1] +|> DISTINCT +^ +== + +# From bug 328283572, expression subqueries with pseudo-columns were triggering +# validator errors. Value table version. +# The failures were different with and without column pruning. +[{{|no_}}prune_unused_columns] +SELECT {{|EXISTS|ARRAY}}(FROM TestExtraValueTable) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#4 AS `$col1` [PROTO] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=PROTO + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1] + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: EXISTS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#4 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=EXISTS + | +-subquery= + | +-TableScan(table=TestExtraValueTable) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: ARRAY +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#4 AS `$col1` [ARRAY>] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=ARRAY> + | +-subquery_type=ARRAY + | +-subquery= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1] + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_, +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#4 AS `$col1` [PROTO] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=PROTO + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1] + | +-input_scan= + | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2, RowId#3], table=TestExtraValueTable, column_index_list=[0, 1, 2]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_,EXISTS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#4 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=EXISTS + | +-subquery= + | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2, RowId#3], table=TestExtraValueTable, column_index_list=[0, 1, 2]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_,ARRAY +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#4 AS `$col1` [ARRAY>] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#4] + +-expr_list= + | +-$col1#4 := + | +-SubqueryExpr + | +-type=ARRAY> + | +-subquery_type=ARRAY + | +-subquery= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1] + | +-input_scan= + | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2, RowId#3], table=TestExtraValueTable, column_index_list=[0, 1, 2]) + +-input_scan= + +-SingleRowScan +== + +# From bug 328283572, expression subqueries with pseudo-columns were triggering +# validator errors. +# Non-value table version - after the DROP, it has only one visible column, +# but still has pseudo-columns. +[{{|no_}}prune_unused_columns] +SELECT {{|EXISTS|ARRAY|NULL IN}}(FROM EnumTable + |> DROP TestEnum, AnotherTestEnum) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [INT32] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=INT32 + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1] + | +-input_scan= + | +-TableScan(column_list=[EnumTable.key#1], table=EnumTable, column_index_list=[0]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: EXISTS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=EXISTS + | +-subquery= + | +-TableScan(table=EnumTable) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: ARRAY +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=ARRAY + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1] + | +-input_scan= + | +-TableScan(column_list=[EnumTable.key#1], table=EnumTable, column_index_list=[0]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: NULL IN +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=IN + | +-in_expr= + | | +-Literal(type=INT32, value=NULL) + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1] + | +-input_scan= + | +-TableScan(column_list=[EnumTable.key#1], table=EnumTable, column_index_list=[0]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_, +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [INT32] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=INT32 + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1] + | +-input_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_,EXISTS +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=EXISTS + | +-subquery= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_,ARRAY +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=ARRAY + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1] + | +-input_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_,NULL IN +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=IN + | +-in_expr= + | | +-Literal(type=INT32, value=NULL) + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.key#1] + | +-input_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +== + +# Special case where we have a pseudo-column copied as a real column, so +# there might be two copies of that column in the NameList and column_list +# even though only one is in the visible NameList. +[{{|no_}}prune_unused_columns] +SELECT (FROM EnumTable + |> EXTEND RowId AS RowId2 + |> DROP key, TestEnum, AnotherTestEnum) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BYTES + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.RowId#5] + | +-input_scan= + | +-TableScan(column_list=[EnumTable.RowId#5], table=EnumTable, column_index_list=[4]) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_ +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BYTES] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BYTES + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[EnumTable.RowId#5] + | +-input_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +== + +# EXISTS works even when the subquery produces multiple visible columns. +[{{|no_}}prune_unused_columns] +SELECT EXISTS(FROM EnumTable) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=EXISTS + | +-subquery= + | +-TableScan(table=EnumTable) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_ +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=BOOL + | +-subquery_type=EXISTS + | +-subquery= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +== + +# LIKE ANY with a subquery producuing one column plus pseudo-columns. +# TODO LIKE ANY is not fully implemented yet. This should +# be producing LIKE_ANY/LIKE_ALL subqueries, not SCALAR subqueries. +[language_features=PIPES,V_1_3_LIKE_ANY_SOME_ALL,V_1_4_LIKE_ANY_SOME_ALL_SUBQUERY] +[{{|no_}}prune_unused_columns] +SELECT 'abc' LIKE ANY(FROM EnumTable + |> EXTEND 'a%' AS pattern + |> DROP key, TestEnum, AnotherTestEnum) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#7 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#7] + +-expr_list= + | +-$col1#7 := + | +-FunctionCall(ZetaSQL:$like_any(STRING, repeated(1) STRING) -> BOOL) + | +-Literal(type=STRING, value="abc") + | +-SubqueryExpr + | +-type=STRING + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[$pipe_extend.pattern#6] + | +-input_scan= + | +-ProjectScan + | +-column_list=[$pipe_extend.pattern#6] + | +-expr_list= + | | +-pattern#6 := Literal(type=STRING, value="a%") + | +-input_scan= + | +-TableScan(table=EnumTable) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: no_ +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#7 AS `$col1` [BOOL] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#7] + +-expr_list= + | +-$col1#7 := + | +-FunctionCall(ZetaSQL:$like_any(STRING, repeated(1) STRING) -> BOOL) + | +-Literal(type=STRING, value="abc") + | +-SubqueryExpr + | +-type=STRING + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[$pipe_extend.pattern#6] + | +-input_scan= + | +-ProjectScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, EnumTable.RowId#5, $pipe_extend.pattern#6] + | +-expr_list= + | | +-pattern#6 := Literal(type=STRING, value="a%") + | +-input_scan= + | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 3, 4]) + +-input_scan= + +-SingleRowScan +== + +# This compares ProjectScan output produced for SELECT and EXTEND. +# SELECT makes an extra ProjectScan for the no-op project that just +# adds aliases but EXTEND does not. +SELECT 1 x +|> {{SELECT|EXTEND}} x AS y, x AS z +|> STATIC_DESCRIBE +-- +ALTERNATION GROUP: SELECT +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS y [INT64] +| +-$query.x#1 AS z [INT64] ++-query= + +-StaticDescribeScan + +-column_list=$query.[x#1, x#1] + +-describe_text= + | """ + | NameList: + | y INT64 $query.x#1 + | z INT64 $query.x#1 + | NameScope: + | Names: + | y -> INT64 ($query.x#1) + | z -> INT64 ($query.x#1) + | """ + +-input_scan= + +-ProjectScan + +-column_list=$query.[x#1, x#1] + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: EXTEND +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.x#1 AS y [INT64] +| +-$query.x#1 AS z [INT64] ++-query= + +-StaticDescribeScan + +-column_list=[$query.x#1] + +-describe_text= + | """ + | NameList: + | x INT64 $query.x#1 + | y INT64 $query.x#1 + | z INT64 $query.x#1 + | NameScope: + | Names: + | x -> INT64 ($query.x#1) + | y -> INT64 ($query.x#1) + | z -> INT64 ($query.x#1) + | """ + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan diff --git a/zetasql/analyzer/testdata/pipe_rename.test b/zetasql/analyzer/testdata/pipe_rename.test new file mode 100644 index 000000000..e7eb474ad --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_rename.test @@ -0,0 +1,299 @@ +[default language_features=PIPES,PIPE_STATIC_DESCRIBE] +FROM KeyValue +|> STATIC_DESCRIBE +|> RENAME key AS k +|> RENAME k AS k2 +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS k2 [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | k2 INT64 KeyValue.Key#1 (value table) + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Value -> STRING (KeyValue.Value#2) (implicit) + | k2 -> INT64 (KeyValue.Key#1) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | Value table columns: + | KeyValue.Key#1 + | """ + +-input_scan= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | """ + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +# Swap two columns atomically. +# KeyValue.key is the original `key` before renaming. +# KeyValue expands to the struct for the original row. +FROM KeyValue +|> RENAME key AS value, value AS key +|> WHERE KeyValue.key = value +|> EXTEND KeyValue AS kv +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS value [INT64] +| +-KeyValue.Value#2 AS key [STRING] +| +-$pipe_extend.kv#4 AS kv [STRUCT] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.kv#4] + +-expr_list= + | +-kv#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-input_scan= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +== + +# Rename columns to their original name. This is allowed. +FROM KeyValue +|> RENAME Key AS Key, Value AS Value +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +# Rename columns to names that differ by case. +# For the first column, the lhs name has different case but the +# rhs name matches the existing column. +FROM KeyValue +|> RENAME kEy AS Key, vaLUe AS vALUE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS vALUE [STRING] ++-query= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +# If we make a new range variable, it has the renamed columns. +FROM KeyValue rv1 +|> RENAME key AS xxx +|> AS rv2 +|> SELECT rv2, rv2.xxx +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.rv2#4 AS rv2 [STRUCT] +| +-KeyValue.Key#1 AS xxx [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.rv2#4, KeyValue.Key#1] + +-expr_list= + | +-rv2#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="rv1") +== + +# It's not an error to introduce ambiguous names in the output columns. +# The renamed column is ambiguous with an existing column. +FROM KeyValue +|> EXTEND 1.0 AS xx +|> RENAME key AS xx +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS xx [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_extend.xx#3 AS xx [DOUBLE] ++-query= + +-StaticDescribeScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.xx#3] + +-describe_text= + | """ + | NameList: + | xx INT64 KeyValue.Key#1 (value table) + | Value STRING KeyValue.Value#2 + | xx DOUBLE $pipe_extend.xx#3 + | NameScope: + | Names: + | Value -> STRING (KeyValue.Value#2) (implicit) + | xx -> ambiguous + | Range variables: + | KeyValue -> RANGE_VARIABLE + | """ + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.xx#3] + +-expr_list= + | +-xx#3 := Literal(type=DOUBLE, value=1) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +# Range variables still hide columns, even after the rename. +FROM KeyValue kv +|> RENAME key AS kv +|> STATIC_DESCRIBE +|> EXTEND kv AS kv2 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS kv [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_extend.kv2#4 AS kv2 [STRUCT] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_extend.kv2#4] + +-expr_list= + | +-kv2#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-input_scan= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | kv INT64 KeyValue.Key#1 (value table) + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | kv -> RANGE_VARIABLE + | """ + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") +== + +FROM KeyValue +|> RENAME bad AS y +-- +ERROR: Column name in pipe RENAME not found in input table: bad [at 2:11] +|> RENAME bad AS y + ^ +== + +FROM KeyValue +|> RENAME kEy AS k1, keY AS k2 +-- +ERROR: Duplicate column name in pipe RENAME: keY [at 2:22] +|> RENAME kEy AS k1, keY AS k2 + ^ +== + +FROM KeyValue +|> RENAME `$abc` AS def +-- +ERROR: Cannot use pipe RENAME with internal alias `$abc` [at 2:11] +|> RENAME `$abc` AS def + ^ +== + +FROM KeyValue +|> RENAME abc AS `$def` +-- +ERROR: Cannot use pipe RENAME with internal alias `$def` [at 2:18] +|> RENAME abc AS `$def` + ^ +== + +FROM KeyValue kv +|> RENAME kv AS xxx +-- +ERROR: Name in pipe RENAME is a table alias; RENAME can only rename columns: kv [at 2:11] +|> RENAME kv AS xxx + ^ +== + +FROM TestExtraValueTable vt +|> RENAME int32_val1 AS vvv +-- +ERROR: Name in pipe RENAME is a field inside a value table; RENAME can only rename columns: int32_val1 [at 2:11] +|> RENAME int32_val1 AS vvv + ^ +== + +FROM EnumTable +|> RENAME Filename AS fff +-- +ERROR: Name in pipe RENAME is present but is not a column on the pipe input table; RENAME can only rename columns: Filename [at 2:11] +|> RENAME Filename AS fff + ^ +== + +SELECT 1 x, 2 x +|> RENAME x AS y +-- +ERROR: Column name in pipe RENAME exists more than once in input table: x [at 2:11] +|> RENAME x AS y + ^ +== + +# Filename is both a real column and a pseudo-column. +FROM EnumTable +|> EXTEND Filename +|> RENAME Filename AS fff +-- +ERROR: Name in pipe RENAME is ambiguous; RENAME can only rename columns: Filename [at 3:11] +|> RENAME Filename AS fff + ^ +== + +# `key` is both a column and a range variable. +FROM KeyValue +|> AS key +|> RENAME key AS xxx +-- +ERROR: Name in pipe RENAME is a table alias; RENAME can only rename columns: key [at 3:11] +|> RENAME key AS xxx + ^ +== + +# Attempt to rename a correlated column reference. +FROM KeyValue +|> EXTEND (SELECT 1 x + |> RENAME key AS zzz) +-- +ERROR: Name in pipe RENAME is present but is not a column on the pipe input table; RENAME can only rename columns: key [at 3:22] + |> RENAME key AS zzz) + ^ diff --git a/zetasql/analyzer/testdata/pipe_select.test b/zetasql/analyzer/testdata/pipe_select.test new file mode 100644 index 000000000..8106a561e --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_select.test @@ -0,0 +1,870 @@ +[default language_features=PIPES,ANALYTIC_FUNCTIONS,V_1_1_SELECT_STAR_EXCEPT_REPLACE] +select 1 x, 2 y +|> select *, x+y as z +|> select x, z +; +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$pipe_select.z#3 AS z [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $pipe_select.z#3] + +-input_scan= + +-ProjectScan + +-column_list=[$query.x#1, $query.y#2, $pipe_select.z#3] + +-expr_list= + | +-z#3 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-ColumnRef(type=INT64, column=$query.y#2) + +-input_scan= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +# Can't have WITH ANONYMIZATION clause on pipe SELECT. +select 1 +|> select with anonymization 1,2 +-- +ERROR: Pipe SELECT does not support SELECT WITH [at 2:11] +|> select with anonymization 1,2 + ^ +== + +select 1 x +|> select 1 + count(*) +-- +ERROR: Aggregate function COUNT(*) not allowed in pipe SELECT [at 2:15] +|> select 1 + count(*) + ^ +== + +[language_features=PIPES] +select 1 x +|> select sum(x) OVER () +-- +ERROR: Analytic functions not supported [at 2:11] +|> select sum(x) OVER () + ^ +== + +# Window functions. ProjectScans are added for the expression +# fragments before and after the AnalyticScan. +# The common OVER clause becomes one AnalyticFunctionGroup, but this only +# works for empty OVER clauses. (This is the same in standard syntax.) +select 1 x, 2 y, 3 z +|> select 1 + sum(x) OVER (), + avg(y) OVER (), + x, + avg(x+1) OVER (partition by y order by z) AS a2 +|> select a2 +-- +QueryStmt ++-output_column_list= +| +-$analytic.a2#10 AS a2 [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$analytic.a2#10] + +-input_scan= + +-ProjectScan + +-column_list=[$pipe_select.$col1#8, $analytic.$analytic2#9, $query.x#1, $analytic.a2#10] + +-expr_list= + | +-$col1#8 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Literal(type=INT64, value=1) + | +-ColumnRef(type=INT64, column=$analytic.$analytic1#7) + +-input_scan= + +-AnalyticScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#7, $analytic.$analytic2#9, $analytic.a2#10] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-function_group_list= + +-AnalyticFunctionGroup + | +-analytic_function_list= + | +-$analytic1#7 := + | | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.x#1) + | | +-window_frame= + | | +-WindowFrame(frame_unit=ROWS) + | | +-start_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | +-end_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + | +-$analytic2#9 := + | +-AnalyticFunctionCall(ZetaSQL:avg(INT64) -> DOUBLE) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-AnalyticFunctionGroup + +-partition_by= + | +-WindowPartitioning + | +-partition_by_list= + | +-ColumnRef(type=INT64, column=$query.y#2) + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.z#3) + +-analytic_function_list= + +-a2#10 := + +-AnalyticFunctionCall(ZetaSQL:avg(INT64) -> DOUBLE) + +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.x#1) + +-Literal(type=INT64, value=1) + +-window_frame= + +-WindowFrame(frame_unit=RANGE) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=CURRENT ROW) +== + +# Window function on a value table input. +FROM TestExtraValueTable t +|> SELECT t, sum(int32_val1) OVER (ORDER BY int32_val1, t.int32_val2) +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS t [PROTO] +| +-$analytic.$analytic1#5 AS `$col2` [INT64] ++-query= + +-ProjectScan + +-column_list=[TestExtraValueTable.value#1, $analytic.$analytic1#5] + +-input_scan= + +-AnalyticScan + +-column_list=[TestExtraValueTable.value#1, $analytic.$analytic1#5] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1, $orderby.int32_val1#6, $orderby.int32_val2#7] + | +-expr_list= + | | +-int32_val1#6 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val1 + | | | +-default_value=0 + | | +-int32_val2#7 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="t") + +-function_group_list= + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT32, column=$orderby.int32_val1#6) + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=INT32, column=$orderby.int32_val2#7) + +-analytic_function_list= + +-$analytic1#5 := + +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-Cast(INT32 -> INT64) + +-GetProtoField + +-type=INT32 + +-expr= + | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + +-field_descriptor=int32_val1 + +-default_value=0 + +-window_frame= + +-WindowFrame(frame_unit=RANGE) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=CURRENT ROW) +== + +# Window function in combination with DISTINCT. +select 1 x, 2 y, 3 z +|> select distinct 1+sum(x) OVER () xp1 +-- +QueryStmt ++-output_column_list= +| +-$distinct.xp1#7 AS xp1 [INT64] ++-query= + +-AggregateScan + +-column_list=[$distinct.xp1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#5, $pipe_select.xp1#6] + | +-expr_list= + | | +-xp1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Literal(type=INT64, value=1) + | | +-ColumnRef(type=INT64, column=$analytic.$analytic1#5) + | +-input_scan= + | +-AnalyticScan + | +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#5] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2, z#3] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | | +-z#3 := Literal(type=INT64, value=3) + | | +-input_scan= + | | +-SingleRowScan + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-analytic_function_list= + | +-$analytic1#5 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-group_by_list= + +-xp1#7 := ColumnRef(type=INT64, column=$pipe_select.xp1#6) +== + +# Window function computed but not used. +FROM KeyValue +|> select key, count(*) over () +|> select key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1] + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, $analytic.$analytic1#4] + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, $analytic.$analytic1#4] + +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#4 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Window function over aggregate function works in standard SQL, but +# doesn't make sense here. +select 1 x, 2 y, 3 z +|> select distinct sum(sum(x)) OVER () +-- +ERROR: Aggregate function SUM not allowed in pipe SELECT [at 2:24] +|> select distinct sum(sum(x)) OVER () + ^ +== + +# Calling SELECT with a window function after AGGREGATE. +FROM KeyValue +|> AGGREGATE max(value) as max_value GROUP BY key +|> SELECT key, max_value, RANK() OVER (ORDER BY max_value) +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#4 AS key [INT64] +| +-$aggregate.max_value#3 AS max_value [STRING] +| +-$analytic.$analytic1#6 AS `$col3` [INT64] ++-query= + +-ProjectScan + +-column_list=[$groupby.key#4, $aggregate.max_value#3, $analytic.$analytic1#6] + +-input_scan= + +-AnalyticScan + +-column_list=[$groupby.key#4, $aggregate.max_value#3, $analytic.$analytic1#6] + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#4, $aggregate.max_value#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-group_by_list= + | | +-key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-aggregate_list= + | +-max_value#3 := + | +-AggregateFunctionCall(ZetaSQL:max(STRING) -> STRING) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-function_group_list= + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=STRING, column=$aggregate.max_value#3) + +-analytic_function_list= + +-$analytic1#6 := AnalyticFunctionCall(ZetaSQL:rank() -> INT64) +== + +from KeyValue +|> select count(*) OVER w +-- +ERROR: Cannot reference a named window in pipe SELECT [at 2:25] +|> select count(*) OVER w + ^ +== + +select 1 z +|> select @{hint1=1} z*2 as z +|> select @{hint2=1} as struct 1 x, z*2 y +-- +ERROR: Pipe SELECT does not support hints yet [at 2:11] +|> select @{hint1=1} z*2 as z + ^ +== + +select 1 z +|> select distinct z +-- +QueryStmt ++-output_column_list= +| +-$distinct.z#2 AS z [INT64] ++-query= + +-AggregateScan + +-column_list=[$distinct.z#2] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.z#1] + | +-expr_list= + | | +-z#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-group_by_list= + +-z#2 := ColumnRef(type=INT64, column=$query.z#1) +== + +FROM KeyValue +|> SELECT DISTINCT *, concat('abc', value) +-- +QueryStmt ++-output_column_list= +| +-$distinct.Key#4 AS Key [INT64] +| +-$distinct.Value#5 AS Value [STRING] +| +-$distinct.$col2#6 AS `$col2` [STRING] ++-query= + +-AggregateScan + +-column_list=$distinct.[Key#4, Value#5, $col2#6] + +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_select.$col2#3] + | +-expr_list= + | | +-$col2#3 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-Literal(type=STRING, value="abc") + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-group_by_list= + +-Key#4 := ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + +-$col2#6 := ColumnRef(type=STRING, column=$pipe_select.$col2#3) +== + +# Can't DISTINCT on a proto. +FROM TestTable +|> select distinct KitchenSink +-- +ERROR: Column KitchenSink of type PROTO cannot be used in SELECT DISTINCT [at 2:20] +|> select distinct KitchenSink + ^ +== + +# Can do DISTINCT when constructing a proto. +FROM TestTable +|> SELECT DISTINCT AS zetasql_test__.KitchenSinkPB + KitchenSink.int32_val, KitchenSink.int64_key_1, 123 AS int64_key_2 +-- +QueryStmt ++-output_column_list= +| +-$make_proto.$proto#10 AS `$proto` [PROTO] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_proto.$proto#10] + +-expr_list= + | +-$proto#10 := + | +-MakeProto + | +-type=PROTO + | +-field_list= + | +-int32_val := ColumnRef(type=INT32, column=$distinct.int32_val#7) + | +-int64_key_1 := ColumnRef(type=INT64, column=$distinct.int64_key_1#8) + | +-int64_key_2 := ColumnRef(type=INT64, column=$distinct.int64_key_2#9) + +-input_scan= + +-AggregateScan + +-column_list=$distinct.[int32_val#7, int64_key_1#8, int64_key_2#9] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $pipe_select.int32_val#4, $pipe_select.int64_key_1#5, $pipe_select.int64_key_2#6] + | +-expr_list= + | | +-int32_val#4 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int32_val + | | | +-default_value=77 + | | +-int64_key_1#5 := + | | | +-GetProtoField + | | | +-type=INT64 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=int64_key_1 + | | +-int64_key_2#6 := Literal(type=INT64, value=123) + | +-input_scan= + | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + +-group_by_list= + +-int32_val#7 := ColumnRef(type=INT32, column=$pipe_select.int32_val#4) + +-int64_key_1#8 := ColumnRef(type=INT64, column=$pipe_select.int64_key_1#5) + +-int64_key_2#9 := ColumnRef(type=INT64, column=$pipe_select.int64_key_2#6) +== + +# Can do DISTINCT when constructing a struct. +FROM TestTable tt, KeyValue kv +|> select DISTINCT AS STRUCT tt.KitchenSink.bool_val, kv.key +-- +QueryStmt ++-output_column_list= +| +-$make_struct.$struct#9 AS `$struct` [STRUCT] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_struct.$struct#9] + +-expr_list= + | +-$struct#9 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=BOOL, column=$distinct.bool_val#7) + | +-ColumnRef(type=INT64, column=$distinct.Key#8) + +-input_scan= + +-AggregateScan + +-column_list=$distinct.[bool_val#7, Key#8] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, KeyValue.Key#4, $pipe_select.bool_val#6] + | +-expr_list= + | | +-bool_val#6 := + | | +-GetProtoField + | | +-type=BOOL + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=bool_val + | | +-default_value=false + | +-input_scan= + | +-JoinScan + | +-column_list=[TestTable.KitchenSink#3, KeyValue.Key#4] + | +-left_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2], alias="tt") + | +-right_scan= + | +-TableScan(column_list=[KeyValue.Key#4], table=KeyValue, column_index_list=[0], alias="kv") + +-group_by_list= + +-bool_val#7 := ColumnRef(type=BOOL, column=$pipe_select.bool_val#6) + +-Key#8 := ColumnRef(type=INT64, column=KeyValue.Key#4) +== + +select 1 x, "abc" y +|> select x AS y, y AS x +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS y [INT64] +| +-$query.y#2 AS x [STRING] ++-query= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-input_scan= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=STRING, value="abc") + +-input_scan= + +-SingleRowScan +== + +# SELECT (by default) makes the result no longer a value table. +select as value 1 +|> select * +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$value_column` [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-input_scan= + +-ProjectScan + +-column_list=[$query.$col1#1] + +-expr_list= + | +-$col1#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +== + +# SELECT can make a value table. +select 1 x, 2 y +|> select as value x +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS `$value_column` [INT64] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$query.x#1] + +-input_scan= + +-ProjectScan + +-column_list=$query.[x#1, y#2] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + | +-y#2 := Literal(type=INT64, value=2) + +-input_scan= + +-SingleRowScan +== + +select (select 1, 2 y |> select as struct *, t) +from unnest([1,2]) t +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#6 AS `$col1` [STRUCT] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#6] + +-expr_list= + | +-$col1#6 := + | +-SubqueryExpr + | +-type=STRUCT + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=$array.t#1) + | +-subquery= + | +-ProjectScan + | +-column_list=[$make_struct.$struct#5] + | +-expr_list= + | | +-$struct#5 := + | | +-MakeStruct + | | +-type=STRUCT + | | +-field_list= + | | +-ColumnRef(type=INT64, column=$expr_subquery.$col1#2) + | | +-ColumnRef(type=INT64, column=$expr_subquery.y#3) + | | +-ColumnRef(type=INT64, column=$pipe_select.t#4) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$expr_subquery.$col1#2, $expr_subquery.y#3, $pipe_select.t#4] + | +-expr_list= + | | +-t#4 := ColumnRef(type=INT64, column=$array.t#1, is_correlated=TRUE) + | +-input_scan= + | +-ProjectScan + | +-column_list=$expr_subquery.[$col1#2, y#3] + | +-expr_list= + | | +-$col1#2 := Literal(type=INT64, value=1) + | | +-y#3 := Literal(type=INT64, value=2) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-ArrayScan + +-column_list=[$array.t#1] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.t#1] +== + +# SELECT AS Proto +select 17 value, 'k' key +|> SELECT AS zetasql_test__.KeyValueStruct * +-- +QueryStmt ++-output_column_list= +| +-$make_proto.$proto#3 AS `$proto` [PROTO] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_proto.$proto#3] + +-expr_list= + | +-$proto#3 := + | +-MakeProto + | +-type=PROTO + | +-field_list= + | +-value := ColumnRef(type=INT64, column=$query.value#1) + | +-key := ColumnRef(type=STRING, column=$query.key#2) + +-input_scan= + +-ProjectScan + +-column_list=$query.[value#1, key#2] + +-input_scan= + +-ProjectScan + +-column_list=$query.[value#1, key#2] + +-expr_list= + | +-value#1 := Literal(type=INT64, value=17) + | +-key#2 := Literal(type=STRING, value="k") + +-input_scan= + +-SingleRowScan +== + +# This is an example where the SELECT pipe turns into three ProjectScans, +# for the initial expression (MakeStruct), dot-star, and MakeMproto. +# TODO Possibly there could be one more once hints are supported. +select 'k' key +|> SELECT AS zetasql_test__.KeyValueStruct struct(key,17 as value).* +-- +QueryStmt ++-output_column_list= +| +-$make_proto.$proto#5 AS `$proto` [PROTO] ++-is_value_table=TRUE ++-query= + +-ProjectScan + +-column_list=[$make_proto.$proto#5] + +-expr_list= + | +-$proto#5 := + | +-MakeProto + | +-type=PROTO + | +-field_list= + | +-key := ColumnRef(type=STRING, column=$pipe_select.key#3) + | +-value := ColumnRef(type=INT64, column=$pipe_select.value#4) + +-input_scan= + +-ProjectScan + +-column_list=$pipe_select.[key#3, value#4] + +-expr_list= + | +-key#3 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#2) + | | +-field_idx=0 + | +-value#4 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#2) + | +-field_idx=1 + +-input_scan= + +-ProjectScan + +-column_list=[$query.key#1, $preproject.$struct#2] + +-expr_list= + | +-$struct#2 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=STRING, column=$query.key#1) + | +-Literal(type=INT64, value=17) + +-input_scan= + +-ProjectScan + +-column_list=[$query.key#1] + +-expr_list= + | +-key#1 := Literal(type=STRING, value="k") + +-input_scan= + +-SingleRowScan +== + +# This shows a pipe SELECT with an inferred table type passed in (for a +# braced proto constructor). +[language_features=PIPES,V_1_3_BRACED_PROTO_CONSTRUCTORS] +SELECT NEW zetasql_test__.KitchenSinkPB { + int64_key_1: 1 + int64_key_2: 2 + nested_value: (SELECT 1 |> SELECT { {{nested_int64|bad_name}}: 5 }) +} +-- +ALTERNATION GROUP: nested_int64 +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [PROTO] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#3] + +-expr_list= + | +-$col1#3 := + | +-MakeProto + | +-type=PROTO + | +-field_list= + | +-int64_key_1 := Literal(type=INT64, value=1) + | +-int64_key_2 := Literal(type=INT64, value=2) + | +-nested_value := + | +-SubqueryExpr + | +-type=PROTO + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[$pipe_select.$col1#2] + | +-expr_list= + | | +-$col1#2 := + | | +-MakeProto + | | +-type=PROTO + | | +-field_list= + | | +-nested_int64 := Literal(type=INT64, value=5) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$expr_subquery.$col1#1] + | +-expr_list= + | | +-$col1#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-SingleRowScan +-- +ALTERNATION GROUP: bad_name +-- +ERROR: Field 1 has name bad_name which is not a field in proto zetasql_test__.KitchenSinkPB.Nested [at 4:39] + nested_value: (SELECT 1 |> SELECT { bad_name: 5 }) + ^ +== + +# This shows type inferrence is not currently passed through pipe WHERE +# to pipe SELECT. +[language_features=PIPES,V_1_3_BRACED_PROTO_CONSTRUCTORS] +SELECT NEW zetasql_test__.KitchenSinkPB { + int64_key_1: 1 + int64_key_2: 2 + nested_value: (SELECT 1 |> SELECT { nested_int64: 5 } |> WHERE true) +} +-- +ERROR: Unable to infer a type for braced constructor [at 4:37] + nested_value: (SELECT 1 |> SELECT { nested_int64: 5 } |> WHERE true) + ^ +== + +FROM (SELECT 1 AS col1, 2 AS col2) +|> SELECT ANY_VALUE(col2) +-- +ERROR: Aggregate function ANY_VALUE not allowed in pipe SELECT [at 2:11] +|> SELECT ANY_VALUE(col2) + ^ +== + +FROM (SELECT 1 AS col1, 2 AS col2) +|> SELECT ANY_VALUE(STRUCT(col2)) +-- +ERROR: Aggregate function ANY_VALUE not allowed in pipe SELECT [at 2:11] +|> SELECT ANY_VALUE(STRUCT(col2)) + ^ +== + +# b/327665663 - previously, the ExprResolutionInfo option to disallow +# aggregates was not propagated inside .* resolution, so the error disallowing +# aggregates wouldn't happen, and this led to a ret_check. +FROM (SELECT 1 AS col1, 2 AS col2) +|> SELECT ANY_VALUE(STRUCT(col2)).* +-- +ERROR: Aggregate function ANY_VALUE not allowed in pipe SELECT [at 2:11] +|> SELECT ANY_VALUE(STRUCT(col2)).* + ^ +== + +FROM KeyValue +|> SELECT SUM(key).* +-- +ERROR: Aggregate function SUM not allowed in pipe SELECT [at 2:11] +|> SELECT SUM(key).* + ^ +== + +FROM KeyValue +|> SELECT STRUCT(SUM(key)).* +-- +ERROR: Aggregate function SUM not allowed in pipe SELECT [at 2:18] +|> SELECT STRUCT(SUM(key)).* + ^ +== + +# Window functions are allowed in pipe SELECT, and therefore +# combining them with STRUCT and .* is okay. +FROM KeyValue +|> SELECT STRUCT(COUNT(*) OVER ()).* +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.$field1#5 AS `$field1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.$field1#5] + +-expr_list= + | +-$field1#5 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$preproject.$struct#4) + | +-field_idx=0 + +-input_scan= + +-ProjectScan + +-column_list=[$analytic.$analytic1#6, $preproject.$struct#4] + +-expr_list= + | +-$struct#4 := + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=$analytic.$analytic1#6) + +-input_scan= + +-AnalyticScan + +-column_list=[$analytic.$analytic1#6] + +-input_scan= + | +-TableScan(table=KeyValue) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#6 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Make sure appropriate errors are generated for the expressions +# inside SELECT * REPLACE. +FROM KeyValue +|> SELECT * REPLACE (sum(key) AS key) +-- +ERROR: Aggregate function SUM not allowed in pipe SELECT [at 2:22] +|> SELECT * REPLACE (sum(key) AS key) + ^ +== + +FROM KeyValue +|> SELECT struct(key, value).* REPLACE (sum(key) AS key) +-- +ERROR: Aggregate function SUM not allowed in pipe SELECT [at 2:41] +|> SELECT struct(key, value).* REPLACE (sum(key) AS key) + ^ +== + +FROM KeyValue +|> SELECT * REPLACE (COUNT(*) OVER () AS key) +-- +ERROR: Cannot use analytic functions inside SELECT * REPLACE [at 2:22] +|> SELECT * REPLACE (COUNT(*) OVER () AS key) + ^ diff --git a/zetasql/analyzer/testdata/pipe_set.test b/zetasql/analyzer/testdata/pipe_set.test new file mode 100644 index 000000000..0b8bc186e --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_set.test @@ -0,0 +1,997 @@ +[default language_features=PIPES,PIPE_STATIC_DESCRIBE,ANALYTIC_FUNCTIONS] +from KeyValue +|> STATIC_DESCRIBE +|> SET kEY = 'abc' +|> STATIC_DESCRIBE +|> WHERE value is null +|> WHERE KeyValue.value is null +|> WHERE KeyValue.key is null +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.kEY#3 AS Key [STRING] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.kEY#3] + +-input_scan= + | +-FilterScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.kEY#3] + | +-input_scan= + | | +-FilterScan + | | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.kEY#3] + | | +-input_scan= + | | | +-StaticDescribeScan + | | | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.kEY#3] + | | | +-describe_text= + | | | | """ + | | | | NameList: + | | | | Key STRING $pipe_set.kEY#3 + | | | | Value STRING KeyValue.Value#2 + | | | | NameScope: + | | | | Names: + | | | | Key -> STRING ($pipe_set.kEY#3) + | | | | Value -> STRING (KeyValue.Value#2) (implicit) + | | | | Range variables: + | | | | KeyValue -> RANGE_VARIABLE + | | | | """ + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.kEY#3] + | | | +-expr_list= + | | | | +-kEY#3 := Literal(type=STRING, value="abc") + | | | +-input_scan= + | | | +-StaticDescribeScan + | | | +-column_list=KeyValue.[Key#1, Value#2] + | | | +-describe_text= + | | | | """ + | | | | NameList: + | | | | Key INT64 KeyValue.Key#1 + | | | | Value STRING KeyValue.Value#2 + | | | | NameScope: + | | | | Names: + | | | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | | | Value -> STRING (KeyValue.Value#2) (implicit) + | | | | Range variables: + | | | | KeyValue -> RANGE_VARIABLE + | | | | """ + | | | +-input_scan= + | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +== + +from KeyValue +|> set Key = 5, kEy = 6 +-- +ERROR: Duplicate column name in pipe SET: kEy [at 2:17] +|> set Key = 5, kEy = 6 + ^ +== + +# Column order is preserved by SET. +select 1 c, 2 b, 3 d +|> SET b = 'x' +|> STATIC_DESCRIBE +|> select * +-- +QueryStmt ++-output_column_list= +| +-$query.c#1 AS c [INT64] +| +-$pipe_set.b#4 AS b [STRING] +| +-$query.d#3 AS d [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.c#1, $pipe_set.b#4, $query.d#3] + +-input_scan= + +-StaticDescribeScan + +-column_list=[$query.c#1, $query.b#2, $query.d#3, $pipe_set.b#4] + +-describe_text= + | """ + | NameList: + | c INT64 $query.c#1 + | b STRING $pipe_set.b#4 + | d INT64 $query.d#3 + | NameScope: + | Names: + | b -> STRING ($pipe_set.b#4) + | c -> INT64 ($query.c#1) + | d -> INT64 ($query.d#3) + | """ + +-input_scan= + +-ProjectScan + +-column_list=[$query.c#1, $query.b#2, $query.d#3, $pipe_set.b#4] + +-expr_list= + | +-b#4 := Literal(type=STRING, value="x") + +-input_scan= + +-ProjectScan + +-column_list=$query.[c#1, b#2, d#3] + +-expr_list= + | +-c#1 := Literal(type=INT64, value=1) + | +-b#2 := Literal(type=INT64, value=2) + | +-d#3 := Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +== + +from KeyValue +|> set key = 1, something = 2, value = 3 +-- +ERROR: Column name in pipe SET not found in input table: something [at 2:17] +|> set key = 1, something = 2, value = 3 + ^ +== + +from KeyValue +|> set `$abc` = 5 +-- +ERROR: Cannot use pipe SET with internal alias `$abc` [at 2:8] +|> set `$abc` = 5 + ^ +== + +FROM KeyValue kv1 CROSS JOIN KeyValue kv2 +|> set key = 5 +-- +ERROR: Column name in pipe SET exists more than once in input table: Key [at 2:8] +|> set key = 5 + ^ +== + +FROM KeyValue kv +|> set key = 1.5 +|> SELECT key, kv.key, *, kv.* +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.key#3 AS key [DOUBLE] +| +-KeyValue.Key#1 AS key [INT64] +| +-$pipe_set.key#3 AS Key [DOUBLE] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[$pipe_set.key#3, KeyValue.Key#1, $pipe_set.key#3, KeyValue.Value#2, KeyValue.Key#1, KeyValue.Value#2] + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.key#3] + +-expr_list= + | +-key#3 := Literal(type=DOUBLE, value=1.5) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") +== + +FROM KeyValue +|> SET key = key+1, value = value +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.key#3 AS Key [INT64] +| +-$pipe_set.value#4 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.key#3, $pipe_set.value#4] + +-expr_list= + | +-key#3 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-Literal(type=INT64, value=1) + | +-value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +FROM KeyValue kv +|> SET kv = 5 +-- +ERROR: Name in pipe SET is a table alias; SET can only update columns: kv [at 2:8] +|> SET kv = 5 + ^ +== + +# This is allowed because there's a column `key`. +# The range variable `key` is also dropped in favor of the new column. +FROM KeyValue key +|> SET key = 1.5 +|> SELECT {{key|key.key|value}} +-- +ALTERNATION GROUP: key +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.key#3 AS key [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$pipe_set.key#3] + +-input_scan= + +-ProjectScan + +-column_list=[$pipe_set.key#3] + +-expr_list= + | +-key#3 := Literal(type=DOUBLE, value=1.5) + +-input_scan= + +-TableScan(table=KeyValue, alias="key") +-- +ALTERNATION GROUP: key.key +-- +ERROR: Cannot access field key on a value with type DOUBLE [at 3:15] +|> SELECT key.key + ^ +-- +ALTERNATION GROUP: value +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Value#2 AS value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Value#2] + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Value#2, $pipe_set.key#3] + +-expr_list= + | +-key#3 := Literal(type=DOUBLE, value=1.5) + +-input_scan= + +-TableScan(column_list=[KeyValue.Value#2], table=KeyValue, column_index_list=[1], alias="key") +== + +# Replacing a pseudo-column doesn't work. +FROM EnumTable +|> WHERE Filename is not null +|> SET Filename = 5 +-- +ERROR: Name in pipe SET is present but is not a column on the pipe input table; SET can only update columns: Filename [at 3:8] +|> SET Filename = 5 + ^ +== + +# SET works because there is a real column Filename. +# The pseudo-column Filename is also dropped in favor of the column. +FROM EnumTable +|> WHERE Filename is null +|> EXTEND 'abc' AS Filename +|> STATIC_DESCRIBE +|> SET Filename = 1.5 +|> STATIC_DESCRIBE +|> WHERE Filename is null +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-$pipe_set.Filename#7 AS Filename [DOUBLE] ++-query= + +-FilterScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_extend.Filename#6, $pipe_set.Filename#7] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_extend.Filename#6, $pipe_set.Filename#7] + | +-describe_text= + | | """ + | | NameList: + | | key INT32 EnumTable.key#1 + | | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | | Filename DOUBLE $pipe_set.Filename#7 + | | NameScope: + | | Names: + | | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (EnumTable.AnotherTestEnum#3) (implicit) + | | Filename -> DOUBLE ($pipe_set.Filename#7) + | | RowId -> BYTES (EnumTable.RowId#5) (implicit) (pseudo-column) + | | TestEnum -> zetasql_test__.TestEnum (EnumTable.TestEnum#2) (implicit) + | | key -> INT32 (EnumTable.key#1) (implicit) + | | Range variables: + | | EnumTable -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_extend.Filename#6, $pipe_set.Filename#7] + | +-expr_list= + | | +-Filename#7 := Literal(type=DOUBLE, value=1.5) + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_extend.Filename#6] + | +-describe_text= + | | """ + | | NameList: + | | key INT32 EnumTable.key#1 + | | TestEnum zetasql_test__.TestEnum EnumTable.TestEnum#2 + | | AnotherTestEnum zetasql_test__.AnotherTestEnum EnumTable.AnotherTestEnum#3 + | | Filename STRING $pipe_extend.Filename#6 + | | NameScope: + | | Names: + | | AnotherTestEnum -> zetasql_test__.AnotherTestEnum (EnumTable.AnotherTestEnum#3) (implicit) + | | Filename -> ambiguous + | | RowId -> BYTES (EnumTable.RowId#5) (implicit) (pseudo-column) + | | TestEnum -> zetasql_test__.TestEnum (EnumTable.TestEnum#2) (implicit) + | | key -> INT32 (EnumTable.key#1) (implicit) + | | Range variables: + | | EnumTable -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_extend.Filename#6] + | +-expr_list= + | | +-Filename#6 := Literal(type=STRING, value="abc") + | +-input_scan= + | +-FilterScan + | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4] + | +-input_scan= + | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4], table=EnumTable, column_index_list=[0, 1, 2, 3]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=EnumTable.Filename#4) + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(DOUBLE) -> BOOL) + +-ColumnRef(type=DOUBLE, column=$pipe_set.Filename#7) +== + +FROM KeyValue, UNNEST([1]) elem WITH OFFSET +|> STATIC_DESCRIBE +|> SET elem = 'x', offset = 'y' +|> STATIC_DESCRIBE +|> WHERE elem is null +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$pipe_set.elem#5 AS elem [STRING] +| +-$pipe_set.offset#6 AS offset [STRING] ++-query= + +-FilterScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.elem#5, $pipe_set.offset#6] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.elem#5, $pipe_set.offset#6] + | +-describe_text= + | | """ + | | NameList: + | | Key INT64 KeyValue.Key#1 + | | Value STRING KeyValue.Value#2 + | | elem STRING $pipe_set.elem#5 + | | offset STRING $pipe_set.offset#6 + | | NameScope: + | | Names: + | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | Value -> STRING (KeyValue.Value#2) (implicit) + | | elem -> STRING ($pipe_set.elem#5) + | | offset -> STRING ($pipe_set.offset#6) + | | Range variables: + | | KeyValue -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $pipe_set.elem#5, $pipe_set.offset#6] + | +-expr_list= + | | +-elem#5 := Literal(type=STRING, value="x") + | | +-offset#6 := Literal(type=STRING, value="y") + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-describe_text= + | | """ + | | NameList: + | | Key INT64 KeyValue.Key#1 + | | Value STRING KeyValue.Value#2 + | | elem INT64 $array.elem#3 (value table) + | | offset INT64 $array_offset.offset#4 (value table) + | | NameScope: + | | Names: + | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | Value -> STRING (KeyValue.Value#2) (implicit) + | | Range variables: + | | KeyValue -> RANGE_VARIABLE + | | elem -> RANGE_VARIABLE<$value> + | | offset -> RANGE_VARIABLE<$value> + | | Value table columns: + | | $array.elem#3 + | | $array_offset.offset#4 + | | """ + | +-input_scan= + | +-ArrayScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=[1]) + | +-element_column_list=[$array.elem#3] + | +-array_offset_column= + | +-ColumnHolder(column=$array_offset.offset#4) + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + +-ColumnRef(type=STRING, column=$pipe_set.elem#5) +== + +# We can't SET field names or pseudo-columns from a value table. +FROM TestExtraValueTable vt +|> SET {{int32_val1|Filename}} = 5 +-- +ALTERNATION GROUP: int32_val1 +-- +ERROR: Name in pipe SET is a field inside a value table; SET can only update columns: int32_val1 [at 2:8] +|> SET int32_val1 = 5 + ^ +-- +ALTERNATION GROUP: Filename +-- +ERROR: Name in pipe SET is present but is not a column on the pipe input table; SET can only update columns: Filename [at 2:8] +|> SET Filename = 5 + ^ +== + +# We can update a value table column (which is also a range variable). +FROM TestExtraValueTable vt1, TestExtraValueTable vt2 +|> STATIC_DESCRIBE +|> SET vt1 = 0 +|> STATIC_DESCRIBE +|> WHERE int32_val1 = 1 +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.vt1#7 AS vt1 [INT64] +| +-TestExtraValueTable.value#4 AS vt2 [PROTO] ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#4, $pipe_set.vt1#7] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#4, $pipe_set.vt1#7] + | +-describe_text= + | | """ + | | NameList: + | | vt1 INT64 $pipe_set.vt1#7 + | | vt2 zetasql_test__.TestExtraPB TestExtraValueTable.value#4 (value table) (excluded_field_names vt1) + | | NameScope: + | | Names: + | | Filename -> ambiguous + | | RowId -> ambiguous + | | vt1 -> INT64 ($pipe_set.vt1#7) + | | Range variables: + | | vt2 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#4 (excluded_field_names vt1) + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#4, $pipe_set.vt1#7] + | +-expr_list= + | | +-vt1#7 := Literal(type=INT64, value=0) + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#4] + | +-describe_text= + | | """ + | | NameList: + | | vt1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | | vt2 zetasql_test__.TestExtraPB TestExtraValueTable.value#4 (value table) + | | NameScope: + | | Names: + | | Filename -> ambiguous + | | RowId -> ambiguous + | | Range variables: + | | vt1 -> RANGE_VARIABLE<$value> + | | vt2 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 + | | TestExtraValueTable.value#4 + | | """ + | +-input_scan= + | +-JoinScan + | +-column_list=[TestExtraValueTable.value#4] + | +-left_scan= + | | +-TableScan(table=TestExtraValueTable, alias="vt1") + | +-right_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0], alias="vt2") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#4) + | +-field_descriptor=int32_val1 + | +-default_value=0 + +-Literal(type=INT32, value=1) +== + +# Tricky case where we update a column that is also a field of a value +# table. This adds an exclusion on the value table so the column +# works as expected after SET. Before the SET, reading +# int32_val1 is ambiguous, since it is both a column and a field. +# SELECT * finds the column int32_val1, not the field. +FROM TestExtraValueTable vt1 +|> EXTEND vt1.int32_val1 +|> WHERE {{int32_val1=1|true}} +|> STATIC_DESCRIBE +|> SET int32_val1 = 1.9 +|> STATIC_DESCRIBE +|> WHERE int32_val1 = 1 +|> SELECT * +-- +ALTERNATION GROUP: int32_val1=1 +-- +ERROR: Column name int32_val1 is ambiguous [at 3:10] +|> WHERE int32_val1=1 + ^ +-- +ALTERNATION GROUP: true +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.int32_val2#6 AS int32_val2 [INT32] +| +-$pipe_select.str_value#7 AS str_value [ARRAY] +| +-$pipe_set.int32_val1#5 AS int32_val1 [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.int32_val2#6, $pipe_select.str_value#7, $pipe_set.int32_val1#5] + +-expr_list= + | +-int32_val2#6 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-str_value#7 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4, $pipe_set.int32_val1#5] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4, $pipe_set.int32_val1#5] + | +-describe_text= + | | """ + | | NameList: + | | vt1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) (excluded_field_names int32_val1) + | | int32_val1 DOUBLE $pipe_set.int32_val1#5 + | | NameScope: + | | Names: + | | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | | int32_val1 -> DOUBLE ($pipe_set.int32_val1#5) + | | Range variables: + | | vt1 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 (excluded_field_names int32_val1) + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4, $pipe_set.int32_val1#5] + | +-expr_list= + | | +-int32_val1#5 := Literal(type=DOUBLE, value=1.9) + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | +-describe_text= + | | """ + | | NameList: + | | vt1 zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | | int32_val1 INT32 $pipe_extend.int32_val1#4 + | | NameScope: + | | Names: + | | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | | int32_val1 -> INT32 ($pipe_extend.int32_val1#4) + | | Range variables: + | | vt1 -> RANGE_VARIABLE<$value> + | | Value table columns: + | | TestExtraValueTable.value#1 + | | """ + | +-input_scan= + | +-FilterScan + | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestExtraValueTable.value#1, $pipe_extend.int32_val1#4] + | | +-expr_list= + | | | +-int32_val1#4 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val1 + | | | +-default_value=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt1") + | +-filter_expr= + | +-Literal(type=BOOL, value=true) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(DOUBLE, DOUBLE) -> BOOL) + +-ColumnRef(type=DOUBLE, column=$pipe_set.int32_val1#5) + +-Literal(type=DOUBLE, value=1) +== + +# DEFAULT reserved keyword works in DML UPDATE...SET but not here. +from KeyValue +|> set key = DEFAULT +-- +ERROR: Syntax error: Unexpected keyword DEFAULT [at 2:14] +|> set key = DEFAULT + ^ +== + +from KeyValue +|> set key[0] = 5 +-- +ERROR: Syntax error: Expected "." or "=" but got "[" [at 2:11] +|> set key[0] = 5 + ^ +== + +from KeyValue +|> set key += 5 +-- +ERROR: Syntax error: Expected "." or "=" but got "+=" [at 2:12] +|> set key += 5 + ^ +== + +from KeyValue +|> set key = sum(key) +-- +ERROR: Aggregate function SUM not allowed in pipe SET clause [at 2:14] +|> set key = sum(key) + ^ +== + +from KeyValue +|> set key = avg(sum(key)) OVER () +-- +ERROR: Aggregate function SUM not allowed in pipe SET clause [at 2:18] +|> set key = avg(sum(key)) OVER () + ^ +== + +[language_features=PIPES] +from KeyValue +|> set key = sum(key) OVER () +-- +ERROR: Analytic functions not supported [at 2:14] +|> set key = sum(key) OVER () + ^ +== + +# Replace a column by an window function of itself. +from KeyValue +|> set key = sum(key) OVER () +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.key#4 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.key#3, $pipe_set.key#4] + +-expr_list= + | +-key#4 := ColumnRef(type=INT64, column=$analytic.key#3) + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.key#3] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-key#3 := + +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Replace a column by an window function of a different column. +from KeyValue +|> set value = sum(key) OVER (partition by key) +|> select *, value, KeyValue.value +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-$pipe_set.value#4 AS Value [INT64] +| +-$pipe_set.value#4 AS value [INT64] +| +-KeyValue.Value#2 AS value [STRING] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, $pipe_set.value#4, $pipe_set.value#4, KeyValue.Value#2] + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.value#3, $pipe_set.value#4] + +-expr_list= + | +-value#4 := ColumnRef(type=INT64, column=$analytic.value#3) + +-input_scan= + +-AnalyticScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.value#3] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-function_group_list= + +-AnalyticFunctionGroup + +-partition_by= + | +-WindowPartitioning + | +-partition_by_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-analytic_function_list= + +-value#3 := + +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Window functions with expressions before and after the window function. +select 1 x, 2 y, 3 z, 4 value +|> set value = x+sum(y+1) OVER (partition by x+2 order by y+3), + z = sum(z) OVER (), + y = y + 1, + x = sum(y) OVER () + sum(z) OVER () +-- +QueryStmt ++-output_column_list= +| +-$pipe_set.x#12 AS x [INT64] +| +-$pipe_set.y#9 AS y [INT64] +| +-$pipe_set.z#8 AS z [INT64] +| +-$pipe_set.value#6 AS value [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $query.value#4, $analytic.$analytic1#5, $analytic.z#7, $analytic.$analytic3#10, $analytic.$analytic4#11, $pipe_set.value#6, $pipe_set.z#8, $pipe_set.y#9, $pipe_set.x#12] + +-expr_list= + | +-value#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.x#1) + | | +-ColumnRef(type=INT64, column=$analytic.$analytic1#5) + | +-z#8 := ColumnRef(type=INT64, column=$analytic.z#7) + | +-y#9 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.y#2) + | | +-Literal(type=INT64, value=1) + | +-x#12 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$analytic.$analytic3#10) + | +-ColumnRef(type=INT64, column=$analytic.$analytic4#11) + +-input_scan= + +-AnalyticScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $query.value#4, $analytic.$analytic1#5, $analytic.z#7, $analytic.$analytic3#10, $analytic.$analytic4#11] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1, $query.y#2, $query.z#3, $query.value#4, $partitionby.$partitionbycol1#13, $orderby.$orderbycol1#14] + | +-expr_list= + | | +-$partitionbycol1#13 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=$query.x#1) + | | | +-Literal(type=INT64, value=2) + | | +-$orderbycol1#14 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=$query.y#2) + | | +-Literal(type=INT64, value=3) + | +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3, value#4] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | | +-value#4 := Literal(type=INT64, value=4) + | +-input_scan= + | +-SingleRowScan + +-function_group_list= + +-AnalyticFunctionGroup + | +-partition_by= + | | +-WindowPartitioning + | | +-partition_by_list= + | | +-ColumnRef(type=INT64, column=$partitionby.$partitionbycol1#13) + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#14) + | +-analytic_function_list= + | +-$analytic1#5 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-Literal(type=INT64, value=1) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-AnalyticFunctionGroup + +-analytic_function_list= + +-z#7 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.z#3) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-$analytic3#10 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-$analytic4#11 := + +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-ColumnRef(type=INT64, column=$query.z#3) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Columns computed in SET with window function that are never referenced. +from KeyValue +|> set value = count(*) over () +|> select 123 +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.$col1#5 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.$col1#5] + +-expr_list= + | +-$col1#5 := Literal(type=INT64, value=123) + +-input_scan= + +-ProjectScan + +-column_list=[$analytic.value#3, $pipe_set.value#4] + +-expr_list= + | +-value#4 := ColumnRef(type=INT64, column=$analytic.value#3) + +-input_scan= + +-AnalyticScan + +-column_list=[$analytic.value#3] + +-input_scan= + | +-TableScan(table=KeyValue) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-value#3 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +from KeyValue +|> set value = sum(key) OVER w +-- +ERROR: Cannot reference a named window in pipe SET clause [at 2:30] +|> set value = sum(key) OVER w + ^ +== + +# Try to use SET to update an outer correlated column. +from KeyValue +|> extend ( + select 1 x + |> SET key = 5 + ) +-- +ERROR: Name in pipe SET is present but is not a column on the pipe input table; SET can only update columns: key [at 4:13] + |> SET key = 5 + ^ +== + +# Try to use SET to update a correlated name that is ambiguous. +from KeyValue +|> extend key +|> extend ( + select 1 x + |> SET key = 5 + ) +-- +ERROR: Name in pipe SET is ambiguous; SET can only update columns: key [at 5:13] + |> SET key = 5 + ^ +== + +# Try to use SET to update a constant. +from KeyValue +|> SET key = TestConstantProto +|> SET TestConstantProto = 5 +-- +ERROR: Column name in pipe SET not found in input table: TestConstantProto [at 3:8] +|> SET TestConstantProto = 5 + ^ +== + +# If collation is enabled, collation propagates to output columns the same way +# for SELECT and SET. +# TODO Enable java support for collation. +[no_java] +[language_features=PIPES{{|,V_1_3_ANNOTATION_FRAMEWORK|,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT}}] +from CollatedTable +|> select string_ci, concat(string_ci, "xxx") as newcol1, + null as newcol2 +|> SET newcol2=concat(string_ci, "yyy") +-- +ALTERNATION GROUPS: + + ,V_1_3_ANNOTATION_FRAMEWORK +-- +QueryStmt ++-output_column_list= +| +-CollatedTable.string_ci#1{Collation:"und:ci"} AS string_ci [STRING] +| +-$pipe_select.newcol1#5 AS newcol1 [STRING] +| +-$pipe_set.newcol2#7 AS newcol2 [STRING] ++-query= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5, $pipe_select.newcol2#6, $pipe_set.newcol2#7] + +-expr_list= + | +-newcol2#7 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | +-Literal(type=STRING, value="yyy") + +-input_scan= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5, $pipe_select.newcol2#6] + +-expr_list= + | +-newcol1#5 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | | +-Literal(type=STRING, value="xxx") + | +-newcol2#6 := Literal(type=INT64, value=NULL) + +-input_scan= + +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) +-- +ALTERNATION GROUP: ,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT +-- +QueryStmt ++-output_column_list= +| +-CollatedTable.string_ci#1{Collation:"und:ci"} AS string_ci [STRING] +| +-$pipe_select.newcol1#5{Collation:"und:ci"} AS newcol1 [STRING] +| +-$pipe_set.newcol2#7{Collation:"und:ci"} AS newcol2 [STRING] ++-query= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5{Collation:"und:ci"}, $pipe_select.newcol2#6, $pipe_set.newcol2#7{Collation:"und:ci"}] + +-expr_list= + | +-newcol2#7 := + | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | +-type_annotation_map={Collation:"und:ci"} + | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | +-Literal(type=STRING, value="yyy") + +-input_scan= + +-ProjectScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $pipe_select.newcol1#5{Collation:"und:ci"}, $pipe_select.newcol2#6] + +-expr_list= + | +-newcol1#5 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-type_annotation_map={Collation:"und:ci"} + | | +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + | | +-Literal(type=STRING, value="xxx") + | +-newcol2#6 := Literal(type=INT64, value=NULL) + +-input_scan= + +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) diff --git a/zetasql/analyzer/testdata/pipe_set_operation.test b/zetasql/analyzer/testdata/pipe_set_operation.test new file mode 100644 index 000000000..7934c4faf --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_set_operation.test @@ -0,0 +1,385 @@ +# Pipe set operations aren't supported yet, but this has some tests for +# pipe operators after set operations. +[default language_features=PIPES] + +# Test UNION ALL producing a value table. +# The result works as a value table, but pseudo-columns are still dropped. +(from TestExtraValueTable vt) +UNION ALL +(from TestExtraValueTable) +|> {{limit 1|where str_value is null|SELECT vt, vt.str_value, *|where filename = ''}} +-- +ALTERNATION GROUP: limit 1 +-- +QueryStmt ++-output_column_list= +| +-$union_all.vt#7 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-LimitOffsetScan + +-column_list=[$union_all.vt#7] + +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.vt#7] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + | | +-output_column_list=[TestExtraValueTable.value#1] + | +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + | +-output_column_list=[TestExtraValueTable.value#4] + +-limit= + +-Literal(type=INT64, value=1) +-- +ALTERNATION GROUP: where str_value is null +-- +QueryStmt ++-output_column_list= +| +-$union_all.vt#7 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[$union_all.vt#7] + +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.vt#7] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + | | +-output_column_list=[TestExtraValueTable.value#1] + | +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + | +-output_column_list=[TestExtraValueTable.value#4] + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + +-GetProtoField + +-type=ARRAY + +-expr= + | +-ColumnRef(type=PROTO, column=$union_all.vt#7) + +-field_descriptor=str_value + +-default_value=[] +-- +ALTERNATION GROUP: SELECT vt, vt.str_value, * +-- +QueryStmt ++-output_column_list= +| +-$union_all.vt#7 AS vt [PROTO] +| +-$pipe_select.str_value#8 AS str_value [ARRAY] +| +-$pipe_select.int32_val1#9 AS int32_val1 [INT32] +| +-$pipe_select.int32_val2#10 AS int32_val2 [INT32] +| +-$pipe_select.str_value#11 AS str_value [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$union_all.vt#7, $pipe_select.str_value#8, $pipe_select.int32_val1#9, $pipe_select.int32_val2#10, $pipe_select.str_value#11] + +-expr_list= + | +-str_value#8 := + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$union_all.vt#7) + | | +-field_descriptor=str_value + | | +-default_value=[] + | +-int32_val1#9 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$union_all.vt#7) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-int32_val2#10 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$union_all.vt#7) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-str_value#11 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=$union_all.vt#7) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-SetOperationScan + +-column_list=[$union_all.vt#7] + +-op_type=UNION_ALL + +-input_item_list= + +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + | +-output_column_list=[TestExtraValueTable.value#1] + +-SetOperationItem + +-scan= + | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + +-output_column_list=[TestExtraValueTable.value#4] +-- +ALTERNATION GROUP: where filename = '' +-- +ERROR: Unrecognized name: filename [at 6:10] +|> where filename = '' + ^ +== + +# A set operation on value tables acts as value table when used as a table +# subquery. +from ( + (from TestExtraValueTable vt) + UNION ALL + (from TestExtraValueTable) +) +|> where {{str_value|vt.str_value}} is null +-- +QueryStmt ++-output_column_list= +| +-$union_all.vt#7 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[$union_all.vt#7] + +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.vt#7] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + | | +-output_column_list=[TestExtraValueTable.value#1] + | +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + | +-output_column_list=[TestExtraValueTable.value#4] + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + +-GetProtoField + +-type=ARRAY + +-expr= + | +-ColumnRef(type=PROTO, column=$union_all.vt#7) + +-field_descriptor=str_value + +-default_value=[] +== + +# A set operation value table acts as value table when used as a table subquery, +# with an alias on the subquery. +[language_features=PIPES,V_1_2_GROUP_BY_STRUCT] +from ( + select AS STRUCT 1 a, 2 b + intersect all + select AS STRUCT 3 c, 4 d +) as input +|> where b = 2 +|> where input.b = 2 +-- +QueryStmt ++-output_column_list= +| +-$intersect_all.$struct#7 AS `$value` [STRUCT] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[$intersect_all.$struct#7] + +-input_scan= + | +-FilterScan + | +-column_list=[$intersect_all.$struct#7] + | +-input_scan= + | | +-SetOperationScan + | | +-column_list=[$intersect_all.$struct#7] + | | +-op_type=INTERSECT_ALL + | | +-input_item_list= + | | +-SetOperationItem + | | | +-scan= + | | | | +-ProjectScan + | | | | +-column_list=[$make_struct.$struct#3] + | | | | +-expr_list= + | | | | | +-$struct#3 := + | | | | | +-MakeStruct + | | | | | +-type=STRUCT + | | | | | +-field_list= + | | | | | +-ColumnRef(type=INT64, column=$intersect_all1.a#1) + | | | | | +-ColumnRef(type=INT64, column=$intersect_all1.b#2) + | | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=$intersect_all1.[a#1, b#2] + | | | | +-expr_list= + | | | | | +-a#1 := Literal(type=INT64, value=1) + | | | | | +-b#2 := Literal(type=INT64, value=2) + | | | | +-input_scan= + | | | | +-SingleRowScan + | | | +-output_column_list=[$make_struct.$struct#3] + | | +-SetOperationItem + | | +-scan= + | | | +-ProjectScan + | | | +-column_list=[$intersect_all2_cast.$struct#8] + | | | +-expr_list= + | | | | +-$struct#8 := + | | | | +-Cast(STRUCT -> STRUCT) + | | | | +-ColumnRef(type=STRUCT, column=$make_struct.$struct#6) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[$make_struct.$struct#6] + | | | +-expr_list= + | | | | +-$struct#6 := + | | | | +-MakeStruct + | | | | +-type=STRUCT + | | | | +-field_list= + | | | | +-ColumnRef(type=INT64, column=$intersect_all2.c#4) + | | | | +-ColumnRef(type=INT64, column=$intersect_all2.d#5) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$intersect_all2.[c#4, d#5] + | | | +-expr_list= + | | | | +-c#4 := Literal(type=INT64, value=3) + | | | | +-d#5 := Literal(type=INT64, value=4) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-output_column_list=[$intersect_all2_cast.$struct#8] + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$intersect_all.$struct#7) + | | +-field_idx=1 + | +-Literal(type=INT64, value=2) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$intersect_all.$struct#7) + | +-field_idx=1 + +-Literal(type=INT64, value=2) +== + +# This is similar to the table subquery case, but written as a parenthesized set +# operation producing a value table, without a table subquery. +( + (from TestExtraValueTable) + union all + (from TestExtraValueTable) +) +|> {{where str_value is null|where true|select *}} +-- +ALTERNATION GROUP: where str_value is null +-- +QueryStmt ++-output_column_list= +| +-$union_all.TestExtraValueTable#7 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[$union_all.TestExtraValueTable#7] + +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.TestExtraValueTable#7] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | | +-output_column_list=[TestExtraValueTable.value#1] + | +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + | +-output_column_list=[TestExtraValueTable.value#4] + +-filter_expr= + +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + +-GetProtoField + +-type=ARRAY + +-expr= + | +-ColumnRef(type=PROTO, column=$union_all.TestExtraValueTable#7) + +-field_descriptor=str_value + +-default_value=[] +-- +ALTERNATION GROUP: where true +-- +QueryStmt ++-output_column_list= +| +-$union_all.TestExtraValueTable#7 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[$union_all.TestExtraValueTable#7] + +-input_scan= + | +-SetOperationScan + | +-column_list=[$union_all.TestExtraValueTable#7] + | +-op_type=UNION_ALL + | +-input_item_list= + | +-SetOperationItem + | | +-scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | | +-output_column_list=[TestExtraValueTable.value#1] + | +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + | +-output_column_list=[TestExtraValueTable.value#4] + +-filter_expr= + +-Literal(type=BOOL, value=true) +-- +ALTERNATION GROUP: select * +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.int32_val1#8 AS int32_val1 [INT32] +| +-$pipe_select.int32_val2#9 AS int32_val2 [INT32] +| +-$pipe_select.str_value#10 AS str_value [ARRAY] ++-query= + +-ProjectScan + +-column_list=$pipe_select.[int32_val1#8, int32_val2#9, str_value#10] + +-expr_list= + | +-int32_val1#8 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$union_all.TestExtraValueTable#7) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-int32_val2#9 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=$union_all.TestExtraValueTable#7) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-str_value#10 := + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=$union_all.TestExtraValueTable#7) + | +-field_descriptor=str_value + | +-default_value=[] + +-input_scan= + +-SetOperationScan + +-column_list=[$union_all.TestExtraValueTable#7] + +-op_type=UNION_ALL + +-input_item_list= + +-SetOperationItem + | +-scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | +-output_column_list=[TestExtraValueTable.value#1] + +-SetOperationItem + +-scan= + | +-TableScan(column_list=[TestExtraValueTable.value#4], table=TestExtraValueTable, column_index_list=[0]) + +-output_column_list=[TestExtraValueTable.value#4] +== + +from TestTable +|> union all (from TestTable) +-- +ERROR: Pipe set operations not supported yet; Try using standard set operator syntax between parenthesized pipe queries [at 2:1] +|> union all (from TestTable) +^ +== + +(from TestTable) +|> intersect all (from TestTable) +-- +ERROR: Pipe set operations not supported yet; Try using standard set operator syntax between parenthesized pipe queries [at 2:1] +|> intersect all (from TestTable) +^ diff --git a/zetasql/analyzer/testdata/pipe_static_describe.test b/zetasql/analyzer/testdata/pipe_static_describe.test new file mode 100644 index 000000000..fb63b81bd --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_static_describe.test @@ -0,0 +1,462 @@ +[language_features=PIPE_STATIC_DESCRIBE] +select * from KeyValue +|> STATIC_DESCRIBE +-- +ERROR: Pipe query syntax not supported [at 2:1] +|> STATIC_DESCRIBE +^ +== + +[language_features=PIPES] +from KeyValue +|> STATIC_DESCRIBE +-- +ERROR: Pipe STATIC_DESCRIBE not supported [at 2:1] +|> STATIC_DESCRIBE +^ +== + +[default language_features=PIPES,PIPE_STATIC_DESCRIBE] +from KeyValue +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | """ + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +# STATIC_DESCRIBE is a no-op that shows the NameList state, without changing it. +# Note that `Value` still gets pruned out because it's not accessed, even though +# it's shown in the NameList. +from KeyValue +|> STATIC_DESCRIBE +|> STATIC_DESCRIBE +|> where key=1 +|> select KeyValue.key +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS key [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1] + +-input_scan= + +-FilterScan + +-column_list=[KeyValue.Key#1] + +-input_scan= + | +-StaticDescribeScan + | +-column_list=[KeyValue.Key#1] + | +-describe_text= + | | """ + | | NameList: + | | Key INT64 KeyValue.Key#1 + | | Value STRING KeyValue.Value#2 + | | NameScope: + | | Names: + | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | Value -> STRING (KeyValue.Value#2) (implicit) + | | Range variables: + | | KeyValue -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[KeyValue.Key#1] + | +-describe_text= + | | """ + | | NameList: + | | Key INT64 KeyValue.Key#1 + | | Value STRING KeyValue.Value#2 + | | NameScope: + | | Names: + | | Key -> INT64 (KeyValue.Key#1) (implicit) + | | Value -> STRING (KeyValue.Value#2) (implicit) + | | Range variables: + | | KeyValue -> RANGE_VARIABLE + | | """ + | +-input_scan= + | +-TableScan(column_list=[KeyValue.Key#1], table=KeyValue, column_index_list=[0]) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=INT64, value=1) +== + +# Describe a value table. +FROM TestExtraValueTable +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1] + +-describe_text= + | """ + | NameList (is_value_table = true): + | TestExtraValueTable zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Range variables: + | TestExtraValueTable -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +== + +FROM TestExtraValueTable vt CROSS JOIN KeyValue kv +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS vt [PROTO] +| +-KeyValue.Key#4 AS Key [INT64] +| +-KeyValue.Value#5 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=[TestExtraValueTable.value#1, KeyValue.Key#4, KeyValue.Value#5] + +-describe_text= + | """ + | NameList: + | vt zetasql_test__.TestExtraPB TestExtraValueTable.value#1 (value table) + | Key INT64 KeyValue.Key#4 + | Value STRING KeyValue.Value#5 + | NameScope: + | Names: + | Filename -> STRING (TestExtraValueTable.Filename#2) (implicit) (pseudo-column) + | Key -> INT64 (KeyValue.Key#4) (implicit) + | RowId -> BYTES (TestExtraValueTable.RowId#3) (implicit) (pseudo-column) + | Value -> STRING (KeyValue.Value#5) (implicit) + | Range variables: + | kv -> RANGE_VARIABLE + | vt -> RANGE_VARIABLE<$value> + | Value table columns: + | TestExtraValueTable.value#1 + | """ + +-input_scan= + +-JoinScan + +-column_list=[TestExtraValueTable.value#1, KeyValue.Key#4, KeyValue.Value#5] + +-left_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="vt") + +-right_scan= + +-TableScan(column_list=KeyValue.[Key#4, Value#5], table=KeyValue, column_index_list=[0, 1], alias="kv") +== + +# Describe a FULL JOIN USING, which has a special column setup. +FROM KeyValue kv1 FULL JOIN KeyValue kv2 USING (key) +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-$full_join.key#5 AS key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-KeyValue.Value#4 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, KeyValue.Key#3, KeyValue.Value#4, $full_join.key#5] + +-describe_text= + | """ + | NameList: + | key INT64 $full_join.key#5 + | Value STRING KeyValue.Value#2 + | Value STRING KeyValue.Value#4 + | NameScope: + | Names: + | Value -> ambiguous + | key -> INT64 ($full_join.key#5) + | Range variables: + | kv1 -> RANGE_VARIABLE + | kv2 -> RANGE_VARIABLE + | """ + +-input_scan= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, KeyValue.Key#3, KeyValue.Value#4, $full_join.key#5] + +-expr_list= + | +-key#5 := + | +-FunctionCall(ZetaSQL:coalesce(repeated(2) INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-input_scan= + +-JoinScan + +-column_list=KeyValue.[Key#1, Value#2, Key#3, Value#4] + +-join_type=FULL + +-left_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv1") + +-right_scan= + | +-TableScan(column_list=KeyValue.[Key#3, Value#4], table=KeyValue, column_index_list=[0, 1], alias="kv2") + +-join_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-ColumnRef(type=INT64, column=KeyValue.Key#3) + +-has_using=TRUE +== + +# Running after a standard query. +select * from KeyValue +where key = 10 +order by key +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | """ + +-input_scan= + +-OrderByScan + +-column_list=KeyValue.[Key#1, Value#2] + +-is_ordered=TRUE + +-input_scan= + | +-FilterScan + | +-column_list=KeyValue.[Key#1, Value#2] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=10) + +-order_by_item_list= + +-OrderByItem + +-parse_location=47-50 + +-column_ref= + +-ColumnRef(type=INT64, column=KeyValue.Key#1) +== + +# A case with an empty NameScope. +select 1, 2, 3 +|> STATIC_DESCRIBE +|> AS abc +|> STATIC_DESCRIBE +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#1 AS `$col1` [INT64] +| +-$query.$col2#2 AS `$col2` [INT64] +| +-$query.$col3#3 AS `$col3` [INT64] ++-query= + +-StaticDescribeScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3] + +-describe_text= + | """ + | NameList: + | INT64 $query.$col1#1 + | INT64 $query.$col2#2 + | INT64 $query.$col3#3 + | NameScope: + | Range variables: + | abc -> RANGE_VARIABLE<$col1,$col2,$col3> + | """ + +-input_scan= + +-StaticDescribeScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3] + +-describe_text= + | """ + | NameList: + | INT64 $query.$col1#1 + | INT64 $query.$col2#2 + | INT64 $query.$col3#3 + | NameScope: + | """ + +-input_scan= + +-ProjectScan + +-column_list=$query.[$col1#1, $col2#2, $col3#3] + +-expr_list= + | +-$col1#1 := Literal(type=INT64, value=1) + | +-$col2#2 := Literal(type=INT64, value=2) + | +-$col3#3 := Literal(type=INT64, value=3) + +-input_scan= + +-SingleRowScan +== + +# A case with a nested scope. +# The outer scopes aren't part of a table's NameList, so +# they won't show up here. +from KeyValue +|> select (select key |> STATIC_DESCRIBE + |> extend value + |> extend (from KeyValue kv2 |> STATIC_DESCRIBE + |> select kv2) AS subq + |> STATIC_DESCRIBE + |> select as struct *) +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.$col1#11 AS `$col1` [STRUCT>] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.$col1#11] + +-expr_list= + | +-$col1#11 := + | +-SubqueryExpr + | +-type=STRUCT> + | +-subquery_type=SCALAR + | +-parameter_list= + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-subquery= + | +-ProjectScan + | +-column_list=[$make_struct.$struct#10] + | +-expr_list= + | | +-$struct#10 := + | | +-MakeStruct + | | +-type=STRUCT> + | | +-field_list= + | | +-ColumnRef(type=INT64, column=$expr_subquery.key#3) + | | +-ColumnRef(type=STRING, column=$pipe_extend.value#4) + | | +-ColumnRef(type=STRUCT, column=$pipe_extend.subq#9) + | +-input_scan= + | +-ProjectScan + | +-column_list=[$expr_subquery.key#3, $pipe_extend.value#4, $pipe_extend.subq#9] + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[$expr_subquery.key#3, $pipe_extend.value#4, $pipe_extend.subq#9] + | +-describe_text= + | | """ + | | NameList: + | | key INT64 $expr_subquery.key#3 + | | value STRING $pipe_extend.value#4 + | | subq STRUCT $pipe_extend.subq#9 + | | NameScope: + | | Names: + | | key -> INT64 ($expr_subquery.key#3) + | | subq -> STRUCT ($pipe_extend.subq#9) + | | value -> STRING ($pipe_extend.value#4) + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[$expr_subquery.key#3, $pipe_extend.value#4, $pipe_extend.subq#9] + | +-expr_list= + | | +-subq#9 := + | | +-SubqueryExpr + | | +-type=STRUCT + | | +-subquery_type=SCALAR + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$pipe_select.kv2#8] + | | +-expr_list= + | | | +-kv2#8 := + | | | +-MakeStruct + | | | +-type=STRUCT + | | | +-field_list= + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#5) + | | | +-ColumnRef(type=STRING, column=KeyValue.Value#6) + | | +-input_scan= + | | +-StaticDescribeScan + | | +-column_list=KeyValue.[Key#5, Value#6] + | | +-describe_text= + | | | """ + | | | NameList: + | | | Key INT64 KeyValue.Key#5 + | | | Value STRING KeyValue.Value#6 + | | | NameScope: + | | | Names: + | | | Key -> INT64 (KeyValue.Key#5) (implicit) + | | | Value -> STRING (KeyValue.Value#6) (implicit) + | | | Range variables: + | | | kv2 -> RANGE_VARIABLE + | | | """ + | | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#5, Value#6], table=KeyValue, column_index_list=[0, 1], alias="kv2") + | +-input_scan= + | +-ProjectScan + | +-column_list=[$expr_subquery.key#3, $pipe_extend.value#4] + | +-expr_list= + | | +-value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2, is_correlated=TRUE) + | +-input_scan= + | +-StaticDescribeScan + | +-column_list=[$expr_subquery.key#3] + | +-describe_text= + | | """ + | | NameList: + | | key INT64 $expr_subquery.key#3 + | | NameScope: + | | Names: + | | key -> INT64 ($expr_subquery.key#3) + | | """ + | +-input_scan= + | +-ProjectScan + | +-column_list=[$expr_subquery.key#3] + | +-expr_list= + | | +-key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1, is_correlated=TRUE) + | +-input_scan= + | +-SingleRowScan + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) +== + +# TODO SQLBuilder currently loses the STATIC_DESCRIBE, since it can't +# generate pipe operators yet. +[show_unparsed] +from KeyValue +|> static_describe +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-StaticDescribeScan + +-column_list=KeyValue.[Key#1, Value#2] + +-describe_text= + | """ + | NameList: + | Key INT64 KeyValue.Key#1 + | Value STRING KeyValue.Value#2 + | NameScope: + | Names: + | Key -> INT64 (KeyValue.Key#1) (implicit) + | Value -> STRING (KeyValue.Value#2) (implicit) + | Range variables: + | KeyValue -> RANGE_VARIABLE + | """ + +-input_scan= + +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +[UNPARSED_SQL] +SELECT + keyvalue_3.a_1 AS Key, + keyvalue_3.a_2 AS Value +FROM + ( + SELECT + KeyValue.Key AS a_1, + KeyValue.Value AS a_2 + FROM + KeyValue + ) AS keyvalue_3; diff --git a/zetasql/analyzer/testdata/pipe_tablesample.test b/zetasql/analyzer/testdata/pipe_tablesample.test new file mode 100644 index 000000000..4872a8c19 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_tablesample.test @@ -0,0 +1,541 @@ +# Tests for the pipe TABLESAMPLE operator. +# This isn't testing all the error cases, which are covered +# in tablesample.test. +# It's just testing that the pipe operator works, and that +# name scoping on pipe inputs works as expected. + +[language_features=PIPES] +from TestTable +|> TABLESAMPLE BERNOULLI (10 PERCENT) +-- +ERROR: TABLESAMPLE not supported [at 2:4] +|> TABLESAMPLE BERNOULLI (10 PERCENT) + ^ +== + +[default language_features=PIPES,TABLESAMPLE,STRATIFIED_RESERVOIR_TABLESAMPLE] +from TestTable +|> TABLESAMPLE BERNOULLI (10.5 PERCENT) +|> TABLESAMPLE RESERVOIR (10 ROWS) +|> TABLESAMPLE SYSTEM (10 ROWS) REPEATABLE(50) +|> TABLESAMPLE xxxxx (@test_param_int32 ROWS) REPEATABLE(@test_param_int32) +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] ++-query= + +-SampleScan + +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + +-input_scan= + | +-SampleScan + | +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + | +-input_scan= + | | +-SampleScan + | | +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + | | +-input_scan= + | | | +-SampleScan + | | | +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + | | | +-input_scan= + | | | | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + | | | +-method="bernoulli" + | | | +-size= + | | | | +-Literal(type=DOUBLE, value=10.5) + | | | +-unit=PERCENT + | | +-method="reservoir" + | | +-size= + | | | +-Literal(type=INT64, value=10) + | | +-unit=ROWS + | +-method="system" + | +-size= + | | +-Literal(type=INT64, value=10) + | +-unit=ROWS + | +-repeatable_argument= + | +-Literal(type=INT64, value=50) + +-method="xxxxx" + +-size= + | +-Cast(INT32 -> INT64) + | +-Parameter(type=INT32, name="test_param_int32") + +-unit=ROWS + +-repeatable_argument= + +-Cast(INT32 -> INT64) + +-Parameter(type=INT32, name="test_param_int32") +== + +FROM TestTable +|> TABLESAMPLE RESERVOIR (10 ROWS PARTITION BY key) +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] ++-query= + +-SampleScan + +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3] + +-input_scan= + | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2]) + +-method="reservoir" + +-size= + | +-Literal(type=INT64, value=10) + +-unit=ROWS + +-partition_by_list= + +-ColumnRef(type=INT32, column=TestTable.key#1) +== + +# This adds a column `w`. +# The psuedo-column `filename` also survives. +FROM EnumTable +|> TABLESAMPLE RESERVOIR (100 ROWS) WITH WEIGHT w +|> WHERE w >= 10.0 +|> WHERE key = 5 +|> WHERE filename = 'abc' +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-$sample_weight.w#6 AS w [DOUBLE] ++-query= + +-FilterScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $sample_weight.w#6] + +-input_scan= + | +-FilterScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $sample_weight.w#6] + | +-input_scan= + | | +-FilterScan + | | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $sample_weight.w#6] + | | +-input_scan= + | | | +-SampleScan + | | | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $sample_weight.w#6] + | | | +-input_scan= + | | | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4], table=EnumTable, column_index_list=[0, 1, 2, 3]) + | | | +-method="reservoir" + | | | +-size= + | | | | +-Literal(type=INT64, value=100) + | | | +-unit=ROWS + | | | +-weight_column= + | | | +-ColumnHolder(column=$sample_weight.w#6) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$greater_or_equal(DOUBLE, DOUBLE) -> BOOL) + | | +-ColumnRef(type=DOUBLE, column=$sample_weight.w#6) + | | +-Literal(type=DOUBLE, value=10) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=EnumTable.key#1) + | +-Literal(type=INT32, value=5) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=EnumTable.Filename#4) + +-Literal(type=STRING, value="abc") +== + +# Value table makes it through, with its pseudo-column `filename`. +FROM TestExtraValueTable vt +|> TABLESAMPLE RESERVOIR (12 ROWS PARTITION BY int32_val1, vt.int32_val2) +|> where str_value is not null +|> where vt.str_value is not null +|> where filename = 'abc' +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=TestExtraValueTable.[value#1, Filename#2] + +-input_scan= + | +-FilterScan + | +-column_list=TestExtraValueTable.[value#1, Filename#2] + | +-input_scan= + | | +-FilterScan + | | +-column_list=TestExtraValueTable.[value#1, Filename#2] + | | +-input_scan= + | | | +-SampleScan + | | | +-column_list=TestExtraValueTable.[value#1, Filename#2] + | | | +-input_scan= + | | | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="vt") + | | | +-method="reservoir" + | | | +-size= + | | | | +-Literal(type=INT64, value=12) + | | | +-unit=ROWS + | | | +-partition_by_list= + | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val2 + | | | +-default_value=0 + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=str_value + | | +-default_value=[] + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | +-GetProtoField + | +-type=ARRAY + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=str_value + | +-default_value=[] + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + +-Literal(type=STRING, value="abc") +== + +# When we add the `weight` column, the output is no longer a value table, but +# it still contains the value table. +FROM TestExtraValueTable vt +|> TABLESAMPLE BERNOULLI (12 PERCENT) WITH WEIGHT +|> where str_value is not null +|> where vt.str_value is not null +|> where filename = 'abc' +|> where weight > 1.5 +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS vt [PROTO] +| +-$sample_weight.weight#4 AS weight [DOUBLE] ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $sample_weight.weight#4] + +-input_scan= + | +-FilterScan + | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $sample_weight.weight#4] + | +-input_scan= + | | +-FilterScan + | | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $sample_weight.weight#4] + | | +-input_scan= + | | | +-FilterScan + | | | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $sample_weight.weight#4] + | | | +-input_scan= + | | | | +-SampleScan + | | | | +-column_list=[TestExtraValueTable.value#1, TestExtraValueTable.Filename#2, $sample_weight.weight#4] + | | | | +-input_scan= + | | | | | +-TableScan(column_list=TestExtraValueTable.[value#1, Filename#2], table=TestExtraValueTable, column_index_list=[0, 1], alias="vt") + | | | | +-method="bernoulli" + | | | | +-size= + | | | | | +-Literal(type=INT64, value=12) + | | | | +-unit=PERCENT + | | | | +-weight_column= + | | | | +-ColumnHolder(column=$sample_weight.weight#4) + | | | +-filter_expr= + | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=str_value + | | | +-default_value=[] + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | +-FunctionCall(ZetaSQL:$is_null(ARRAY) -> BOOL) + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=str_value + | | +-default_value=[] + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=TestExtraValueTable.Filename#2) + | +-Literal(type=STRING, value="abc") + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(DOUBLE, DOUBLE) -> BOOL) + +-ColumnRef(type=DOUBLE, column=$sample_weight.weight#4) + +-Literal(type=DOUBLE, value=1.5) +== + +# Range variables make it through TABLESAMPLE. +FROM TestTable t1 JOIN TestTable t2 USING (key) +|> TABLESAMPLE BERNOULLI (10 ROWS) WITH WEIGHT ww +|> SELECT key, t1.key, t2.key, t1, t2.*, ww, * +-- +QueryStmt ++-output_column_list= +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.key#4 AS key [INT32] +| +-$pipe_select.t1#9 AS t1 [STRUCT, KitchenSink PROTO>] +| +-TestTable.key#4 AS key [INT32] +| +-TestTable.TestEnum#5 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#6 AS KitchenSink [PROTO] +| +-$sample_weight.ww#7 AS ww [DOUBLE] +| +-TestTable.key#1 AS key [INT32] +| +-TestTable.TestEnum#2 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#3 AS KitchenSink [PROTO] +| +-TestTable.TestEnum#5 AS TestEnum [ENUM] +| +-TestTable.KitchenSink#6 AS KitchenSink [PROTO] +| +-$sample_weight.ww#7 AS ww [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[TestTable.key#1, TestTable.key#1, TestTable.key#4, $pipe_select.t1#9, TestTable.key#4, TestTable.TestEnum#5, TestTable.KitchenSink#6, $sample_weight.ww#7, TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, TestTable.TestEnum#5, TestTable.KitchenSink#6, $sample_weight.ww#7] + +-expr_list= + | +-t1#9 := + | +-MakeStruct + | +-type=STRUCT, KitchenSink PROTO> + | +-field_list= + | +-ColumnRef(type=INT32, column=TestTable.key#1) + | +-ColumnRef(type=ENUM, column=TestTable.TestEnum#2) + | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + +-input_scan= + +-SampleScan + +-column_list=[TestTable.key#1, TestTable.TestEnum#2, TestTable.KitchenSink#3, TestTable.key#4, TestTable.TestEnum#5, TestTable.KitchenSink#6, $sample_weight.ww#7] + +-input_scan= + | +-JoinScan + | +-column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3, key#4, TestEnum#5, KitchenSink#6] + | +-left_scan= + | | +-TableScan(column_list=TestTable.[key#1, TestEnum#2, KitchenSink#3], table=TestTable, column_index_list=[0, 1, 2], alias="t1") + | +-right_scan= + | | +-TableScan(column_list=TestTable.[key#4, TestEnum#5, KitchenSink#6], table=TestTable, column_index_list=[0, 1, 2], alias="t2") + | +-join_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | | +-ColumnRef(type=INT32, column=TestTable.key#1) + | | +-ColumnRef(type=INT32, column=TestTable.key#4) + | +-has_using=TRUE + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=10) + +-unit=ROWS + +-weight_column= + +-ColumnHolder(column=$sample_weight.ww#7) +== + +# Test for SQLBuilder, of a TABLESAMPLE operator after unusual preceding scans. +[show_unparsed] +FROM TestTable +|> AGGREGATE COUNT(*) c GROUP BY key +|> {{LIMIT 10|EXTEND 123}} +|> TABLESAMPLE BERNOULLI (10 ROWS){{| WITH WEIGHT ww}} +-- +ALTERNATION GROUP: LIMIT 10, +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#5 AS key [INT32] +| +-$aggregate.c#4 AS c [INT64] ++-query= + +-SampleScan + +-column_list=[$groupby.key#5, $aggregate.c#4] + +-input_scan= + | +-LimitOffsetScan + | +-column_list=[$groupby.key#5, $aggregate.c#4] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.key#5, $aggregate.c#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + | | +-group_by_list= + | | | +-key#5 := ColumnRef(type=INT32, column=TestTable.key#1) + | | +-aggregate_list= + | | +-c#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + | +-limit= + | +-Literal(type=INT64, value=10) + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=10) + +-unit=ROWS + +[UNPARSED_SQL] +SELECT + limitoffsetscan_5.a_3 AS key, + limitoffsetscan_5.a_4 AS c +FROM + ( + SELECT + testtable_2.a_1 AS a_3, + COUNT(*) AS a_4 + FROM + ( + SELECT + TestTable.key AS a_1 + FROM + TestTable + ) AS testtable_2 + GROUP BY 1 + LIMIT 10 + ) AS limitoffsetscan_5 TABLESAMPLE bernoulli(10 ROWS); +-- +ALTERNATION GROUP: LIMIT 10, WITH WEIGHT ww +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#5 AS key [INT32] +| +-$aggregate.c#4 AS c [INT64] +| +-$sample_weight.ww#6 AS ww [DOUBLE] ++-query= + +-SampleScan + +-column_list=[$groupby.key#5, $aggregate.c#4, $sample_weight.ww#6] + +-input_scan= + | +-LimitOffsetScan + | +-column_list=[$groupby.key#5, $aggregate.c#4] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.key#5, $aggregate.c#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + | | +-group_by_list= + | | | +-key#5 := ColumnRef(type=INT32, column=TestTable.key#1) + | | +-aggregate_list= + | | +-c#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + | +-limit= + | +-Literal(type=INT64, value=10) + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=10) + +-unit=ROWS + +-weight_column= + +-ColumnHolder(column=$sample_weight.ww#6) + +[UNPARSED_SQL] +SELECT + limitoffsetscan_5.a_3 AS key, + limitoffsetscan_5.a_4 AS c, + a_6 AS ww +FROM + ( + SELECT + testtable_2.a_1 AS a_3, + COUNT(*) AS a_4 + FROM + ( + SELECT + TestTable.key AS a_1 + FROM + TestTable + ) AS testtable_2 + GROUP BY 1 + LIMIT 10 + ) AS limitoffsetscan_5 TABLESAMPLE bernoulli(10 ROWS) WITH WEIGHT AS a_6; +-- +ALTERNATION GROUP: EXTEND 123, +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#5 AS key [INT32] +| +-$aggregate.c#4 AS c [INT64] +| +-$pipe_extend.$col1#6 AS `$col1` [INT64] ++-query= + +-SampleScan + +-column_list=[$groupby.key#5, $aggregate.c#4, $pipe_extend.$col1#6] + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.key#5, $aggregate.c#4, $pipe_extend.$col1#6] + | +-expr_list= + | | +-$col1#6 := Literal(type=INT64, value=123) + | +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#5, $aggregate.c#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + | +-group_by_list= + | | +-key#5 := ColumnRef(type=INT32, column=TestTable.key#1) + | +-aggregate_list= + | +-c#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=10) + +-unit=ROWS + +[UNPARSED_SQL] +SELECT + projectscan_7.a_3 AS key, + projectscan_7.a_4 AS c, + projectscan_7.a_6 AS a_6 +FROM + ( + SELECT + aggregatescan_5.a_3 AS a_3, + aggregatescan_5.a_4 AS a_4, + 123 AS a_6 + FROM + ( + SELECT + testtable_2.a_1 AS a_3, + COUNT(*) AS a_4 + FROM + ( + SELECT + TestTable.key AS a_1 + FROM + TestTable + ) AS testtable_2 + GROUP BY 1 + ) AS aggregatescan_5 + ) AS projectscan_7 TABLESAMPLE bernoulli(10 ROWS); +-- +ALTERNATION GROUP: EXTEND 123, WITH WEIGHT ww +-- +QueryStmt ++-output_column_list= +| +-$groupby.key#5 AS key [INT32] +| +-$aggregate.c#4 AS c [INT64] +| +-$pipe_extend.$col1#6 AS `$col1` [INT64] +| +-$sample_weight.ww#7 AS ww [DOUBLE] ++-query= + +-SampleScan + +-column_list=[$groupby.key#5, $aggregate.c#4, $pipe_extend.$col1#6, $sample_weight.ww#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.key#5, $aggregate.c#4, $pipe_extend.$col1#6] + | +-expr_list= + | | +-$col1#6 := Literal(type=INT64, value=123) + | +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.key#5, $aggregate.c#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.key#1], table=TestTable, column_index_list=[0]) + | +-group_by_list= + | | +-key#5 := ColumnRef(type=INT32, column=TestTable.key#1) + | +-aggregate_list= + | +-c#4 := AggregateFunctionCall(ZetaSQL:$count_star() -> INT64) + +-method="bernoulli" + +-size= + | +-Literal(type=INT64, value=10) + +-unit=ROWS + +-weight_column= + +-ColumnHolder(column=$sample_weight.ww#7) + +[UNPARSED_SQL] +SELECT + projectscan_7.a_3 AS key, + projectscan_7.a_4 AS c, + projectscan_7.a_6 AS a_6, + a_8 AS ww +FROM + ( + SELECT + aggregatescan_5.a_3 AS a_3, + aggregatescan_5.a_4 AS a_4, + 123 AS a_6 + FROM + ( + SELECT + testtable_2.a_1 AS a_3, + COUNT(*) AS a_4 + FROM + ( + SELECT + TestTable.key AS a_1 + FROM + TestTable + ) AS testtable_2 + GROUP BY 1 + ) AS aggregatescan_5 + ) AS projectscan_7 TABLESAMPLE bernoulli(10 ROWS) WITH WEIGHT AS a_8; diff --git a/zetasql/analyzer/testdata/pipe_unpivot.test b/zetasql/analyzer/testdata/pipe_unpivot.test new file mode 100644 index 000000000..077ae49f6 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_unpivot.test @@ -0,0 +1,213 @@ +# Tests for pipe UNPIVOT include a subset of tests from unpivot.test. +# +# The resolver code is all shared so this doesn't duplicate exhaustive tests +# for the full behavior. +# Since Resolved ASTs are the same, this also doesn't include rewriter output. +# See unpivot.test for those details. +# +# Unlike standard UNPIVOT, here it is unnecessary to test all combinations with +# tables, subqueries, TVFs, UNNEST, etc, since pipe operators are orthogonal +# and independent of where the input table came from. +# +# When UNPIVOT is used on a single table FROM clause, the behavior is identical +# for the standard syntax suffix operator and the pipe operator. Several tests +# show this by using an alternation {{ | \|> }} that includes a pipe or not. +[default language_features=PIPES,PIPE_STATIC_DESCRIBE,V_1_3_UNPIVOT,TABLE_VALUED_FUNCTIONS] +# Output with or without an alias is the same, except for +# whether the range variable appears. +# Output includes the unmentioned column 'Value' plus the created +# columns 'a' and 'b'. +FROM KeyValue +|> UNPIVOT(a for b in (Key)){{| AS unpivot}} +|> STATIC_DESCRIBE +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$unpivot.Value#5 AS Value [STRING] +| +-$unpivot.a#3 AS a [INT64] +| +-$unpivot.b#4 AS b [STRING] ++-query= + +-StaticDescribeScan + +-column_list=$unpivot.[Value#5, a#3, b#4] + +-describe_text= + | """ + | NameList: + | Value STRING $unpivot.Value#5 + | a INT64 $unpivot.a#3 + | b STRING $unpivot.b#4 + | NameScope: + | Names: + | Value -> STRING ($unpivot.Value#5) + | a -> INT64 ($unpivot.a#3) + | b -> STRING ($unpivot.b#4) + | """ + +-input_scan= + +-UnpivotScan + +-column_list=$unpivot.[Value#5, a#3, b#4] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-value_column_list=[$unpivot.a#3] + +-label_column=$unpivot.b#4 + +-label_list= + | +-Literal(type=STRING, value="Key") + +-unpivot_arg_list= + | +-UnpivotArg + | +-column_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-projected_input_column_list= + | +-Value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + +-include_nulls=FALSE +-- +ALTERNATION GROUP: AS unpivot +-- +QueryStmt ++-output_column_list= +| +-$unpivot.Value#5 AS Value [STRING] +| +-$unpivot.a#3 AS a [INT64] +| +-$unpivot.b#4 AS b [STRING] ++-query= + +-StaticDescribeScan + +-column_list=$unpivot.[Value#5, a#3, b#4] + +-describe_text= + | """ + | NameList: + | Value STRING $unpivot.Value#5 + | a INT64 $unpivot.a#3 + | b STRING $unpivot.b#4 + | NameScope: + | Names: + | Value -> STRING ($unpivot.Value#5) + | a -> INT64 ($unpivot.a#3) + | b -> STRING ($unpivot.b#4) + | Range variables: + | unpivot -> RANGE_VARIABLE + | """ + +-input_scan= + +-UnpivotScan + +-column_list=$unpivot.[Value#5, a#3, b#4] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-value_column_list=[$unpivot.a#3] + +-label_column=$unpivot.b#4 + +-label_list= + | +-Literal(type=STRING, value="Key") + +-unpivot_arg_list= + | +-UnpivotArg + | +-column_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-projected_input_column_list= + | +-Value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + +-include_nulls=FALSE +== + +FROM KeyValue +|> UNPIVOT(a for b in (Key)) AS unpivot +|> SELECT *, a, unpivot.b, unpivot.value +-- +QueryStmt ++-output_column_list= +| +-$unpivot.Value#5 AS Value [STRING] +| +-$unpivot.a#3 AS a [INT64] +| +-$unpivot.b#4 AS b [STRING] +| +-$unpivot.a#3 AS a [INT64] +| +-$unpivot.b#4 AS b [STRING] +| +-$unpivot.Value#5 AS value [STRING] ++-query= + +-ProjectScan + +-column_list=$unpivot.[Value#5, a#3, b#4, a#3, b#4, Value#5] + +-input_scan= + +-UnpivotScan + +-column_list=$unpivot.[Value#5, a#3, b#4] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-value_column_list=[$unpivot.a#3] + +-label_column=$unpivot.b#4 + +-label_list= + | +-Literal(type=STRING, value="Key") + +-unpivot_arg_list= + | +-UnpivotArg + | +-column_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-projected_input_column_list= + | +-Value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + +-include_nulls=FALSE +== + +# UNPIVOT on a standard syntax query, feeding output into of a TVF. +SELECT Key, Value +FROM KeyValue +GROUP BY Key, Value +|> UNPIVOT(a for b in (Key)) +|> CALL tvf_one_relation_arg_output_schema_is_input_schema() +-- +QueryStmt ++-output_column_list= +| +-tvf_one_relation_arg_output_schema_is_input_schema.Value#8 AS Value [STRING] +| +-tvf_one_relation_arg_output_schema_is_input_schema.a#9 AS a [INT64] +| +-tvf_one_relation_arg_output_schema_is_input_schema.b#10 AS b [STRING] ++-query= + +-TVFScan + +-column_list=tvf_one_relation_arg_output_schema_is_input_schema.[Value#8, a#9, b#10] + +-tvf=tvf_one_relation_arg_output_schema_is_input_schema((ANY TABLE) -> ANY TABLE) + +-signature=(TABLE) -> TABLE + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-UnpivotScan + | | +-column_list=$unpivot.[Value#7, a#5, b#6] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=$groupby.[Key#3, Value#4] + | | | +-input_scan= + | | | +-AggregateScan + | | | +-column_list=$groupby.[Key#3, Value#4] + | | | +-input_scan= + | | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | | +-group_by_list= + | | | +-Key#3 := ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-value_column_list=[$unpivot.a#5] + | | +-label_column=$unpivot.b#6 + | | +-label_list= + | | | +-Literal(type=STRING, value="Key") + | | +-unpivot_arg_list= + | | | +-UnpivotArg + | | | +-column_list= + | | | +-ColumnRef(type=INT64, column=$groupby.Key#3) + | | +-projected_input_column_list= + | | | +-Value#7 := ColumnRef(type=STRING, column=$groupby.Value#4) + | | +-include_nulls=FALSE + | +-argument_column_list=$unpivot.[Value#7, a#5, b#6] + +-column_index_list=[0, 1, 2] +== + +# UNPIVOT query with all columns pruned. +FROM KeyValue +|> UNPIVOT(a for b in (Key)) +|> SELECT 123 +-- +QueryStmt ++-output_column_list= +| +-$pipe_select.$col1#6 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$pipe_select.$col1#6] + +-expr_list= + | +-$col1#6 := Literal(type=INT64, value=123) + +-input_scan= + +-UnpivotScan + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-value_column_list=[$unpivot.a#3] + +-label_column=$unpivot.b#4 + +-label_list= + | +-Literal(type=STRING, value="Key") + +-unpivot_arg_list= + | +-UnpivotArg + | +-column_list= + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-projected_input_column_list= + | +-Value#5 := ColumnRef(type=STRING, column=KeyValue.Value#2) + +-include_nulls=FALSE diff --git a/zetasql/analyzer/testdata/pipe_where.test b/zetasql/analyzer/testdata/pipe_where.test new file mode 100644 index 000000000..fb91c4d43 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_where.test @@ -0,0 +1,284 @@ +[default language_features=PIPES,ANALYTIC_FUNCTIONS] +from KeyValue +|> where key>10 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=KeyValue.[Key#1, Value#2] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=KeyValue.Key#1) + +-Literal(type=INT64, value=10) +== + +# WHERE preserves value tables +from TestExtraValueTable +|> where int32_val1 = 4 +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS `$value` [PROTO] ++-is_value_table=TRUE ++-query= + +-FilterScan + +-column_list=[TestExtraValueTable.value#1] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=int32_val1 + | +-default_value=0 + +-Literal(type=INT32, value=4) +== + +from KeyValue +|> where key +-- +ERROR: WHERE clause should return type BOOL, but returns INT64 [at 2:10] +|> where key + ^ +== + +from KeyValue +|> where sum(key) > 10 +-- +ERROR: Aggregate function SUM not allowed in pipe WHERE clause [at 2:10] +|> where sum(key) > 10 + ^ +== + +from KeyValue +|> where sum(sum(key)) OVER (order by value) > 10 +-- +ERROR: Aggregate function SUM not allowed in pipe WHERE clause [at 2:14] +|> where sum(sum(key)) OVER (order by value) > 10 + ^ +== + +from KeyValue +|> where sum(key) OVER (order by value) > 10 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3] + +-input_scan= + | +-AnalyticScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-analytic_function_list= + | +-$analytic1#3 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$analytic.$analytic1#3) + +-Literal(type=INT64, value=10) +== + +# Where clause is a window function call that directly returns a bool, without +# needing another ProjectScan or expression computation. +from KeyValue +|> where any_value(key = 4) OVER (order by value) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3] + +-input_scan= + | +-AnalyticScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-analytic_function_list= + | +-$analytic1#3 := + | +-AnalyticFunctionCall(ZetaSQL:any_value(BOOL) -> BOOL) + | +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | +-Literal(type=INT64, value=4) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-filter_expr= + +-ColumnRef(type=BOOL, column=$analytic.$analytic1#3) +== + +# Where clause with a window function call that requires another Project to +# precompute window function inputs. +from KeyValue +|> where sum(key+1) OVER (order by value) > 9 +|> where count(*) OVER (partition by key+1) > 10 +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3, $analytic.$analytic1#4] + +-input_scan= + | +-AnalyticScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3, $analytic.$analytic1#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3, $partitionby.$partitionbycol1#5] + | | +-expr_list= + | | | +-$partitionbycol1#5 := + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=1) + | | +-input_scan= + | | +-FilterScan + | | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3] + | | +-input_scan= + | | | +-AnalyticScan + | | | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3] + | | | +-input_scan= + | | | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | | | +-function_group_list= + | | | +-AnalyticFunctionGroup + | | | +-order_by= + | | | | +-WindowOrdering + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-column_ref= + | | | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | | | +-analytic_function_list= + | | | +-$analytic1#3 := + | | | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | | +-Literal(type=INT64, value=1) + | | | +-window_frame= + | | | +-WindowFrame(frame_unit=RANGE) + | | | +-start_expr= + | | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | | +-end_expr= + | | | +-WindowFrameExpr(boundary_type=CURRENT ROW) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | | +-ColumnRef(type=INT64, column=$analytic.$analytic1#3) + | | +-Literal(type=INT64, value=9) + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-partition_by= + | | +-WindowPartitioning + | | +-partition_by_list= + | | +-ColumnRef(type=INT64, column=$partitionby.$partitionbycol1#5) + | +-analytic_function_list= + | +-$analytic1#4 := + | +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$analytic.$analytic1#4) + +-Literal(type=INT64, value=10) +== + +# Where clause with an expression containing two window functions. +from KeyValue +|> where sum(key) OVER (partition by value) = + count(value) OVER (partition by value) +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] ++-query= + +-FilterScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3, $analytic.$analytic2#4] + +-input_scan= + | +-AnalyticScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $analytic.$analytic1#3, $analytic.$analytic2#4] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-function_group_list= + | +-AnalyticFunctionGroup + | | +-partition_by= + | | | +-WindowPartitioning + | | | +-partition_by_list= + | | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | | +-analytic_function_list= + | | +-$analytic1#3 := + | | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-ColumnRef(type=INT64, column=KeyValue.Key#1) + | | +-window_frame= + | | +-WindowFrame(frame_unit=ROWS) + | | +-start_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | +-end_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + | +-AnalyticFunctionGroup + | +-partition_by= + | | +-WindowPartitioning + | | +-partition_by_list= + | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-analytic_function_list= + | +-$analytic2#4 := + | +-AnalyticFunctionCall(ZetaSQL:count(STRING) -> INT64) + | +-ColumnRef(type=STRING, column=KeyValue.Value#2) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$analytic.$analytic1#3) + +-ColumnRef(type=INT64, column=$analytic.$analytic2#4) +== + +from KeyValue +|> where count(*) OVER w < 10 +-- +ERROR: Cannot reference a named window in pipe WHERE clause [at 2:24] +|> where count(*) OVER w < 10 + ^ diff --git a/zetasql/analyzer/testdata/pipe_window.test b/zetasql/analyzer/testdata/pipe_window.test new file mode 100644 index 000000000..ee8d8e312 --- /dev/null +++ b/zetasql/analyzer/testdata/pipe_window.test @@ -0,0 +1,804 @@ +[default language_features=PIPES,ANALYTIC_FUNCTIONS,V_1_1_SELECT_STAR_EXCEPT_REPLACE] +select 1 x +|> window sum(x) OVER (), count(*) OVER () AS c +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$analytic.$analytic1#4 AS `$col1` [INT64] +| +-$analytic.c#5 AS c [INT64] ++-query= + +-AnalyticScan + +-column_list=[$query.x#1, $analytic.$analytic1#4, $analytic.c#5] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.x#1] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#4 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-c#5 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Expressions without a window function are not allowed. +select 1 x +|> window {{1|x|rand()|x+x|(select x)|sum(x)}} +-- +ALTERNATION GROUP: 1 +-- +ERROR: Pipe WINDOW expression must include a window function call (with an OVER clause) [at 2:11] +|> window 1 + ^ +-- +ALTERNATION GROUP: x +-- +ERROR: Pipe WINDOW expression must include a window function call (with an OVER clause) [at 2:11] +|> window x + ^ +-- +ALTERNATION GROUP: rand() +-- +ERROR: Pipe WINDOW expression must include a window function call (with an OVER clause) [at 2:11] +|> window rand() + ^ +-- +ALTERNATION GROUP: x+x +-- +ERROR: Pipe WINDOW expression must include a window function call (with an OVER clause) [at 2:11] +|> window x+x + ^ +-- +ALTERNATION GROUP: (select x) +-- +ERROR: Pipe WINDOW expression must include a window function call (with an OVER clause) [at 2:11] +|> window (select x) + ^ +-- +ALTERNATION GROUP: sum(x) +-- +ERROR: Aggregate function SUM not allowed in pipe WINDOW [at 2:11] +|> window sum(x) + ^ +== + +# The error points at the aggregate function. +select 1 x +|> WINDOW count(*) OVER () + 1 + sum(x) +-- +ERROR: Aggregate function SUM not allowed in pipe WINDOW [at 2:34] +|> WINDOW count(*) OVER () + 1 + sum(x) + ^ +== + +select 1 x +|> WINDOW count(*) OVER (ORDER BY 1+sum(x)) +-- +ERROR: Aggregate function SUM not allowed in Window ORDER BY [at 2:37] +|> WINDOW count(*) OVER (ORDER BY 1+sum(x)) + ^ +== + +# This pattern is allowed in normal SQL, as a window function of an aggregate function. +select 1 x +|> WINDOW SUM(SUM(x)) OVER () +-- +ERROR: Aggregate function SUM not allowed in pipe WINDOW [at 2:15] +|> WINDOW SUM(SUM(x)) OVER () + ^ +== + +select 1 x +|> WINDOW SUM(SUM(SUM(x))) OVER () +-- +ERROR: Aggregate function SUM not allowed in pipe WINDOW [at 2:19] +|> WINDOW SUM(SUM(SUM(x))) OVER () + ^ +== + +# The two identical OVER clauses are not grouped together. This only works +# (including in standard syntax) for empty OVER clauses or named windows. +select 1 x, 2 y, 3 z +|> window x+sum(y+1) OVER (ORDER BY z), + count(*) OVER (), + sum(x) OVER (), + rank() OVER(ORDER BY z) +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.z#3 AS z [INT64] +| +-$pipe_window.$col1#9 AS `$col1` [INT64] +| +-$analytic.$analytic2#10 AS `$col2` [INT64] +| +-$analytic.$analytic3#11 AS `$col3` [INT64] +| +-$analytic.$analytic4#12 AS `$col4` [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $pipe_window.$col1#9, $analytic.$analytic2#10, $analytic.$analytic3#11, $analytic.$analytic4#12] + +-expr_list= + | +-$col1#9 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-ColumnRef(type=INT64, column=$analytic.$analytic1#8) + +-input_scan= + +-AnalyticScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#8, $analytic.$analytic2#10, $analytic.$analytic3#11, $analytic.$analytic4#12] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-function_group_list= + +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$query.z#3) + | +-analytic_function_list= + | +-$analytic1#8 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.y#2) + | +-Literal(type=INT64, value=1) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-AnalyticFunctionGroup + | +-analytic_function_list= + | +-$analytic2#10 := + | | +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + | | +-window_frame= + | | +-WindowFrame(frame_unit=ROWS) + | | +-start_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | +-end_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + | +-$analytic3#11 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.z#3) + +-analytic_function_list= + +-$analytic4#12 := AnalyticFunctionCall(ZetaSQL:rank() -> INT64) +== + +# An expression combining the result of two window functions. +select 1 x, 2 y, 3 z +|> window sum(x) OVER (ORDER BY y) + avg(x) OVER (ORDER BY y DESC) +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$query.z#3 AS z [INT64] +| +-$pipe_window.$col1#8 AS `$col1` [DOUBLE] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $pipe_window.$col1#8] + +-expr_list= + | +-$col1#8 := + | +-FunctionCall(ZetaSQL:$add(DOUBLE, DOUBLE) -> DOUBLE) + | +-Cast(INT64 -> DOUBLE) + | | +-ColumnRef(type=INT64, column=$analytic.$analytic1#6) + | +-ColumnRef(type=DOUBLE, column=$analytic.$analytic2#7) + +-input_scan= + +-AnalyticScan + +-column_list=[$query.x#1, $query.y#2, $query.z#3, $analytic.$analytic1#6, $analytic.$analytic2#7] + +-input_scan= + | +-ProjectScan + | +-column_list=$query.[x#1, y#2, z#3] + | +-expr_list= + | | +-x#1 := Literal(type=INT64, value=1) + | | +-y#2 := Literal(type=INT64, value=2) + | | +-z#3 := Literal(type=INT64, value=3) + | +-input_scan= + | +-SingleRowScan + +-function_group_list= + +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$query.y#2) + | +-analytic_function_list= + | +-$analytic1#6 := + | +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-ColumnRef(type=INT64, column=$query.x#1) + | +-window_frame= + | +-WindowFrame(frame_unit=RANGE) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=CURRENT ROW) + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | | +-ColumnRef(type=INT64, column=$query.y#2) + | +-is_descending=TRUE + +-analytic_function_list= + +-$analytic2#7 := + +-AnalyticFunctionCall(ZetaSQL:avg(INT64) -> DOUBLE) + +-ColumnRef(type=INT64, column=$query.x#1) + +-window_frame= + +-WindowFrame(frame_unit=RANGE) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=CURRENT ROW) +== + +# Test visibility of names after pipe window. +select 1 x, 2 y +|> window rank() over (order by y) AS r +|> where x=r +|> select * +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] +| +-$query.y#2 AS y [INT64] +| +-$analytic.r#4 AS r [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1, $query.y#2, $analytic.r#4] + +-input_scan= + +-FilterScan + +-column_list=[$query.x#1, $query.y#2, $analytic.r#4] + +-input_scan= + | +-AnalyticScan + | +-column_list=[$query.x#1, $query.y#2, $analytic.r#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=$query.[x#1, y#2] + | | +-expr_list= + | | | +-x#1 := Literal(type=INT64, value=1) + | | | +-y#2 := Literal(type=INT64, value=2) + | | +-input_scan= + | | +-SingleRowScan + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-order_by= + | | +-WindowOrdering + | | +-order_by_item_list= + | | +-OrderByItem + | | +-column_ref= + | | +-ColumnRef(type=INT64, column=$query.y#2) + | +-analytic_function_list= + | +-r#4 := AnalyticFunctionCall(ZetaSQL:rank() -> INT64) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT64, INT64) -> BOOL) + +-ColumnRef(type=INT64, column=$query.x#1) + +-ColumnRef(type=INT64, column=$analytic.r#4) +== + +# Applying WINDOW on a value table input. +FROM TestExtraValueTable t +|> WINDOW sum(int32_val1) OVER (order by t.int32_val2) AS sum_col +-- +QueryStmt ++-output_column_list= +| +-TestExtraValueTable.value#1 AS t [PROTO] +| +-$analytic.sum_col#5 AS sum_col [INT64] ++-query= + +-AnalyticScan + +-column_list=[TestExtraValueTable.value#1, $analytic.sum_col#5] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestExtraValueTable.value#1, $orderby.int32_val2#6] + | +-expr_list= + | | +-int32_val2#6 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="t") + +-function_group_list= + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=INT32, column=$orderby.int32_val2#6) + +-analytic_function_list= + +-sum_col#5 := + +-AnalyticFunctionCall(ZetaSQL:sum(INT64) -> INT64) + +-Cast(INT32 -> INT64) + +-GetProtoField + +-type=INT32 + +-expr= + | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + +-field_descriptor=int32_val1 + +-default_value=0 + +-window_frame= + +-WindowFrame(frame_unit=RANGE) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=CURRENT ROW) +== + +# Range variables and pseudo-columns are preserved after WINDOW. +FROM EnumTable t +|> WHERE key = 1 +|> WHERE filename = '' +|> WINDOW COUNT(*) OVER () + 1 +|> WHERE key = 1 +|> WHERE filename = '' +|> WHERE t.key=1 +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-$pipe_window.$col1#8 AS `$col1` [INT64] ++-query= + +-FilterScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_window.$col1#8] + +-input_scan= + | +-FilterScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_window.$col1#8] + | +-input_scan= + | | +-FilterScan + | | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_window.$col1#8] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $pipe_window.$col1#8] + | | | +-expr_list= + | | | | +-$col1#8 := + | | | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | | | +-ColumnRef(type=INT64, column=$analytic.$analytic1#7) + | | | | +-Literal(type=INT64, value=1) + | | | +-input_scan= + | | | +-AnalyticScan + | | | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.Filename#4, $analytic.$analytic1#7] + | | | +-input_scan= + | | | | +-FilterScan + | | | | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4] + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4] + | | | | | +-input_scan= + | | | | | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, Filename#4], table=EnumTable, column_index_list=[0, 1, 2, 3], alias="t") + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | | | | | +-ColumnRef(type=INT32, column=EnumTable.key#1) + | | | | | +-Literal(type=INT32, value=1) + | | | | +-filter_expr= + | | | | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | | | | +-ColumnRef(type=STRING, column=EnumTable.Filename#4) + | | | | +-Literal(type=STRING, value="") + | | | +-function_group_list= + | | | +-AnalyticFunctionGroup + | | | +-analytic_function_list= + | | | +-$analytic1#7 := + | | | +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + | | | +-window_frame= + | | | +-WindowFrame(frame_unit=ROWS) + | | | +-start_expr= + | | | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | | | +-end_expr= + | | | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + | | +-ColumnRef(type=INT32, column=EnumTable.key#1) + | | +-Literal(type=INT32, value=1) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=EnumTable.Filename#4) + | +-Literal(type=STRING, value="") + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(INT32, INT32) -> BOOL) + +-ColumnRef(type=INT32, column=EnumTable.key#1) + +-Literal(type=INT32, value=1) +== + +# This tests that unselected pseudo-columns are still pruned +# from the column_lists. `Filename#4` is removed. +FROM EnumTable +|> WINDOW COUNT(*) OVER () +|> WHERE RowId = b'' +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] +| +-EnumTable.TestEnum#2 AS TestEnum [ENUM] +| +-EnumTable.AnotherTestEnum#3 AS AnotherTestEnum [ENUM] +| +-$analytic.$analytic1#7 AS `$col1` [INT64] ++-query= + +-FilterScan + +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.RowId#5, $analytic.$analytic1#7] + +-input_scan= + | +-AnalyticScan + | +-column_list=[EnumTable.key#1, EnumTable.TestEnum#2, EnumTable.AnotherTestEnum#3, EnumTable.RowId#5, $analytic.$analytic1#7] + | +-input_scan= + | | +-TableScan(column_list=EnumTable.[key#1, TestEnum#2, AnotherTestEnum#3, RowId#5], table=EnumTable, column_index_list=[0, 1, 2, 4]) + | +-function_group_list= + | +-AnalyticFunctionGroup + | +-analytic_function_list= + | +-$analytic1#7 := + | +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + | +-window_frame= + | +-WindowFrame(frame_unit=ROWS) + | +-start_expr= + | | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + | +-end_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) + +-filter_expr= + +-FunctionCall(ZetaSQL:$equal(BYTES, BYTES) -> BOOL) + +-ColumnRef(type=BYTES, column=EnumTable.RowId#5) + +-Literal(type=BYTES, value=b"") +== + +# This shows a case where a window function is computed but not used. +FROM EnumTable +|> WINDOW COUNT(*) OVER () win +|> SELECT key +-- +QueryStmt ++-output_column_list= +| +-EnumTable.key#1 AS key [INT32] ++-query= + +-ProjectScan + +-column_list=[EnumTable.key#1] + +-input_scan= + +-AnalyticScan + +-column_list=[EnumTable.key#1, $analytic.win#7] + +-input_scan= + | +-TableScan(column_list=[EnumTable.key#1], table=EnumTable, column_index_list=[0]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-win#7 := + +-AnalyticFunctionCall(ZetaSQL:$count_star() -> INT64) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Named windows aren't supported. +from KeyValue +|> window count(*) OVER w +-- +ERROR: Cannot reference a named window in pipe WINDOW [at 2:25] +|> window count(*) OVER w + ^ +== + +# Dot-star works in WINDOW, but still requires a window fucntion. +FROM ComplexTypes +|> SELECT TestStruct +|> WINDOW TestStruct.* +-- +ERROR: Pipe WINDOW expression must include a window function call (with an OVER clause) [at 3:11] +|> WINDOW TestStruct.* + ^ +== + +# Dot-star after a window function. +FROM ComplexTypes +|> SELECT TestStruct +|> WINDOW ANY_VALUE(TestStruct) OVER ().* +-- +QueryStmt ++-output_column_list= +| +-ComplexTypes.TestStruct#5 AS TestStruct [STRUCT>] +| +-$pipe_window.c#8 AS c [INT32] +| +-$pipe_window.d#9 AS d [STRUCT] ++-query= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_window.c#8, $pipe_window.d#9] + +-expr_list= + | +-c#8 := + | | +-GetStructField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=STRUCT>, column=$analytic.$analytic1#7) + | | +-field_idx=0 + | +-d#9 := + | +-GetStructField + | +-type=STRUCT + | +-expr= + | | +-ColumnRef(type=STRUCT>, column=$analytic.$analytic1#7) + | +-field_idx=1 + +-input_scan= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $analytic.$analytic1#10, $analytic.$analytic1#7] + +-expr_list= + | +-$analytic1#7 := ColumnRef(type=STRUCT>, column=$analytic.$analytic1#10) + +-input_scan= + +-AnalyticScan + +-column_list=[ComplexTypes.TestStruct#5, $analytic.$analytic1#10] + +-input_scan= + | +-ProjectScan + | +-column_list=[ComplexTypes.TestStruct#5] + | +-input_scan= + | +-TableScan(column_list=[ComplexTypes.TestStruct#5], table=ComplexTypes, column_index_list=[4]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#10 := + +-AnalyticFunctionCall(ZetaSQL:any_value(STRUCT>) -> STRUCT>) + +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Dot-star after a window function with modifiers. +FROM ComplexTypes +|> SELECT TestStruct +|> WINDOW ANY_VALUE(TestStruct) OVER ().* + REPLACE ('abc' AS d) +-- +QueryStmt ++-output_column_list= +| +-ComplexTypes.TestStruct#5 AS TestStruct [STRUCT>] +| +-$pipe_window.c#8 AS c [INT32] +| +-$pipe_window.d#9 AS d [STRING] ++-query= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_window.c#8, $pipe_window.d#9] + +-expr_list= + | +-c#8 := + | +-GetStructField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=STRUCT>, column=$analytic.$analytic1#7) + | +-field_idx=0 + +-input_scan= + +-ProjectScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_window.d#9, $analytic.$analytic1#10, $analytic.$analytic1#7] + +-expr_list= + | +-$analytic1#7 := ColumnRef(type=STRUCT>, column=$analytic.$analytic1#10) + +-input_scan= + +-AnalyticScan + +-column_list=[ComplexTypes.TestStruct#5, $pipe_window.d#9, $analytic.$analytic1#10] + +-input_scan= + | +-ProjectScan + | +-column_list=[ComplexTypes.TestStruct#5, $pipe_window.d#9] + | +-expr_list= + | | +-d#9 := Literal(type=STRING, value="abc") + | +-input_scan= + | +-ProjectScan + | +-column_list=[ComplexTypes.TestStruct#5] + | +-input_scan= + | +-TableScan(column_list=[ComplexTypes.TestStruct#5], table=ComplexTypes, column_index_list=[4]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-$analytic1#10 := + +-AnalyticFunctionCall(ZetaSQL:any_value(STRUCT>) -> STRUCT>) + +-ColumnRef(type=STRUCT>, column=ComplexTypes.TestStruct#5) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +== + +# Dot-star over an expression constructing a STRUCT. +SELECT 1 pos +|> WINDOW (FIRST_VALUE(STRUCT(1 AS x, 2, 'abc' AS x, null AS y)) + OVER (ORDER BY pos)).* +-- +QueryStmt ++-output_column_list= +| +-$query.pos#1 AS pos [INT64] +| +-$pipe_window.x#3 AS x [INT64] +| +-$pipe_window.$field2#4 AS `$field2` [INT64] +| +-$pipe_window.x#5 AS x [STRING] +| +-$pipe_window.y#6 AS y [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.pos#1, $pipe_window.x#3, $pipe_window.$field2#4, $pipe_window.x#5, $pipe_window.y#6] + +-expr_list= + | +-x#3 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$analytic.$analytic1#2) + | | +-field_idx=0 + | +-$field2#4 := + | | +-GetStructField + | | +-type=INT64 + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$analytic.$analytic1#2) + | | +-field_idx=1 + | +-x#5 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-ColumnRef(type=STRUCT, column=$analytic.$analytic1#2) + | | +-field_idx=2 + | +-y#6 := + | +-GetStructField + | +-type=INT64 + | +-expr= + | | +-ColumnRef(type=STRUCT, column=$analytic.$analytic1#2) + | +-field_idx=3 + +-input_scan= + +-ProjectScan + +-column_list=[$query.pos#1, $analytic.$analytic1#7, $analytic.$analytic1#2] + +-expr_list= + | +-$analytic1#2 := ColumnRef(type=STRUCT, column=$analytic.$analytic1#7) + +-input_scan= + +-AnalyticScan + +-column_list=[$query.pos#1, $analytic.$analytic1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[$query.pos#1] + | +-expr_list= + | | +-pos#1 := Literal(type=INT64, value=1) + | +-input_scan= + | +-SingleRowScan + +-function_group_list= + +-AnalyticFunctionGroup + +-order_by= + | +-WindowOrdering + | +-order_by_item_list= + | +-OrderByItem + | +-column_ref= + | +-ColumnRef(type=INT64, column=$query.pos#1) + +-analytic_function_list= + +-$analytic1#7 := + +-AnalyticFunctionCall(ZetaSQL:first_value(STRUCT) -> STRUCT) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-Literal(type=INT64, value=1) + +-Literal(type=INT64, value=2) + +-Literal(type=STRING, value="abc") + +-Literal(type=INT64, value=NULL) + +-window_frame= + +-WindowFrame(frame_unit=RANGE) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=CURRENT ROW) +== + +# Normal errors from .* apply. This file doesn't repeat all the error case +# tests since they are the same as those in pipe_extend.test and other places. +FROM ComplexTypes +|> SELECT TestStruct +|> WINDOW ANY_VALUE(TestStruct) OVER ().* EXCEPT (bad) +-- +ERROR: Column bad in SELECT * EXCEPT list does not exist [at 3:51] +|> WINDOW ANY_VALUE(TestStruct) OVER ().* EXCEPT (bad) + ^ +== + +# Window function in the .* REPLACE is not allowed. +FROM KeyValue +|> WINDOW ANY_VALUE(STRUCT(key, value)) OVER ().* + REPLACE(COUNT(*) OVER () AS value) +-- +ERROR: Cannot use analytic functions inside SELECT * REPLACE [at 3:14] + REPLACE(COUNT(*) OVER () AS value) + ^ +== + +# Collation propagation for pipe WINDOW, for both aggregate and grouping +# columns. +# TODO Enable java support for collation. +[no_java] +[language_features=PIPES,ANALYTIC_FUNCTIONS{{|,V_1_3_ANNOTATION_FRAMEWORK|,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT}}] +from CollatedTable +|> SELECT string_ci +|> WINDOW MAX(COALESCE(string_ci)) OVER () AS newcol +-- +ALTERNATION GROUPS: + + ,V_1_3_ANNOTATION_FRAMEWORK +-- +QueryStmt ++-output_column_list= +| +-CollatedTable.string_ci#1{Collation:"und:ci"} AS string_ci [STRING] +| +-$analytic.newcol#6 AS newcol [STRING] ++-query= + +-AnalyticScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $analytic.newcol#6] + +-input_scan= + | +-ProjectScan + | +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}] + | +-input_scan= + | +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-newcol#6 := + +-AnalyticFunctionCall(ZetaSQL:max(STRING) -> STRING) + +-FunctionCall(ZetaSQL:coalesce(repeated(1) STRING) -> STRING) + +-ColumnRef(type=STRING, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +-- +ALTERNATION GROUP: ,V_1_3_ANNOTATION_FRAMEWORK,V_1_3_COLLATION_SUPPORT +-- +QueryStmt ++-output_column_list= +| +-CollatedTable.string_ci#1{Collation:"und:ci"} AS string_ci [STRING] +| +-$analytic.newcol#6{Collation:"und:ci"} AS newcol [STRING] ++-query= + +-AnalyticScan + +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}, $analytic.newcol#6{Collation:"und:ci"}] + +-input_scan= + | +-ProjectScan + | +-column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}] + | +-input_scan= + | +-TableScan(column_list=[CollatedTable.string_ci#1{Collation:"und:ci"}], table=CollatedTable, column_index_list=[0]) + +-function_group_list= + +-AnalyticFunctionGroup + +-analytic_function_list= + +-newcol#6 := + +-AnalyticFunctionCall(ZetaSQL:max(STRING) -> STRING) + +-type_annotation_map={Collation:"und:ci"} + +-FunctionCall(ZetaSQL:coalesce(repeated(1) STRING) -> STRING) + +-type_annotation_map={Collation:"und:ci"} + +-ColumnRef(type=STRING, type_annotation_map={Collation:"und:ci"}, column=CollatedTable.string_ci#1{Collation:"und:ci"}) + +-collation_list=[und:ci] + +-window_frame= + +-WindowFrame(frame_unit=ROWS) + +-start_expr= + | +-WindowFrameExpr(boundary_type=UNBOUNDED PRECEDING) + +-end_expr= + +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) diff --git a/zetasql/analyzer/testdata/pivot.test b/zetasql/analyzer/testdata/pivot.test index eb2ca7583..7a52bf798 100644 --- a/zetasql/analyzer/testdata/pivot.test +++ b/zetasql/analyzer/testdata/pivot.test @@ -8,6 +8,127 @@ SELECT * FROM UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key IN (0, 1)); ^ == +[language_features=V_1_3_PIVOT{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] +# 2 UNNESTs +SELECT * FROM UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key IN (0, 1)); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$array.k#1 AS k [STRING] +| +-$array.Value#2 AS Value [INT64] ++-query= + +-ProjectScan + +-column_list=$array.[k#1, Value#2] + +-input_scan= + +-ArrayScan + +-column_list=$array.[k#1, Value#2] + +-input_scan= + | +-ArrayScan + | +-column_list=[$array.k#1] + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=["a", "b"]) + | +-element_column_list=[$array.k#1] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.Value#2] + + +DEPRECATION WARNING: +PIVOT is not allowed with array scans. This will become an error [at 2:63] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key... + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: PIVOT is not allowed with array scans [at 2:63] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key... + ^ +== + +[language_features=V_1_3_PIVOT{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] +# Table with UNNEST +SELECT * FROM KeyValue, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key IN (0, 1)); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$array.Value#3 AS Value [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.Value#3] + +-input_scan= + +-ArrayScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.Value#3] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.Value#3] + + +DEPRECATION WARNING: +PIVOT is not allowed with array scans. This will become an error [at 2:48] +SELECT * FROM KeyValue, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key IN ... + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: PIVOT is not allowed with array scans [at 2:48] +SELECT * FROM KeyValue, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key IN ... + ^ +== + +[language_features=V_1_3_PIVOT{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] +# Table with 2 UNNESTs +SELECT * FROM KeyValue, UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key IN (0, 1)); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$array.k#3 AS k [STRING] +| +-$array.Value#4 AS Value [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.k#3, $array.Value#4] + +-input_scan= + +-ArrayScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.k#3, $array.Value#4] + +-input_scan= + | +-ArrayScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.k#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=["a", "b"]) + | +-element_column_list=[$array.k#3] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.Value#4] + + +DEPRECATION WARNING: +PIVOT is not allowed with array scans. This will become an error [at 2:73] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key... + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: PIVOT is not allowed with array scans [at 2:73] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value PIVOT(COUNT(Value) FOR Key... + ^ +== + [language_features={{|V_1_3_PIVOT,V_1_3_IS_DISTINCT}}] [enabled_ast_rewrites=DEFAULTS] SELECT * FROM KeyValue PIVOT(COUNT(Value) FOR Key IN (0 AS zero, 1 AS one)); @@ -5108,9 +5229,11 @@ QueryStmt | +-null_handling_modifier=IGNORE_NULLS | +-order_by_item_list= | +-OrderByItem + | | +-parse_location=88-101 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) | +-OrderByItem + | +-parse_location=103-109 | +-column_ref= | | +-ColumnRef(type=INT64, column=$subquery1.z#3) | +-is_descending=TRUE @@ -5803,168 +5926,6 @@ QueryStmt +-PivotColumn(column=$pivot._2#11, pivot_expr_index=0, pivot_value_index=0) == -# The PIVOT rewriter depends on aggregate functions ignoring nulls, so it -# disallows PIVOT expressions which use default NULL-handling with functions -# whose default NULL-handling behavior is unknown. -# The `DISABLE_PIVOT_REWRITER_UDA_ERRORS` feature disables this check. -# Reference: b/307599486 -[language_features=V_1_3_PIVOT{{|,DISABLE_PIVOT_REWRITER_UDA_ERRORS}}] -[enabled_ast_rewrites=DEFAULTS] -SELECT * -FROM - (SELECT 'x' AS signal, 1 AS num) - PIVOT( - SumOfAggregateArgs(num) AS uda_result - FOR signal IN ('x') - ); --- -ALTERNATION GROUP: --- -[PRE-REWRITE AST] -QueryStmt -+-output_column_list= -| +-$pivot.uda_result_x#4 AS uda_result_x [INT64] -+-query= - +-ProjectScan - +-column_list=[$pivot.uda_result_x#4] - +-input_scan= - +-PivotScan - +-column_list=[$pivot.uda_result_x#4] - +-input_scan= - | +-ProjectScan - | +-column_list=$subquery1.[signal#1, num#2] - | +-expr_list= - | | +-signal#1 := Literal(type=STRING, value="x") - | | +-num#2 := Literal(type=INT64, value=1) - | +-input_scan= - | +-SingleRowScan - +-pivot_expr_list= - | +-AggregateFunctionCall(Lazy_resolution_function:SumOfAggregateArgs(INT64 agg_arg) -> INT64) - | +-parse_location=64-101 - | +-ColumnRef(type=INT64, column=$subquery1.num#2) - +-for_expr= - | +-ColumnRef(parse_location=112-118, type=STRING, column=$subquery1.signal#1) - +-pivot_value_list= - | +-Literal(type=STRING, value="x", preserve_in_literal_remover=TRUE) - +-pivot_column_list= - +-PivotColumn(column=$pivot.uda_result_x#4, pivot_expr_index=0, pivot_value_index=0) - -Rewrite ERROR: generic::unimplemented: Use of aggregate function Lazy_resolution_function:SumOfAggregateArgs as PIVOT expression is not supported [at 5:7] - SumOfAggregateArgs(num) AS uda_result - ^ - --- -ALTERNATION GROUP: ,DISABLE_PIVOT_REWRITER_UDA_ERRORS --- -QueryStmt -+-output_column_list= -| +-$pivot.uda_result_x#4 AS uda_result_x [INT64] -+-query= - +-ProjectScan - +-column_list=[$pivot.uda_result_x#4] - +-input_scan= - +-PivotScan - +-column_list=[$pivot.uda_result_x#4] - +-input_scan= - | +-ProjectScan - | +-column_list=$subquery1.[signal#1, num#2] - | +-expr_list= - | | +-signal#1 := Literal(type=STRING, value="x") - | | +-num#2 := Literal(type=INT64, value=1) - | +-input_scan= - | +-SingleRowScan - +-pivot_expr_list= - | +-AggregateFunctionCall(Lazy_resolution_function:SumOfAggregateArgs(INT64 agg_arg) -> INT64) - | +-parse_location=64-101 - | +-ColumnRef(type=INT64, column=$subquery1.num#2) - +-for_expr= - | +-ColumnRef(parse_location=112-118, type=STRING, column=$subquery1.signal#1) - +-pivot_value_list= - | +-Literal(type=STRING, value="x", preserve_in_literal_remover=TRUE) - +-pivot_column_list= - +-PivotColumn(column=$pivot.uda_result_x#4, pivot_expr_index=0, pivot_value_index=0) - -[REWRITTEN AST] -QueryStmt -+-output_column_list= -| +-$pivot.uda_result_x#4 AS uda_result_x [INT64] -+-query= - +-ProjectScan - +-column_list=[$pivot.uda_result_x#4] - +-input_scan= - +-AggregateScan - +-column_list=[$pivot.uda_result_x#4] - +-input_scan= - | +-ProjectScan - | +-column_list=[$subquery1.signal#1, $subquery1.num#2, $pivot.$pivot_value#5, $pivot.$pivot_expr_arg#6] - | +-expr_list= - | | +-$pivot_value#5 := ColumnRef(parse_location=112-118, type=STRING, column=$subquery1.signal#1) - | | +-$pivot_expr_arg#6 := ColumnRef(type=INT64, column=$subquery1.num#2) - | +-input_scan= - | +-ProjectScan - | +-column_list=$subquery1.[signal#1, num#2] - | +-expr_list= - | | +-signal#1 := Literal(type=STRING, value="x") - | | +-num#2 := Literal(type=INT64, value=1) - | +-input_scan= - | +-SingleRowScan - +-aggregate_list= - +-uda_result_x#4 := - +-AggregateFunctionCall(Lazy_resolution_function:SumOfAggregateArgs(INT64 agg_arg) -> INT64) - +-SubqueryExpr - +-type=INT64 - +-subquery_type=SCALAR - +-parameter_list= - | +-ColumnRef(type=STRING, column=$pivot.$pivot_value#5) - | +-ColumnRef(type=INT64, column=$pivot.$pivot_expr_arg#6) - +-subquery= - +-ProjectScan - +-column_list=[$expr_subquery.$col1#13] - +-expr_list= - | +-$col1#13 := - | +-FunctionCall(ZetaSQL:if(BOOL, INT64, INT64) -> INT64) - | +-SubqueryExpr - | | +-type=BOOL - | | +-subquery_type=EXISTS - | | +-parameter_list= - | | | +-ColumnRef(type=STRING, column=$subquery1.pivot_column#8) - | | | +-ColumnRef(type=STRING, column=$subquery1.pivot_value#9) - | | +-subquery= - | | +-SetOperationScan - | | +-column_list=[$intersect_all.pivot_column#12] - | | +-op_type=INTERSECT_ALL - | | +-input_item_list= - | | +-SetOperationItem - | | | +-scan= - | | | | +-ProjectScan - | | | | +-column_list=[$intersect_all1.pivot_column#10] - | | | | +-expr_list= - | | | | | +-pivot_column#10 := ColumnRef(type=STRING, column=$subquery1.pivot_column#8, is_correlated=TRUE) - | | | | +-input_scan= - | | | | +-SingleRowScan - | | | +-output_column_list=[$intersect_all1.pivot_column#10] - | | +-SetOperationItem - | | +-scan= - | | | +-ProjectScan - | | | +-column_list=[$intersect_all2.pivot_value#11] - | | | +-expr_list= - | | | | +-pivot_value#11 := ColumnRef(type=STRING, column=$subquery1.pivot_value#9, is_correlated=TRUE) - | | | +-input_scan= - | | | +-SingleRowScan - | | +-output_column_list=[$intersect_all2.pivot_value#11] - | +-ColumnRef(type=INT64, column=$subquery1.orig_arg#7) - | +-Literal(type=INT64, value=NULL) - +-input_scan= - +-ProjectScan - +-column_list=$subquery1.[orig_arg#7, pivot_column#8, pivot_value#9] - +-expr_list= - | +-orig_arg#7 := ColumnRef(type=INT64, column=$pivot.$pivot_expr_arg#6, is_correlated=TRUE) - | +-pivot_column#8 := ColumnRef(type=STRING, column=$pivot.$pivot_value#5, is_correlated=TRUE) - | +-pivot_value#9 := Literal(type=STRING, value="x", preserve_in_literal_remover=TRUE) - +-input_scan= - +-SingleRowScan -== - # Regression test for b/309024915 WITH Produce AS ( SELECT 'Kale' as product, 51 as sales, 'Q1' as quarter, 2020 as year UNION ALL diff --git a/zetasql/analyzer/testdata/positional_query_params.test b/zetasql/analyzer/testdata/positional_query_params.test index b2e4056bb..82f4330e3 100644 --- a/zetasql/analyzer/testdata/positional_query_params.test +++ b/zetasql/analyzer/testdata/positional_query_params.test @@ -326,6 +326,7 @@ QueryStmt | | +-TableScan(table=KeyValue) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=32-33 | +-column_ref= | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) +-limit= @@ -513,6 +514,7 @@ QueryStmt | | +-value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=99-100 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.key#3) +-limit= @@ -671,6 +673,7 @@ QueryStmt | | +-ColumnRef(type=ARRAY, column=$subquery1.b#4) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=140-141 | +-column_ref= | +-ColumnRef(type=INT64, column=$subquery1.c#5) +-limit= diff --git a/zetasql/analyzer/testdata/proto_braced_constructors.test b/zetasql/analyzer/testdata/proto_braced_constructors.test index a4e8b3625..ca2265829 100644 --- a/zetasql/analyzer/testdata/proto_braced_constructors.test +++ b/zetasql/analyzer/testdata/proto_braced_constructors.test @@ -1734,6 +1734,7 @@ QueryStmt | +-column_index_list=[0] +-order_by_item_list= +-OrderByItem + +-parse_location=156-176 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#5) diff --git a/zetasql/analyzer/testdata/query_params.test b/zetasql/analyzer/testdata/query_params.test index bead8b28c..b7a06a44d 100644 --- a/zetasql/analyzer/testdata/query_params.test +++ b/zetasql/analyzer/testdata/query_params.test @@ -249,6 +249,7 @@ QueryStmt | | +-value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=55-56 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.key#3) +-limit= @@ -282,6 +283,7 @@ QueryStmt | | +-value#4 := ColumnRef(type=STRING, column=KeyValue.Value#2) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=55-56 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.key#3) +-limit= diff --git a/zetasql/analyzer/testdata/safe_function.test b/zetasql/analyzer/testdata/safe_function.test index d564c3802..f2c15bfa5 100644 --- a/zetasql/analyzer/testdata/safe_function.test +++ b/zetasql/analyzer/testdata/safe_function.test @@ -692,6 +692,7 @@ QueryStmt | | | | | +-ColumnHolder(column=$array_offset.off#12) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=228-231 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.off#12) | | | +-Literal(type=ARRAY, value=NULL, has_explicit_type=TRUE) @@ -745,6 +746,7 @@ QueryStmt | | | | | +-Literal(type=DOUBLE, value=0) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=243-246 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.off#17) | | | +-Literal(type=ARRAY, value=NULL, has_explicit_type=TRUE) diff --git a/zetasql/analyzer/testdata/select_dotstar.test b/zetasql/analyzer/testdata/select_dotstar.test index 956642506..0be733f03 100644 --- a/zetasql/analyzer/testdata/select_dotstar.test +++ b/zetasql/analyzer/testdata/select_dotstar.test @@ -1302,6 +1302,7 @@ QueryStmt +-ColumnRef(type=DATE, column=my_table.modified_date#6) +-order_by_item_list= | +-OrderByItem + | +-parse_location=130-148 | +-column_ref= | | +-ColumnRef(type=DATE, column=my_table.modified_date#6) | +-is_descending=TRUE @@ -1751,9 +1752,11 @@ QueryStmt | +-ColumnRef(type=INT64, column=my_table.key#4) +-order_by_item_list= +-OrderByItem + | +-parse_location=175-176 | +-column_ref= | +-ColumnRef(type=INT64, column=$query.k#10) +-OrderByItem + +-parse_location=178-179 +-column_ref= +-ColumnRef(type=STRING, column=$query.value#11) == @@ -1842,9 +1845,11 @@ QueryStmt | +-value#13 := ColumnRef(type=STRING, column=$query.value#11) +-order_by_item_list= +-OrderByItem + | +-parse_location=184-185 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.k#12) +-OrderByItem + +-parse_location=187-188 +-column_ref= +-ColumnRef(type=STRING, column=$distinct.value#13) == @@ -2565,6 +2570,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +-order_by_item_list= +-OrderByItem + +-parse_location=62-78 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#3) == @@ -2649,6 +2655,7 @@ QueryStmt | +-WindowFrameExpr(boundary_type=UNBOUNDED FOLLOWING) +-order_by_item_list= +-OrderByItem + +-parse_location=123-137 +-column_ref= +-ColumnRef(type=INT64, column=$analytic.$analytic1#8) == @@ -2775,6 +2782,7 @@ QueryStmt | +-ColumnRef(type=INT64, column=Perf_Accounts_D.region#15) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=352-362 | +-column_ref= | | +-ColumnRef(type=INT64, column=Perf_Accounts_D.ratio#16) | +-is_descending=TRUE @@ -3141,6 +3149,7 @@ QueryStmt | +-x#4 := ColumnRef(type=INT64, column=$pre_groupby.x#3) +-order_by_item_list= +-OrderByItem + +-parse_location=86-87 +-column_ref= +-ColumnRef(type=INT64, column=$groupby.x#4) == @@ -3310,9 +3319,11 @@ QueryStmt | +-c#6 := ColumnRef(type=STRING, column=$pre_groupby.c#4) +-order_by_item_list= +-OrderByItem + | +-parse_location=77-78 | +-column_ref= | +-ColumnRef(type=INT32, column=$groupby.b#5) +-OrderByItem + +-parse_location=80-81 +-column_ref= +-ColumnRef(type=STRING, column=$groupby.c#6) == diff --git a/zetasql/analyzer/testdata/sql_function_inlining.test b/zetasql/analyzer/testdata/sql_function_inlining.test index 75b49fc04..282fdb49c 100644 --- a/zetasql/analyzer/testdata/sql_function_inlining.test +++ b/zetasql/analyzer/testdata/sql_function_inlining.test @@ -1266,6 +1266,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#6) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#6) | +-input_scan= @@ -1354,6 +1355,7 @@ QueryStmt | | | +-ColumnHolder(column=$array_offset.off#7) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=216-219 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#7) | +-input_scan= diff --git a/zetasql/analyzer/testdata/sql_table_function_inlining.test b/zetasql/analyzer/testdata/sql_table_function_inlining.test index 3f4cc13e0..c4a42209b 100644 --- a/zetasql/analyzer/testdata/sql_table_function_inlining.test +++ b/zetasql/analyzer/testdata/sql_table_function_inlining.test @@ -1,5 +1,5 @@ [default enabled_ast_rewrites=ALL] -[default language_features=TABLE_VALUED_FUNCTIONS,V_1_4_WITH_EXPRESSION] +[default language_features=TABLE_VALUED_FUNCTIONS,V_1_4_WITH_EXPRESSION,V_1_1_ORDER_BY_IN_AGGREGATE] [default no_java] SELECT {{*|a|b}} FROM NullarySelect(); @@ -297,6 +297,7 @@ QueryStmt | | | +-Literal(type=INT64, value=1) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=231-234 | | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.off#5) | +-input_scan= @@ -3837,3 +3838,135 @@ QueryStmt | +-ColumnRef(type=INT64, column=arg_table.value#7) +-has_using=TRUE +== + +# Skip unparser because of b/224585752 +[no_run_unparser] +# Make sure order by in aggregate is not mangled by ORDER_BY_AND_LIMIT_IN_AGGREGATE rewriter +SELECT * FROM UnaryTableArgAggregatedWithOrderBy((SELECT a FROM abTable)); +-- +QueryStmt ++-output_column_list= +| +-UnaryTableArgAggregatedWithOrderBy.arr#3 AS arr [ARRAY] ++-query= + +-ProjectScan + +-column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3] + +-input_scan= + +-TVFScan + +-column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3] + +-tvf=UnaryTableArgAggregatedWithOrderBy((TABLE arg0) -> TABLE>) + +-signature=(TABLE) -> TABLE> + +-argument_list= + | +-FunctionArgument + | +-scan= + | | +-ProjectScan + | | +-column_list=[abTable.a#1] + | | +-input_scan= + | | +-TableScan(column_list=[abTable.a#1], table=abTable, column_index_list=[0]) + | +-argument_column_list=[abTable.a#1] + +-column_index_list=[0] + + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-UnaryTableArgAggregatedWithOrderBy.arr#3 AS arr [ARRAY] ++-query= + +-ProjectScan + +-column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3] + +-input_scan= + +-WithScan + +-column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="arg0" + | +-with_subquery= + | +-ProjectScan + | +-column_list=[abTable.a#1] + | +-input_scan= + | +-TableScan(column_list=[abTable.a#1], table=abTable, column_index_list=[0]) + +-query= + +-WithScan + +-column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3] + +-with_entry_list= + | +-WithEntry + | +-with_query_name="t" + | +-with_subquery= + | +-ProjectScan + | +-column_list=[$aggregate.arr#4] + | +-input_scan= + | +-ProjectScan + | +-column_list=[$aggregate.arr#4] + | +-expr_list= + | | +-arr#4 := + | | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#7) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#10] + | | +-expr_list= + | | | +-injected#10 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#9) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#9) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#9] + | | +-expr_list= + | | | +-$out#9 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#7, is_correlated=TRUE) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.a#6] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-ProjectScan + | | | | +-column_list=[$agg_rewriter.a#6] + | | | | +-expr_list= + | | | | | +-a#6 := + | | | | | +-GetStructField + | | | | | +-type=INT64 + | | | | | +-expr= + | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#8) + | | | | | +-field_idx=0 + | | | | +-input_scan= + | | | | +-ArrayScan + | | | | +-column_list=[$agg_rewriter.$struct#8] + | | | | +-array_expr_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#7, is_correlated=TRUE) + | | | | +-element_column_list=[$agg_rewriter.$struct#8] + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=155-156 + | | | +-column_ref= + | | | +-ColumnRef(type=INT64, column=$agg_rewriter.a#6) + | | +-input_scan= + | | +-SingleRowScan + | +-input_scan= + | +-AggregateScan + | +-column_list=[$agg_rewriter.$array#7] + | +-input_scan= + | | +-WithRefScan(column_list=[arg0.a#5], with_query_name="arg0") + | +-aggregate_list= + | +-$array#7 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=INT64, column=arg0.a#5) + +-query= + +-ProjectScan + +-column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3] + +-input_scan= + +-WithRefScan(column_list=[UnaryTableArgAggregatedWithOrderBy.arr#3], with_query_name="t") diff --git a/zetasql/analyzer/testdata/sql_template_function_inlining.test b/zetasql/analyzer/testdata/sql_template_function_inlining.test index adbbfe9e4..5db227d98 100644 --- a/zetasql/analyzer/testdata/sql_template_function_inlining.test +++ b/zetasql/analyzer/testdata/sql_template_function_inlining.test @@ -72,6 +72,7 @@ FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) | +-ColumnHolder(column=$array_offset.offset#2) +-order_by_item_list= +-OrderByItem + +-parse_location=181-192 +-column_ref= | +-ColumnRef(type=INT64, column=$array_offset.offset#2) +-is_descending=TRUE @@ -113,6 +114,7 @@ QueryStmt | | +-ColumnHolder(column=$array_offset.offset#3) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=181-192 | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#3) | +-is_descending=TRUE @@ -158,6 +160,7 @@ FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) | +-ColumnHolder(column=$array_offset.offset#2) +-order_by_item_list= +-OrderByItem + +-parse_location=181-192 +-column_ref= | +-ColumnRef(type=INT64, column=$array_offset.offset#2) +-is_descending=TRUE @@ -199,6 +202,7 @@ QueryStmt | | +-ColumnHolder(column=$array_offset.offset#3) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=181-192 | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#3) | +-is_descending=TRUE @@ -244,6 +248,7 @@ FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) | +-ColumnHolder(column=$array_offset.offset#2) +-order_by_item_list= +-OrderByItem + +-parse_location=181-192 +-column_ref= | +-ColumnRef(type=INT64, column=$array_offset.offset#2) +-is_descending=TRUE @@ -285,6 +290,7 @@ QueryStmt | | +-ColumnHolder(column=$array_offset.offset#3) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=181-192 | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#3) | +-is_descending=TRUE @@ -330,6 +336,7 @@ FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) | +-ColumnHolder(column=$array_offset.offset#2) +-order_by_item_list= +-OrderByItem + +-parse_location=181-192 +-column_ref= | +-ColumnRef(type=INT64, column=$array_offset.offset#2) +-is_descending=TRUE @@ -371,6 +378,7 @@ QueryStmt | | +-ColumnHolder(column=$array_offset.offset#3) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=181-192 | +-column_ref= | | +-ColumnRef(type=INT64, column=$array_offset.offset#3) | +-is_descending=TRUE @@ -559,6 +567,7 @@ QueryStmt | | | | | +-ColumnHolder(column=$array_offset.off#9) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=216-219 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.off#9) | | | +-input_scan= diff --git a/zetasql/analyzer/testdata/string_aggregate.test b/zetasql/analyzer/testdata/string_aggregate.test new file mode 100644 index 000000000..3d79afa82 --- /dev/null +++ b/zetasql/analyzer/testdata/string_aggregate.test @@ -0,0 +1,1200 @@ +[default language_features=V_1_1_ORDER_BY_IN_AGGREGATE] +[default enabled_ast_rewrites=DEFAULTS,+ORDER_BY_AND_LIMIT_IN_AGGREGATE] + +[no_enable_literal_replacement] +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_HAVING_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE,V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE] +# Simple ARRAY_AGG with ORDER BY with all the features. +# no_enable_literal_replacement: Distinct and ORDER BY must match exactly, we +# cannot replace just one literal with parameter +SELECT STRING_AGG(DISTINCT a || "extra", ", " HAVING MAX 3 ORDER BY a || "extra" LIMIT 10) +FROM TestTable, TestTable.KitchenSink.repeated_string_val as a, TestTable.KitchenSink.repeated_bytes_val as b +GROUP BY CHAR_LENGTH(a) - BYTE_LENGTH(b) +-- + +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [STRING] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-AggregateScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$array.a#4) + | | +-Literal(type=STRING, value="extra") + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bytes_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-group_by_list= + | +-$groupbycol1#8 := + | +-FunctionCall(ZetaSQL:$subtract(INT64, INT64) -> INT64) + | +-FunctionCall(ZetaSQL:char_length(STRING) -> INT64) + | | +-ColumnRef(type=STRING, column=$array.a#4) + | +-FunctionCall(ZetaSQL:byte_length(BYTES) -> INT64) + | +-ColumnRef(type=BYTES, column=$array.b#5) + +-aggregate_list= + +-$agg1#7 := + +-AggregateFunctionCall(ZetaSQL:string_agg(STRING, STRING) -> STRING) + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) + +-Literal(type=STRING, value=", ") + +-distinct=TRUE + +-having_modifier= + | +-AggregateHavingModifier + | +-kind=MAX + | +-having_expr= + | +-Literal(type=INT64, value=3) + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=251-263 + | +-column_ref= + | +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) + +-limit= + +-Literal(type=INT64, value=10) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#7 AS `$col1` [STRING] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-input_scan= + +-ProjectScan + +-column_list=[$aggregate.$agg1#7] + +-expr_list= + | +-$agg1#7 := + | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#14] + | | +-expr_list= + | | | +-injected#14 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#13) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#13) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#13] + | | +-expr_list= + | | | +-$out#13 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | +-subquery= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.distinct.arg#12] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.distinct.arg#12] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-AggregateScan + | | | | | +-column_list=[$agg_rewriter.distinct.arg#12] + | | | | | +-input_scan= + | | | | | | +-FilterScan + | | | | | | +-column_list=[$agg_rewriter.$orderbycol1#9] + | | | | | | +-input_scan= + | | | | | | | +-ProjectScan + | | | | | | | +-column_list=[$agg_rewriter.$orderbycol1#9] + | | | | | | | +-expr_list= + | | | | | | | | +-$orderbycol1#9 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=STRING + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT<`$orderbycol1_0` STRING>, column=$agg_rewriter.$struct#11) + | | | | | | | | +-field_idx=0 + | | | | | | | +-input_scan= + | | | | | | | +-ArrayScan + | | | | | | | +-column_list=[$agg_rewriter.$struct#11] + | | | | | | | +-array_expr_list= + | | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | | | | | +-element_column_list=[$agg_rewriter.$struct#11] + | | | | | | +-filter_expr= + | | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.$orderbycol1#9) + | | | | | +-group_by_list= + | | | | | +-distinct.arg#12 := ColumnRef(type=STRING, column=$agg_rewriter.$orderbycol1#9) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=251-263 + | | | | +-column_ref= + | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.distinct.arg#12) + | | | +-limit= + | | | | +-Literal(type=INT64, value=10) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-Literal(type=STRING, value=", ") + +-input_scan= + +-AggregateScan + +-column_list=[$agg_rewriter.$array#10] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$array.a#4) + | | +-Literal(type=STRING, value="extra") + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bytes_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-group_by_list= + | +-$groupbycol1#8 := + | +-FunctionCall(ZetaSQL:$subtract(INT64, INT64) -> INT64) + | +-FunctionCall(ZetaSQL:char_length(STRING) -> INT64) + | | +-ColumnRef(type=STRING, column=$array.a#4) + | +-FunctionCall(ZetaSQL:byte_length(BYTES) -> INT64) + | +-ColumnRef(type=BYTES, column=$array.b#5) + +-aggregate_list= + +-$array#10 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT<`$orderbycol1_0` STRING>) -> ARRAY>) + +-MakeStruct + +-type=STRUCT<`$orderbycol1_0` STRING> + +-field_list= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#6) + +-having_modifier= + +-AggregateHavingModifier + +-kind=MAX + +-having_expr= + +-Literal(type=INT64, value=3) +== + +# Duplicate ARRAY_AGGs in the same AggregateScan +SELECT STRING_AGG(a, "x" ORDER BY a), STRING_AGG(a, ", " ORDER BY a) +FROM TestTable, TestTable.KitchenSink.repeated_string_val as a +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [STRING] +| +-$aggregate.$agg2#6 AS `$col2` [STRING] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:string_agg(STRING, STRING) -> STRING) + | +-ColumnRef(type=STRING, column=$array.a#4) + | +-Literal(type=STRING, value="x") + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=34-35 + | +-column_ref= + | +-ColumnRef(type=STRING, column=$array.a#4) + +-$agg2#6 := + +-AggregateFunctionCall(ZetaSQL:string_agg(STRING, STRING) -> STRING) + +-ColumnRef(type=STRING, column=$array.a#4) + +-Literal(type=STRING, value=", ") + +-order_by_item_list= + +-OrderByItem + +-parse_location=66-67 + +-column_ref= + +-ColumnRef(type=STRING, column=$array.a#4) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [STRING] +| +-$aggregate.$agg2#6 AS `$col2` [STRING] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#6] + +-expr_list= + | +-$agg1#5 := + | | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#8) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#15] + | | | +-expr_list= + | | | | +-injected#15 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#10) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#10) + | | | | +-Literal(type=ARRAY, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#10] + | | | +-expr_list= + | | | | +-$out#10 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#8, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.a#7] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=[$agg_rewriter.a#7] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=[$agg_rewriter.a#7] + | | | | | | +-expr_list= + | | | | | | | +-a#7 := + | | | | | | | +-GetStructField + | | | | | | | +-type=STRING + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#9) + | | | | | | | +-field_idx=0 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#9] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#8, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#9] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#7) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=34-35 + | | | | +-column_ref= + | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#7) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-Literal(type=STRING, value="x") + | +-$agg2#6 := + | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#16] + | | +-expr_list= + | | | +-injected#16 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#14) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#14] + | | +-expr_list= + | | | +-$out#14 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.a#11] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-FilterScan + | | | | +-column_list=[$agg_rewriter.a#11] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=[$agg_rewriter.a#11] + | | | | | +-expr_list= + | | | | | | +-a#11 := + | | | | | | +-GetStructField + | | | | | | +-type=STRING + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#13) + | | | | | | +-field_idx=0 + | | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$agg_rewriter.$struct#13] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#12, is_correlated=TRUE) + | | | | | +-element_column_list=[$agg_rewriter.$struct#13] + | | | | +-filter_expr= + | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#11) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=66-67 + | | | +-column_ref= + | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#11) + | | +-input_scan= + | | +-SingleRowScan + | +-Literal(type=STRING, value=", ") + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#8, $array#12] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$array#8 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=STRING, column=$array.a#4) + +-$array#12 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=STRING, column=$array.a#4) + +== + +# Slight different ARRAY_AGGs in the same AggregateScan +SELECT STRING_AGG(a, "," ORDER BY b), STRING_AGG(b, b", " ORDER BY a) +FROM TestTable, TestTable.KitchenSink.repeated_string_val as a, TestTable.KitchenSink.repeated_bytes_val as b +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#6 AS `$col1` [STRING] +| +-$aggregate.$agg2#7 AS `$col2` [BYTES] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bytes_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-aggregate_list= + +-$agg1#6 := + | +-AggregateFunctionCall(ZetaSQL:string_agg(STRING, STRING) -> STRING) + | +-ColumnRef(type=STRING, column=$array.a#4) + | +-Literal(type=STRING, value=",") + | +-order_by_item_list= + | +-OrderByItem + | +-parse_location=34-35 + | +-column_ref= + | +-ColumnRef(type=BYTES, column=$array.b#5) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:string_agg(BYTES, BYTES) -> BYTES) + +-ColumnRef(type=BYTES, column=$array.b#5) + +-Literal(type=BYTES, value=b", ") + +-order_by_item_list= + +-OrderByItem + +-parse_location=67-68 + +-column_ref= + +-ColumnRef(type=STRING, column=$array.a#4) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#6 AS `$col1` [STRING] +| +-$aggregate.$agg2#7 AS `$col2` [BYTES] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#6, $agg2#7] + +-expr_list= + | +-$agg1#6 := + | | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#18] + | | | +-expr_list= + | | | | +-injected#18 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#12) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#12) + | | | | +-Literal(type=ARRAY, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#12] + | | | +-expr_list= + | | | | +-$out#12 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.a#8] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=$agg_rewriter.[a#8, b#9] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[a#8, b#9] + | | | | | | +-expr_list= + | | | | | | | +-a#8 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=STRING + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#11) + | | | | | | | | +-field_idx=0 + | | | | | | | +-b#9 := + | | | | | | | +-GetStructField + | | | | | | | +-type=BYTES + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#11) + | | | | | | | +-field_idx=1 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#11] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#10, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#11] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#8) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=34-35 + | | | | +-column_ref= + | | | | +-ColumnRef(type=BYTES, column=$agg_rewriter.b#9) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-Literal(type=STRING, value=",") + | +-$agg2#7 := + | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, BYTES) -> BYTES) + | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#15) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#19] + | | +-expr_list= + | | | +-injected#19 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#17) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#17) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#17] + | | +-expr_list= + | | | +-$out#17 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#15, is_correlated=TRUE) + | | | +-subquery= + | | | +-OrderByScan + | | | +-column_list=[$agg_rewriter.b#14] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-FilterScan + | | | | +-column_list=$agg_rewriter.[a#13, b#14] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=$agg_rewriter.[a#13, b#14] + | | | | | +-expr_list= + | | | | | | +-a#13 := + | | | | | | | +-GetStructField + | | | | | | | +-type=STRING + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#16) + | | | | | | | +-field_idx=0 + | | | | | | +-b#14 := + | | | | | | +-GetStructField + | | | | | | +-type=BYTES + | | | | | | +-expr= + | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#16) + | | | | | | +-field_idx=1 + | | | | | +-input_scan= + | | | | | +-ArrayScan + | | | | | +-column_list=[$agg_rewriter.$struct#16] + | | | | | +-array_expr_list= + | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#15, is_correlated=TRUE) + | | | | | +-element_column_list=[$agg_rewriter.$struct#16] + | | | | +-filter_expr= + | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:$is_null(BYTES) -> BOOL) + | | | | +-ColumnRef(type=BYTES, column=$agg_rewriter.b#14) + | | | +-order_by_item_list= + | | | +-OrderByItem + | | | +-parse_location=67-68 + | | | +-column_ref= + | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#13) + | | +-input_scan= + | | +-SingleRowScan + | +-Literal(type=BYTES, value=b", ") + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#10, $array#15] + +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $array.b#5] + | +-input_scan= + | | +-ArrayScan + | | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | | +-input_scan= + | | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | | +-array_expr_list= + | | | +-GetProtoField + | | | +-type=ARRAY + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | | +-field_descriptor=repeated_string_val + | | | +-default_value=[] + | | +-element_column_list=[$array.a#4] + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_bytes_val + | | +-default_value=[] + | +-element_column_list=[$array.b#5] + +-aggregate_list= + +-$array#10 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=STRING, column=$array.a#4) + | +-ColumnRef(type=BYTES, column=$array.b#5) + +-$array#15 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=STRING, column=$array.a#4) + +-ColumnRef(type=BYTES, column=$array.b#5) +== + +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE] + +# STRING_AGG with limits +SELECT STRING_AGG(a, ", " LIMIT 2), STRING_AGG(a, "," ORDER BY CHAR_LENGTH(a)+1 LIMIT 3) +FROM TestTable, TestTable.KitchenSink.repeated_string_val as a +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [STRING] +| +-$aggregate.$agg2#7 AS `$col2` [STRING] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:char_length(STRING) -> INT64) + | | | +-ColumnRef(type=STRING, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:string_agg(STRING, STRING) -> STRING) + | +-ColumnRef(type=STRING, column=$array.a#4) + | +-Literal(type=STRING, value=", ") + | +-limit= + | +-Literal(type=INT64, value=2) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:string_agg(STRING, STRING) -> STRING) + +-ColumnRef(type=STRING, column=$array.a#4) + +-Literal(type=STRING, value=",") + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=88-104 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +-limit= + +-Literal(type=INT64, value=3) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [STRING] +| +-$aggregate.$agg2#7 AS `$col2` [STRING] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-expr_list= + | +-$agg1#5 := + | | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#17] + | | | +-expr_list= + | | | | +-injected#17 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | | | | +-Literal(type=ARRAY, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#11] + | | | +-expr_list= + | | | | +-$out#11 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-LimitOffsetScan + | | | | +-column_list=[$agg_rewriter.a#8] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=[$agg_rewriter.a#8] + | | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=[$agg_rewriter.a#8] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=[$agg_rewriter.a#8] + | | | | | | +-expr_list= + | | | | | | | +-a#8 := + | | | | | | | +-GetStructField + | | | | | | | +-type=STRING + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#10) + | | | | | | | +-field_idx=0 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#10] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#10] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#8) + | | | | +-limit= + | | | | | +-Literal(type=INT64, value=2) + | | | | +-offset= + | | | | +-Literal(type=INT64, value=0) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-Literal(type=STRING, value=", ") + | +-$agg2#7 := + | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#18] + | | +-expr_list= + | | | +-injected#18 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#16) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#16) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#16] + | | +-expr_list= + | | | +-$out#16 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14, is_correlated=TRUE) + | | | +-subquery= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.a#12] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.a#12] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=$agg_rewriter.[a#12, $orderbycol1#13] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[a#12, $orderbycol1#13] + | | | | | | +-expr_list= + | | | | | | | +-a#12 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=STRING + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#15) + | | | | | | | | +-field_idx=0 + | | | | | | | +-$orderbycol1#13 := + | | | | | | | +-GetStructField + | | | | | | | +-type=INT64 + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#15) + | | | | | | | +-field_idx=1 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#15] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#15] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#12) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=88-104 + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#13) + | | | +-limit= + | | | | +-Literal(type=INT64, value=3) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-Literal(type=STRING, value=",") + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#9, $array#14] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:char_length(STRING) -> INT64) + | | | +-ColumnRef(type=STRING, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$array#9 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=STRING, column=$array.a#4) + +-$array#14 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=STRING, column=$array.a#4) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +== + +[language_features=V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_LIMIT_IN_AGGREGATE] + +# STRING_AGG one arg +SELECT STRING_AGG(a LIMIT 2), STRING_AGG(a ORDER BY CHAR_LENGTH(a)+1 LIMIT 3) +FROM TestTable, TestTable.KitchenSink.repeated_string_val as a +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [STRING] +| +-$aggregate.$agg2#7 AS `$col2` [STRING] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-AggregateScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:char_length(STRING) -> INT64) + | | | +-ColumnRef(type=STRING, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$agg1#5 := + | +-AggregateFunctionCall(ZetaSQL:string_agg(STRING) -> STRING) + | +-ColumnRef(type=STRING, column=$array.a#4) + | +-limit= + | +-Literal(type=INT64, value=2) + +-$agg2#7 := + +-AggregateFunctionCall(ZetaSQL:string_agg(STRING) -> STRING) + +-ColumnRef(type=STRING, column=$array.a#4) + +-order_by_item_list= + | +-OrderByItem + | +-parse_location=73-89 + | +-column_ref= + | +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) + +-limit= + +-Literal(type=INT64, value=3) + +[REWRITTEN AST] +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#5 AS `$col1` [STRING] +| +-$aggregate.$agg2#7 AS `$col2` [STRING] ++-query= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-input_scan= + +-ProjectScan + +-column_list=$aggregate.[$agg1#5, $agg2#7] + +-expr_list= + | +-$agg1#5 := + | | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=SCALAR + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9) + | | | +-subquery= + | | | +-ProjectScan + | | | +-column_list=[$with_expr.injected#17] + | | | +-expr_list= + | | | | +-injected#17 := + | | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | | | | | +-Literal(type=INT64, value=1) + | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | | | | +-Literal(type=ARRAY, value=NULL) + | | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[null_if_empty_array.$out#11] + | | | +-expr_list= + | | | | +-$out#11 := + | | | | +-SubqueryExpr + | | | | +-type=ARRAY + | | | | +-subquery_type=ARRAY + | | | | +-parameter_list= + | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | +-subquery= + | | | | +-LimitOffsetScan + | | | | +-column_list=[$agg_rewriter.a#8] + | | | | +-input_scan= + | | | | | +-ProjectScan + | | | | | +-column_list=[$agg_rewriter.a#8] + | | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=[$agg_rewriter.a#8] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=[$agg_rewriter.a#8] + | | | | | | +-expr_list= + | | | | | | | +-a#8 := + | | | | | | | +-GetStructField + | | | | | | | +-type=STRING + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#10) + | | | | | | | +-field_idx=0 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#10] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#9, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#10] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#8) + | | | | +-limit= + | | | | | +-Literal(type=INT64, value=2) + | | | | +-offset= + | | | | +-Literal(type=INT64, value=0) + | | | +-input_scan= + | | | +-SingleRowScan + | | +-Literal(type=STRING, value=",") + | +-$agg2#7 := + | +-FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) + | +-SubqueryExpr + | | +-type=ARRAY + | | +-subquery_type=SCALAR + | | +-parameter_list= + | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14) + | | +-subquery= + | | +-ProjectScan + | | +-column_list=[$with_expr.injected#18] + | | +-expr_list= + | | | +-injected#18 := + | | | +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + | | | +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | | | | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#16) + | | | | +-Literal(type=INT64, value=1) + | | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#16) + | | | +-Literal(type=ARRAY, value=NULL) + | | +-input_scan= + | | +-ProjectScan + | | +-column_list=[null_if_empty_array.$out#16] + | | +-expr_list= + | | | +-$out#16 := + | | | +-SubqueryExpr + | | | +-type=ARRAY + | | | +-subquery_type=ARRAY + | | | +-parameter_list= + | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14, is_correlated=TRUE) + | | | +-subquery= + | | | +-LimitOffsetScan + | | | +-column_list=[$agg_rewriter.a#12] + | | | +-is_ordered=TRUE + | | | +-input_scan= + | | | | +-OrderByScan + | | | | +-column_list=[$agg_rewriter.a#12] + | | | | +-is_ordered=TRUE + | | | | +-input_scan= + | | | | | +-FilterScan + | | | | | +-column_list=$agg_rewriter.[a#12, $orderbycol1#13] + | | | | | +-input_scan= + | | | | | | +-ProjectScan + | | | | | | +-column_list=$agg_rewriter.[a#12, $orderbycol1#13] + | | | | | | +-expr_list= + | | | | | | | +-a#12 := + | | | | | | | | +-GetStructField + | | | | | | | | +-type=STRING + | | | | | | | | +-expr= + | | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#15) + | | | | | | | | +-field_idx=0 + | | | | | | | +-$orderbycol1#13 := + | | | | | | | +-GetStructField + | | | | | | | +-type=INT64 + | | | | | | | +-expr= + | | | | | | | | +-ColumnRef(type=STRUCT, column=$agg_rewriter.$struct#15) + | | | | | | | +-field_idx=1 + | | | | | | +-input_scan= + | | | | | | +-ArrayScan + | | | | | | +-column_list=[$agg_rewriter.$struct#15] + | | | | | | +-array_expr_list= + | | | | | | | +-ColumnRef(type=ARRAY>, column=$agg_rewriter.$array#14, is_correlated=TRUE) + | | | | | | +-element_column_list=[$agg_rewriter.$struct#15] + | | | | | +-filter_expr= + | | | | | +-FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) + | | | | | +-FunctionCall(ZetaSQL:$is_null(STRING) -> BOOL) + | | | | | +-ColumnRef(type=STRING, column=$agg_rewriter.a#12) + | | | | +-order_by_item_list= + | | | | +-OrderByItem + | | | | +-parse_location=73-89 + | | | | +-column_ref= + | | | | +-ColumnRef(type=INT64, column=$agg_rewriter.$orderbycol1#13) + | | | +-limit= + | | | | +-Literal(type=INT64, value=3) + | | | +-offset= + | | | +-Literal(type=INT64, value=0) + | | +-input_scan= + | | +-SingleRowScan + | +-Literal(type=STRING, value=",") + +-input_scan= + +-AggregateScan + +-column_list=$agg_rewriter.[$array#9, $array#14] + +-input_scan= + | +-ProjectScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4, $orderby.$orderbycol1#6] + | +-expr_list= + | | +-$orderbycol1#6 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-FunctionCall(ZetaSQL:char_length(STRING) -> INT64) + | | | +-ColumnRef(type=STRING, column=$array.a#4) + | | +-Literal(type=INT64, value=1) + | +-input_scan= + | +-ArrayScan + | +-column_list=[TestTable.KitchenSink#3, $array.a#4] + | +-input_scan= + | | +-TableScan(column_list=[TestTable.KitchenSink#3], table=TestTable, column_index_list=[2]) + | +-array_expr_list= + | | +-GetProtoField + | | +-type=ARRAY + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestTable.KitchenSink#3) + | | +-field_descriptor=repeated_string_val + | | +-default_value=[] + | +-element_column_list=[$array.a#4] + +-aggregate_list= + +-$array#9 := + | +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + | +-MakeStruct + | +-type=STRUCT + | +-field_list= + | +-ColumnRef(type=STRING, column=$array.a#4) + +-$array#14 := + +-AggregateFunctionCall(ZetaSQL:array_agg(STRUCT) -> ARRAY>) + +-MakeStruct + +-type=STRUCT + +-field_list= + +-ColumnRef(type=STRING, column=$array.a#4) + +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) diff --git a/zetasql/analyzer/testdata/struct_braced_constructors.test b/zetasql/analyzer/testdata/struct_braced_constructors.test index c46d7dfe4..96063b2fd 100644 --- a/zetasql/analyzer/testdata/struct_braced_constructors.test +++ b/zetasql/analyzer/testdata/struct_braced_constructors.test @@ -1645,6 +1645,7 @@ QueryStmt | +-column_index_list=[0] +-order_by_item_list= +-OrderByItem + +-parse_location=157-177 +-column_ref= +-ColumnRef(type=INT32, column=$orderby.$orderbycol1#5) diff --git a/zetasql/analyzer/testdata/tablesample.test b/zetasql/analyzer/testdata/tablesample.test index 496542db6..51659bcf5 100644 --- a/zetasql/analyzer/testdata/tablesample.test +++ b/zetasql/analyzer/testdata/tablesample.test @@ -589,9 +589,9 @@ select * from TestTable T, T.KitchenSink.repeated_int32_val TABLESAMPLE RESERVOIR (1 ROWS) -- -ERROR: TABLESAMPLE cannot be used with arrays [at 2:19] -from TestTable T, T.KitchenSink.repeated_int32_val - ^ +ERROR: TABLESAMPLE is not allowed with array scans [at 3:6] + TABLESAMPLE RESERVOIR (1 ROWS) + ^ == select val @@ -631,16 +631,43 @@ QueryStmt select * from UNNEST([1, 2, 3]) TABLESAMPLE RESERVOIR (1 ROWS) -- -ERROR: TABLESAMPLE cannot be used with arrays [at 1:15] +ERROR: TABLESAMPLE is not allowed with array scans [at 1:33] select * from UNNEST([1, 2, 3]) TABLESAMPLE RESERVOIR (1 ROWS) - ^ + ^ == select * from UNNEST([1, 2, 3]) WITH OFFSET pos TABLESAMPLE RESERVOIR (1 ROWS) -- -ERROR: TABLESAMPLE cannot be used with arrays [at 1:15] +ERROR: TABLESAMPLE is not allowed with array scans [at 1:49] select * from UNNEST([1, 2, 3]) WITH OFFSET pos TABLESAMPLE RESERVOIR (1 ROWS) - ^ + ^ +== + +# 2 UNNESTs +select * from UNNEST(['a', 'b', 'c']) AS k, UNNEST([1, 2, 3]) AS v TABLESAMPLE RESERVOIR (1 ROWS) +-- + +ERROR: TABLESAMPLE is not allowed with array scans [at 1:68] +...a', 'b', 'c']) AS k, UNNEST([1, 2, 3]) AS v TABLESAMPLE RESERVOIR (1 ROWS) + ^ +== + +# Table with UNNEST +select *, m from keyvalue, UNNEST([1, 2, 3]) AS v TABLESAMPLE RESERVOIR (1 ROWS) +-- + +ERROR: TABLESAMPLE is not allowed with array scans [at 1:51] +select *, m from keyvalue, UNNEST([1, 2, 3]) AS v TABLESAMPLE RESERVOIR (1 ROWS) + ^ +== + +# Table with 2 UNNESTs +select * from keyvalue, UNNEST(['a', 'b', 'c']) AS k, UNNEST([1, 2, 3]) AS v TABLESAMPLE RESERVOIR (1 ROWS) +-- + +ERROR: TABLESAMPLE is not allowed with array scans [at 1:78] +...a', 'b', 'c']) AS k, UNNEST([1, 2, 3]) AS v TABLESAMPLE RESERVOIR (1 ROWS) + ^ == select * from (select * from UNNEST([1, 2, 3])) TABLESAMPLE RESERVOIR (1 ROWS) @@ -719,6 +746,7 @@ QueryStmt | | +-unit=ROWS | +-order_by_item_list= | +-OrderByItem + | +-parse_location=136-139 | +-column_ref= | +-ColumnRef(type=INT64, column=KeyValue.Key#1) +-limit= diff --git a/zetasql/analyzer/testdata/ternary_rewriter_functions.test b/zetasql/analyzer/testdata/ternary_rewriter_functions.test index 985024137..f87c5bbfb 100644 --- a/zetasql/analyzer/testdata/ternary_rewriter_functions.test +++ b/zetasql/analyzer/testdata/ternary_rewriter_functions.test @@ -105,6 +105,7 @@ QueryStmt | | | | +-ColumnRef(type=INT64, column=$with_expr.end_offset#6, is_correlated=TRUE) | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=770-773 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.idx#8) | | +-input_scan= @@ -243,6 +244,7 @@ QueryStmt | | | | | +-ColumnRef(type=INT64, column=$with_expr.end_offset#6, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=782-785 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.idx#8) | | | +-input_scan= @@ -392,6 +394,7 @@ QueryStmt | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=321-327 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | +-limit= @@ -427,6 +430,7 @@ QueryStmt | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=537-548 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | +-is_descending=TRUE @@ -527,6 +531,7 @@ QueryStmt | | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=333-339 | | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | | +-limit= @@ -562,6 +567,7 @@ QueryStmt | | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=549-560 | | | | | +-column_ref= | | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | | +-is_descending=TRUE @@ -668,6 +674,7 @@ QueryStmt | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=316-322 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | +-limit= @@ -703,6 +710,7 @@ QueryStmt | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | +-parse_location=527-538 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | +-is_descending=TRUE @@ -804,6 +812,7 @@ QueryStmt | | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=328-334 | | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#6) | | | | +-limit= @@ -839,6 +848,7 @@ QueryStmt | | | | | | +-ColumnRef(type=STRING, column=$subquery1.target_element#4, is_correlated=TRUE) | | | | | +-order_by_item_list= | | | | | +-OrderByItem + | | | | | +-parse_location=539-550 | | | | | +-column_ref= | | | | | | +-ColumnRef(type=INT64, column=$array_offset.offset#8) | | | | | +-is_descending=TRUE diff --git a/zetasql/analyzer/testdata/tvf_relation_args.test b/zetasql/analyzer/testdata/tvf_relation_args.test index fcb60892f..37c0c005e 100644 --- a/zetasql/analyzer/testdata/tvf_relation_args.test +++ b/zetasql/analyzer/testdata/tvf_relation_args.test @@ -4061,6 +4061,7 @@ QueryStmt | | +-has_using=TRUE | +-order_by_item_list= | +-OrderByItem + | +-parse_location=111-114 | +-column_ref= | +-ColumnRef(type=INT64, column=w1.key#5) +-limit= @@ -7550,3 +7551,46 @@ ERROR: No matching signature for tvf_required_named_optional_required_tables Argument 3: expected TABLE, found NULL [at 1:15] select * from tvf_required_named_optional_required_tables((select '1'), ^ +== + +select * from tvf_templated_select_any_scalar_arg((select ["abc"])); +-- +QueryStmt ++-output_column_list= +| +-tvf_templated_select_any_scalar_arg.x#2 AS x [ARRAY] ++-query= + +-ProjectScan + +-column_list=[tvf_templated_select_any_scalar_arg.x#2] + +-input_scan= + +-TVFScan + +-column_list=[tvf_templated_select_any_scalar_arg.x#2] + +-tvf=tvf_templated_select_any_scalar_arg((ANY TYPE) -> ANY TABLE) + +-signature=(ARRAY) -> TABLE> + +-argument_list= + | +-FunctionArgument + | +-expr= + | +-SubqueryExpr + | +-type=ARRAY + | +-subquery_type=SCALAR + | +-subquery= + | +-ProjectScan + | +-column_list=[$expr_subquery.$col1#1] + | +-expr_list= + | | +-$col1#1 := Literal(type=ARRAY, value=["abc"]) + | +-input_scan= + | +-SingleRowScan + +-column_index_list=[0] + +With Templated SQL TVF signature: + tvf_templated_select_any_scalar_arg(ARRAY) -> TABLE> +containing resolved templated query: +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [ARRAY] ++-query= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := ArgumentRef(type=ARRAY, name="x") + +-input_scan= + +-SingleRowScan diff --git a/zetasql/analyzer/testdata/tvf_scalar_args.test b/zetasql/analyzer/testdata/tvf_scalar_args.test index 6a351291d..d91b2a20e 100644 --- a/zetasql/analyzer/testdata/tvf_scalar_args.test +++ b/zetasql/analyzer/testdata/tvf_scalar_args.test @@ -1573,6 +1573,7 @@ QueryStmt | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1], alias="kv") +-order_by_item_list= +-OrderByItem + +-parse_location=35-118 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#6) == diff --git a/zetasql/analyzer/testdata/unary_rewriter_functions.test b/zetasql/analyzer/testdata/unary_rewriter_functions.test index bf19309b9..b4695afba 100644 --- a/zetasql/analyzer/testdata/unary_rewriter_functions.test +++ b/zetasql/analyzer/testdata/unary_rewriter_functions.test @@ -365,6 +365,7 @@ QueryStmt | | +-TableScan(column_list=[KeyValue.Value#2], table=KeyValue, column_index_list=[1]) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=61-66 | +-column_ref= | +-ColumnRef(type=STRING, column=KeyValue.Value#2) +-input_scan= @@ -417,6 +418,7 @@ QueryStmt | | | +-TableScan(column_list=[KeyValue.Value#2], table=KeyValue, column_index_list=[1]) | | +-order_by_item_list= | | +-OrderByItem + | | +-parse_location=61-66 | | +-column_ref= | | +-ColumnRef(type=STRING, column=KeyValue.Value#2) | +-input_scan= @@ -803,9 +805,11 @@ QueryStmt | | | | +-ColumnRef(type=STRING, column=$array.e#3) | | | +-order_by_item_list= | | | +-OrderByItem + | | | | +-parse_location=216-221 | | | | +-column_ref= | | | | +-ColumnRef(type=STRING, column=$array.e#3) | | | +-OrderByItem + | | | +-parse_location=223-230 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.idx#4) | | +-limit= @@ -886,9 +890,11 @@ QueryStmt | | | | | +-ColumnRef(type=STRING, column=$array.e#3) | | | | +-order_by_item_list= | | | | +-OrderByItem + | | | | | +-parse_location=228-233 | | | | | +-column_ref= | | | | | +-ColumnRef(type=STRING, column=$array.e#3) | | | | +-OrderByItem + | | | | +-parse_location=235-242 | | | | +-column_ref= | | | | +-ColumnRef(type=INT64, column=$array_offset.idx#4) | | | +-limit= @@ -971,10 +977,12 @@ QueryStmt | | | | +-ColumnRef(type=INT64, column=$array.e#3) | | | +-order_by_item_list= | | | +-OrderByItem + | | | | +-parse_location=216-222 | | | | +-column_ref= | | | | | +-ColumnRef(type=INT64, column=$array.e#3) | | | | +-is_descending=TRUE | | | +-OrderByItem + | | | +-parse_location=224-231 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.idx#4) | | +-limit= @@ -1063,14 +1071,17 @@ QueryStmt | | | | +-ColumnRef(type=DOUBLE, column=$array.e#3) | | | +-order_by_item_list= | | | +-OrderByItem + | | | | +-parse_location=216-230 | | | | +-column_ref= | | | | | +-ColumnRef(type=BOOL, column=$orderby.$orderbycol1#5) | | | | +-is_descending=TRUE | | | +-OrderByItem + | | | | +-parse_location=232-238 | | | | +-column_ref= | | | | | +-ColumnRef(type=DOUBLE, column=$array.e#3) | | | | +-is_descending=TRUE | | | +-OrderByItem + | | | +-parse_location=240-247 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=$array_offset.idx#4) | | +-limit= diff --git a/zetasql/analyzer/testdata/unnest_single_path.test b/zetasql/analyzer/testdata/unnest_single_path.test index e883314e5..721a9b2a4 100644 --- a/zetasql/analyzer/testdata/unnest_single_path.test +++ b/zetasql/analyzer/testdata/unnest_single_path.test @@ -676,9 +676,11 @@ from ArrayTypes.ProtoArray.(zetasql_test__.TestExtraPBExtensionHolder.test_ex... == [enabled_ast_rewrites=DEFAULTS] -[language_features=V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,V_1_3_UNNEST_AND_FLATTEN_ARRAYS,V_1_3_PIVOT] +[language_features=V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,V_1_3_UNNEST_AND_FLATTEN_ARRAYS,V_1_3_PIVOT{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] SELECT * FROM ArrayTypes.ProtoArray.int32_val1 AS Value PIVOT(COUNT(Value) FOR int32_val1 IN (0, 1)); -- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$array.Value#21 AS Value [INT32] @@ -705,6 +707,13 @@ QueryStmt | +-default_value=0 +-element_column_list=[$array.Value#21] + +DEPRECATION WARNING: +PIVOT is not allowed with array scans. This will become an error [at 1:57] +...FROM ArrayTypes.ProtoArray.int32_val1 AS Value PIVOT(COUNT(Value) FOR int3... + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } + [REWRITTEN AST] QueryStmt +-output_column_list= @@ -736,12 +745,20 @@ QueryStmt +-array_expr_list= | +-ColumnRef(type=ARRAY>, column=ArrayTypes.ProtoArray#15) +-element_column_list=[$flatten.injected#22] +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: PIVOT is not allowed with array scans [at 1:57] +...FROM ArrayTypes.ProtoArray.int32_val1 AS Value PIVOT(COUNT(Value) FOR int3... + ^ == [enabled_ast_rewrites=DEFAULTS] -[language_features=V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,V_1_3_UNNEST_AND_FLATTEN_ARRAYS,V_1_3_UNPIVOT] +[language_features=V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,V_1_3_UNNEST_AND_FLATTEN_ARRAYS,V_1_3_PIVOT{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] SELECT * FROM ArrayTypes.ProtoArray.int32_val1 UNPIVOT(a FOR b IN (int32_val1)); -- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$array.int32_val1#21 AS int32_val1 [INT32] @@ -768,6 +785,13 @@ QueryStmt | +-default_value=0 +-element_column_list=[$array.int32_val1#21] + +DEPRECATION WARNING: +UNPIVOT is not allowed with array scans. This will become an error [at 1:48] +SELECT * FROM ArrayTypes.ProtoArray.int32_val1 UNPIVOT(a FOR b IN (int32_val1)); + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } + [REWRITTEN AST] QueryStmt +-output_column_list= @@ -799,15 +823,21 @@ QueryStmt +-array_expr_list= | +-ColumnRef(type=ARRAY>, column=ArrayTypes.ProtoArray#15) +-element_column_list=[$flatten.injected#22] +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: UNPIVOT is not allowed with array scans [at 1:48] +SELECT * FROM ArrayTypes.ProtoArray.int32_val1 UNPIVOT(a FOR b IN (int32_val1)); + ^ == [language_features=V_1_4_SINGLE_TABLE_NAME_ARRAY_PATH,V_1_3_UNNEST_AND_FLATTEN_ARRAYS,TABLESAMPLE] SELECT * FROM ArrayTypes.ProtoArray.int32_val1 AS Value TABLESAMPLE RESERVOIR (100 ROWS); -- -ERROR: TABLESAMPLE cannot be used with arrays [at 1:15] -SELECT * FROM ArrayTypes.ProtoArray.int32_val1 AS Value - ^ +ERROR: TABLESAMPLE is not allowed with array scans [at 2:1] +TABLESAMPLE RESERVOIR (100 ROWS); +^ == # This is to test backward compatibility. diff --git a/zetasql/analyzer/testdata/unpivot.test b/zetasql/analyzer/testdata/unpivot.test index c13362b64..6a085cfa6 100644 --- a/zetasql/analyzer/testdata/unpivot.test +++ b/zetasql/analyzer/testdata/unpivot.test @@ -296,6 +296,127 @@ SELECT * FROM UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); ^ == +[language_features=V_1_3_UNPIVOT,TABLESAMPLE,TABLE_VALUED_FUNCTIONS{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] +# 2 UNNESTs +SELECT * FROM UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$array.k#1 AS k [STRING] +| +-$array.Value#2 AS Value [INT64] ++-query= + +-ProjectScan + +-column_list=$array.[k#1, Value#2] + +-input_scan= + +-ArrayScan + +-column_list=$array.[k#1, Value#2] + +-input_scan= + | +-ArrayScan + | +-column_list=[$array.k#1] + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=["a", "b"]) + | +-element_column_list=[$array.k#1] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.Value#2] + + +DEPRECATION WARNING: +UNPIVOT is not allowed with array scans. This will become an error [at 2:63] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: UNPIVOT is not allowed with array scans [at 2:63] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); + ^ +== + +[language_features=V_1_3_UNPIVOT,TABLESAMPLE,TABLE_VALUED_FUNCTIONS{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] +# Table with UNNEST +SELECT * FROM KeyValue, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$array.Value#3 AS Value [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.Value#3] + +-input_scan= + +-ArrayScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.Value#3] + +-input_scan= + | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.Value#3] + + +DEPRECATION WARNING: +UNPIVOT is not allowed with array scans. This will become an error [at 2:48] +SELECT * FROM KeyValue, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: UNPIVOT is not allowed with array scans [at 2:48] +SELECT * FROM KeyValue, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); + ^ +== + +[language_features=V_1_3_UNPIVOT,TABLESAMPLE,TABLE_VALUED_FUNCTIONS{{|,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS}}] +# Table with 2 UNNESTs +SELECT * FROM KeyValue, UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-KeyValue.Key#1 AS Key [INT64] +| +-KeyValue.Value#2 AS Value [STRING] +| +-$array.k#3 AS k [STRING] +| +-$array.Value#4 AS Value [INT64] ++-query= + +-ProjectScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.k#3, $array.Value#4] + +-input_scan= + +-ArrayScan + +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.k#3, $array.Value#4] + +-input_scan= + | +-ArrayScan + | +-column_list=[KeyValue.Key#1, KeyValue.Value#2, $array.k#3] + | +-input_scan= + | | +-TableScan(column_list=KeyValue.[Key#1, Value#2], table=KeyValue, column_index_list=[0, 1]) + | +-array_expr_list= + | | +-Literal(type=ARRAY, value=["a", "b"]) + | +-element_column_list=[$array.k#3] + +-array_expr_list= + | +-Literal(type=ARRAY, value=[1, 2]) + +-element_column_list=[$array.Value#4] + + +DEPRECATION WARNING: +UNPIVOT is not allowed with array scans. This will become an error [at 2:73] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); + ^ +[zetasql.DeprecationWarning] { kind: PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN } +-- +ALTERNATION GROUP: ,V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS +-- +ERROR: UNPIVOT is not allowed with array scans [at 2:73] +...UNNEST(['a', 'b']) AS k, UNNEST([1,2]) AS Value UNPIVOT(a for b in (Key)); + ^ +== + # Unpivot with column not present in input source SELECT * FROM KeyValue UNPIVOT(a for b in (x)); -- diff --git a/zetasql/analyzer/testdata/value_tables.test b/zetasql/analyzer/testdata/value_tables.test index 49591e3af..6e0c9a8b9 100644 --- a/zetasql/analyzer/testdata/value_tables.test +++ b/zetasql/analyzer/testdata/value_tables.test @@ -825,6 +825,7 @@ QueryStmt | +-a#5 := ColumnRef(type=INT32, column=$pre_groupby.a#4) +-order_by_item_list= +-OrderByItem + +-parse_location=101-104 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.a#5) -- @@ -892,6 +893,7 @@ QueryStmt | +-A#5 := ColumnRef(type=INT32, column=$pre_groupby.a#4) +-order_by_item_list= +-OrderByItem + +-parse_location=101-104 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.A#5) -- @@ -959,6 +961,7 @@ QueryStmt | +-a#5 := ColumnRef(type=INT32, column=$pre_groupby.A#4) +-order_by_item_list= +-OrderByItem + +-parse_location=101-104 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.a#5) -- @@ -1026,6 +1029,7 @@ QueryStmt | +-A#5 := ColumnRef(type=INT32, column=$pre_groupby.A#4) +-order_by_item_list= +-OrderByItem + +-parse_location=101-104 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.A#5) == @@ -1793,9 +1797,11 @@ QueryStmt | +-default_value=77 +-order_by_item_list= +-OrderByItem + | +-parse_location=80-89 | +-column_ref= | +-ColumnRef(type=BYTES, column=$orderby.$orderbycol1#3) +-OrderByItem + +-parse_location=91-101 +-column_ref= +-ColumnRef(type=BOOL, column=$orderby.$orderbycol2#4) == @@ -1824,6 +1830,7 @@ QueryStmt | +-Literal(type=INT32, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=53-59 +-column_ref= | +-ColumnRef(type=INT32, column=Int32ValueTable.value#1) +-is_descending=TRUE @@ -1900,6 +1907,7 @@ QueryStmt | +-output_column_list=[$make_proto.$proto#8] +-order_by_item_list= +-OrderByItem + +-parse_location=146-149 +-column_ref= +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#10) == @@ -1984,6 +1992,7 @@ QueryStmt | | +-value#7 := ColumnRef(type=STRING, column=$aggregate.value#3) | +-order_by_item_list= | +-OrderByItem + | +-parse_location=187-188 | +-column_ref= | +-ColumnRef(type=INT64, column=$distinct.key#6) +-limit= @@ -2061,6 +2070,7 @@ QueryStmt | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) +-order_by_item_list= +-OrderByItem + +-parse_location=74-84 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) == @@ -2073,6 +2083,8 @@ group by {{k.|}}int32_val1 having {{k.|}}int32_val1 > 0 order by {{k.|}}int32_val1 -- +ALTERNATION GROUP: k.,k.,k.,k. +-- QueryStmt +-output_column_list= | +-$groupby.int32_val1#5 AS int32_val1 [INT32] @@ -2107,315 +2119,316 @@ QueryStmt | +-Literal(type=INT32, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=102-114 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) -== - -# Tests for b/19181915 -select cast(int32_val1 as double) from TestExtraValueTable group by int32_val1 -- -QueryStmt -+-output_column_list= -| +-$query.$col1#5 AS `$col1` [DOUBLE] -+-query= - +-ProjectScan - +-column_list=[$query.$col1#5] - +-expr_list= - | +-$col1#5 := - | +-Cast(INT32 -> DOUBLE) - | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) - +-input_scan= - +-AggregateScan - +-column_list=[$groupby.int32_val1#4] - +-input_scan= - | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) - +-group_by_list= - +-int32_val1#4 := - +-GetProtoField - +-type=INT32 - +-expr= - | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - +-field_descriptor=int32_val1 - +-default_value=0 -== - -select sum(int32_val1) -from TestExtraValueTable -group by int32_val2 -having int32_val2 > 1 +ALTERNATION GROUP: k.,k.,k., -- QueryStmt +-output_column_list= -| +-$aggregate.$agg1#4 AS `$col1` [INT64] +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] +-query= - +-ProjectScan - +-column_list=[$aggregate.$agg1#4] + +-OrderByScan + +-column_list=[$groupby.int32_val1#5] + +-is_ordered=TRUE +-input_scan= - +-FilterScan - +-column_list=[$groupby.int32_val2#5, $aggregate.$agg1#4] - +-input_scan= - | +-AggregateScan - | +-column_list=[$groupby.int32_val2#5, $aggregate.$agg1#4] - | +-input_scan= - | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) - | +-group_by_list= - | | +-int32_val2#5 := - | | +-GetProtoField - | | +-type=INT32 - | | +-expr= - | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | +-field_descriptor=int32_val2 - | | +-default_value=0 - | +-aggregate_list= - | +-$agg1#4 := - | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) - | +-Cast(INT32 -> INT64) - | +-GetProtoField - | +-type=INT32 - | +-expr= - | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | +-field_descriptor=int32_val1 - | +-default_value=0 - +-filter_expr= - +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) - +-ColumnRef(type=INT32, column=$groupby.int32_val2#5) - +-Literal(type=INT32, value=1) -== - -select sum(int32_val1) -from TestExtraValueTable -where int32_val1 > 5 -group by int32_val1 -having sum(int32_val1) > 5 and int32_val1 < 10; + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=102-112 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) -- -QueryStmt -+-output_column_list= -| +-$aggregate.$agg1#4 AS `$col1` [INT64] -+-query= - +-ProjectScan - +-column_list=[$aggregate.$agg1#4] - +-input_scan= - +-FilterScan - +-column_list=[$groupby.int32_val1#5, $aggregate.$agg1#4, $aggregate.$agg2#6] - +-input_scan= - | +-AggregateScan - | +-column_list=[$groupby.int32_val1#5, $aggregate.$agg1#4, $aggregate.$agg2#6] - | +-input_scan= - | | +-FilterScan - | | +-column_list=[TestExtraValueTable.value#1] - | | +-input_scan= - | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) - | | +-filter_expr= - | | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) - | | +-GetProtoField - | | | +-type=INT32 - | | | +-expr= - | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | | +-field_descriptor=int32_val1 - | | | +-default_value=0 - | | +-Literal(type=INT32, value=5) - | +-group_by_list= - | | +-int32_val1#5 := - | | +-GetProtoField - | | +-type=INT32 - | | +-expr= - | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | +-field_descriptor=int32_val1 - | | +-default_value=0 - | +-aggregate_list= - | +-$agg1#4 := - | | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) - | | +-Cast(INT32 -> INT64) - | | +-GetProtoField - | | +-type=INT32 - | | +-expr= - | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | +-field_descriptor=int32_val1 - | | +-default_value=0 - | +-$agg2#6 := - | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) - | +-Cast(INT32 -> INT64) - | +-GetProtoField - | +-type=INT32 - | +-expr= - | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | +-field_descriptor=int32_val1 - | +-default_value=0 - +-filter_expr= - +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) - +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) - | +-ColumnRef(type=INT64, column=$aggregate.$agg2#6) - | +-Literal(type=INT64, value=5) - +-FunctionCall(ZetaSQL:$less(INT32, INT32) -> BOOL) - +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) - +-Literal(type=INT32, value=10) -== - -select int32_val1 as foo, count(int32_val2) -from TestExtraValueTable -GROUP BY foo -order by int32_val1 +ALTERNATION GROUPS: + k.,k.,,k. + k.,,k.,k. + k.,k.,k. -- QueryStmt +-output_column_list= -| +-$groupby.foo#6 AS foo [INT32] -| +-$aggregate.$agg1#4 AS `$col2` [INT64] +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] +-query= +-OrderByScan - +-column_list=[$groupby.foo#6, $aggregate.$agg1#4] + +-column_list=[$groupby.int32_val1#5] +-is_ordered=TRUE +-input_scan= - | +-AggregateScan - | +-column_list=[$groupby.foo#6, $aggregate.$agg1#4] + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] | +-input_scan= - | | +-ProjectScan - | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.foo#5] - | | +-expr_list= - | | | +-foo#5 := - | | | +-GetProtoField - | | | +-type=INT32 - | | | +-expr= - | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | | +-field_descriptor=int32_val1 - | | | +-default_value=0 + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] | | +-input_scan= - | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) - | +-group_by_list= - | | +-foo#6 := ColumnRef(type=INT32, column=$pre_groupby.foo#5) - | +-aggregate_list= - | +-$agg1#4 := - | +-AggregateFunctionCall(ZetaSQL:count(INT32) -> INT64) - | +-GetProtoField - | +-type=INT32 - | +-expr= - | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | +-field_descriptor=int32_val2 - | +-default_value=0 + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=100-112 +-column_ref= - +-ColumnRef(type=INT32, column=$groupby.foo#6) -== - -select max(int32_val2) -from TestExtraValueTable -WHERE int32_val1 > 1000000 - and int32_val1 < 1500000 -group by int32_val1 -order by int32_val1 + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) +-- +ALTERNATION GROUPS: + k.,k.,, + k.,,k., + k.,k., -- QueryStmt +-output_column_list= -| +-$aggregate.$agg1#4 AS `$col1` [INT32] +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] +-query= +-OrderByScan - +-column_list=[$aggregate.$agg1#4] + +-column_list=[$groupby.int32_val1#5] +-is_ordered=TRUE +-input_scan= - | +-AggregateScan - | +-column_list=[$groupby.int32_val1#5, $aggregate.$agg1#4] + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] | +-input_scan= - | | +-FilterScan - | | +-column_list=[TestExtraValueTable.value#1] + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] | | +-input_scan= - | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) - | | +-filter_expr= - | | +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) - | | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) - | | | +-GetProtoField - | | | | +-type=INT32 - | | | | +-expr= - | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | | | +-field_descriptor=int32_val1 - | | | | +-default_value=0 - | | | +-Literal(type=INT32, value=1000000) - | | +-FunctionCall(ZetaSQL:$less(INT32, INT32) -> BOOL) - | | +-GetProtoField - | | | +-type=INT32 - | | | +-expr= - | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | | +-field_descriptor=int32_val1 - | | | +-default_value=0 - | | +-Literal(type=INT32, value=1500000) - | +-group_by_list= - | | +-int32_val1#5 := - | | +-GetProtoField - | | +-type=INT32 - | | +-expr= - | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | | +-field_descriptor=int32_val1 - | | +-default_value=0 - | +-aggregate_list= - | +-$agg1#4 := - | +-AggregateFunctionCall(ZetaSQL:max(INT32) -> INT32) - | +-GetProtoField - | +-type=INT32 - | +-expr= - | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | +-field_descriptor=int32_val2 - | +-default_value=0 + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) +-order_by_item_list= +-OrderByItem + +-parse_location=100-110 +-column_ref= +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) -== - -select int32_val2, int32_val1 -from TestExtraValueTable -group by 1, 2 +-- +ALTERNATION GROUPS: + k.,,,k. + k.,,k. + k.,k. -- QueryStmt +-output_column_list= -| +-$groupby.int32_val2#4 AS int32_val2 [INT32] | +-$groupby.int32_val1#5 AS int32_val1 [INT32] +-query= - +-ProjectScan - +-column_list=$groupby.[int32_val2#4, int32_val1#5] + +-OrderByScan + +-column_list=[$groupby.int32_val1#5] + +-is_ordered=TRUE +-input_scan= - +-AggregateScan - +-column_list=$groupby.[int32_val2#4, int32_val1#5] - +-input_scan= - | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) - +-group_by_list= - +-int32_val2#4 := - | +-GetProtoField - | +-type=INT32 - | +-expr= - | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - | +-field_descriptor=int32_val2 - | +-default_value=0 - +-int32_val1#5 := - +-GetProtoField - +-type=INT32 - +-expr= - | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) - +-field_descriptor=int32_val1 - +-default_value=0 + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=98-110 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) +-- +ALTERNATION GROUPS: + k.,,, + k.,, + k., +-- +QueryStmt ++-output_column_list= +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.int32_val1#5] + +-is_ordered=TRUE + +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=98-108 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) +-- +ALTERNATION GROUP: k. +-- +QueryStmt ++-output_column_list= +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.int32_val1#5] + +-is_ordered=TRUE + +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=96-108 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) +-- +ALTERNATION GROUP: +-- +QueryStmt ++-output_column_list= +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] ++-query= + +-OrderByScan + +-column_list=[$groupby.int32_val1#5] + +-is_ordered=TRUE + +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.int32_val1#5] + | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.int32_val1#5] + | | +-input_scan= + | | | +-ProjectScan + | | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.int32_val1#4] + | | | +-expr_list= + | | | | +-int32_val1#4 := + | | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0], alias="k") + | | +-group_by_list= + | | +-int32_val1#5 := ColumnRef(type=INT32, column=$pre_groupby.int32_val1#4) + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + | +-Literal(type=INT32, value=0) + +-order_by_item_list= + +-OrderByItem + +-parse_location=96-106 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) == -select int32_val1+1, int32_val1+int32_val1 -from TestExtraValueTable -group by int32_val1 +# Tests for b/19181915 +select cast(int32_val1 as double) from TestExtraValueTable group by int32_val1 -- QueryStmt +-output_column_list= -| +-$query.$col1#5 AS `$col1` [INT64] -| +-$query.$col2#6 AS `$col2` [INT64] +| +-$query.$col1#5 AS `$col1` [DOUBLE] +-query= +-ProjectScan - +-column_list=$query.[$col1#5, $col2#6] + +-column_list=[$query.$col1#5] +-expr_list= | +-$col1#5 := - | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) - | | +-Cast(INT32 -> INT64) - | | | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) - | | +-Literal(type=INT64, value=1) - | +-$col2#6 := - | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) - | +-Cast(INT32 -> INT64) - | | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) - | +-Cast(INT32 -> INT64) - | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) + | +-Cast(INT32 -> DOUBLE) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) +-input_scan= +-AggregateScan +-column_list=[$groupby.int32_val1#4] @@ -2431,37 +2444,254 @@ QueryStmt +-default_value=0 == -select int32_val1+1, int32_val1, int32_val1 k, int32_val1+2 as k2 +select sum(int32_val1) from TestExtraValueTable -group by k; +group by int32_val2 +having int32_val2 > 1 -- QueryStmt +-output_column_list= -| +-$query.$col1#5 AS `$col1` [INT64] -| +-$groupby.k#4 AS int32_val1 [INT32] -| +-$groupby.k#4 AS k [INT32] -| +-$query.k2#6 AS k2 [INT64] +| +-$aggregate.$agg1#4 AS `$col1` [INT64] +-query= +-ProjectScan - +-column_list=[$query.$col1#5, $groupby.k#4, $groupby.k#4, $query.k2#6] - +-expr_list= - | +-$col1#5 := - | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) - | | +-Cast(INT32 -> INT64) - | | | +-ColumnRef(type=INT32, column=$groupby.k#4) - | | +-Literal(type=INT64, value=1) - | +-k2#6 := - | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) - | +-Cast(INT32 -> INT64) - | | +-ColumnRef(type=INT32, column=$groupby.k#4) - | +-Literal(type=INT64, value=2) + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + +-FilterScan + +-column_list=[$groupby.int32_val2#5, $aggregate.$agg1#4] + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int32_val2#5, $aggregate.$agg1#4] + | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | +-group_by_list= + | | +-int32_val2#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val2 + | | +-default_value=0 + | +-aggregate_list= + | +-$agg1#4 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-Cast(INT32 -> INT64) + | +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=int32_val1 + | +-default_value=0 + +-filter_expr= + +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + +-ColumnRef(type=INT32, column=$groupby.int32_val2#5) + +-Literal(type=INT32, value=1) +== + +select sum(int32_val1) +from TestExtraValueTable +where int32_val1 > 5 +group by int32_val1 +having sum(int32_val1) > 5 and int32_val1 < 10; +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [INT64] ++-query= + +-ProjectScan + +-column_list=[$aggregate.$agg1#4] + +-input_scan= + +-FilterScan + +-column_list=[$groupby.int32_val1#5, $aggregate.$agg1#4, $aggregate.$agg2#6] + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int32_val1#5, $aggregate.$agg1#4, $aggregate.$agg2#6] + | +-input_scan= + | | +-FilterScan + | | +-column_list=[TestExtraValueTable.value#1] + | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val1 + | | | +-default_value=0 + | | +-Literal(type=INT32, value=5) + | +-group_by_list= + | | +-int32_val1#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-aggregate_list= + | +-$agg1#4 := + | | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | | +-Cast(INT32 -> INT64) + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-$agg2#6 := + | +-AggregateFunctionCall(ZetaSQL:sum(INT64) -> INT64) + | +-Cast(INT32 -> INT64) + | +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=int32_val1 + | +-default_value=0 + +-filter_expr= + +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + +-FunctionCall(ZetaSQL:$greater(INT64, INT64) -> BOOL) + | +-ColumnRef(type=INT64, column=$aggregate.$agg2#6) + | +-Literal(type=INT64, value=5) + +-FunctionCall(ZetaSQL:$less(INT32, INT32) -> BOOL) + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) + +-Literal(type=INT32, value=10) +== + +select int32_val1 as foo, count(int32_val2) +from TestExtraValueTable +GROUP BY foo +order by int32_val1 +-- +QueryStmt ++-output_column_list= +| +-$groupby.foo#6 AS foo [INT32] +| +-$aggregate.$agg1#4 AS `$col2` [INT64] ++-query= + +-OrderByScan + +-column_list=[$groupby.foo#6, $aggregate.$agg1#4] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.foo#6, $aggregate.$agg1#4] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[TestExtraValueTable.value#1, $pre_groupby.foo#5] + | | +-expr_list= + | | | +-foo#5 := + | | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val1 + | | | +-default_value=0 + | | +-input_scan= + | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | +-group_by_list= + | | +-foo#6 := ColumnRef(type=INT32, column=$pre_groupby.foo#5) + | +-aggregate_list= + | +-$agg1#4 := + | +-AggregateFunctionCall(ZetaSQL:count(INT32) -> INT64) + | +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=int32_val2 + | +-default_value=0 + +-order_by_item_list= + +-OrderByItem + +-parse_location=91-101 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.foo#6) +== + +select max(int32_val2) +from TestExtraValueTable +WHERE int32_val1 > 1000000 + and int32_val1 < 1500000 +group by int32_val1 +order by int32_val1 +-- +QueryStmt ++-output_column_list= +| +-$aggregate.$agg1#4 AS `$col1` [INT32] ++-query= + +-OrderByScan + +-column_list=[$aggregate.$agg1#4] + +-is_ordered=TRUE + +-input_scan= + | +-AggregateScan + | +-column_list=[$groupby.int32_val1#5, $aggregate.$agg1#4] + | +-input_scan= + | | +-FilterScan + | | +-column_list=[TestExtraValueTable.value#1] + | | +-input_scan= + | | | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + | | +-filter_expr= + | | +-FunctionCall(ZetaSQL:$and(BOOL, repeated(1) BOOL) -> BOOL) + | | +-FunctionCall(ZetaSQL:$greater(INT32, INT32) -> BOOL) + | | | +-GetProtoField + | | | | +-type=INT32 + | | | | +-expr= + | | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | | +-field_descriptor=int32_val1 + | | | | +-default_value=0 + | | | +-Literal(type=INT32, value=1000000) + | | +-FunctionCall(ZetaSQL:$less(INT32, INT32) -> BOOL) + | | +-GetProtoField + | | | +-type=INT32 + | | | +-expr= + | | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | | +-field_descriptor=int32_val1 + | | | +-default_value=0 + | | +-Literal(type=INT32, value=1500000) + | +-group_by_list= + | | +-int32_val1#5 := + | | +-GetProtoField + | | +-type=INT32 + | | +-expr= + | | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | | +-field_descriptor=int32_val1 + | | +-default_value=0 + | +-aggregate_list= + | +-$agg1#4 := + | +-AggregateFunctionCall(ZetaSQL:max(INT32) -> INT32) + | +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=int32_val2 + | +-default_value=0 + +-order_by_item_list= + +-OrderByItem + +-parse_location=131-141 + +-column_ref= + +-ColumnRef(type=INT32, column=$groupby.int32_val1#5) +== + +select int32_val2, int32_val1 +from TestExtraValueTable +group by 1, 2 +-- +QueryStmt ++-output_column_list= +| +-$groupby.int32_val2#4 AS int32_val2 [INT32] +| +-$groupby.int32_val1#5 AS int32_val1 [INT32] ++-query= + +-ProjectScan + +-column_list=$groupby.[int32_val2#4, int32_val1#5] +-input_scan= +-AggregateScan - +-column_list=[$groupby.k#4] + +-column_list=$groupby.[int32_val2#4, int32_val1#5] +-input_scan= | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) +-group_by_list= - +-k#4 := + +-int32_val2#4 := + | +-GetProtoField + | +-type=INT32 + | +-expr= + | | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + | +-field_descriptor=int32_val2 + | +-default_value=0 + +-int32_val1#5 := +-GetProtoField +-type=INT32 +-expr= @@ -2470,30 +2700,107 @@ QueryStmt +-default_value=0 == -select int32_val1+1, int32_val1, int32_val1 k, int32_val1+2 as k2 +select int32_val1+1, int32_val1+int32_val1 from TestExtraValueTable -group by 2; +group by int32_val1 -- QueryStmt +-output_column_list= | +-$query.$col1#5 AS `$col1` [INT64] -| +-$groupby.int32_val1#4 AS int32_val1 [INT32] -| +-$groupby.int32_val1#4 AS k [INT32] -| +-$query.k2#6 AS k2 [INT64] +| +-$query.$col2#6 AS `$col2` [INT64] +-query= +-ProjectScan - +-column_list=[$query.$col1#5, $groupby.int32_val1#4, $groupby.int32_val1#4, $query.k2#6] + +-column_list=$query.[$col1#5, $col2#6] +-expr_list= | +-$col1#5 := | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) | | +-Cast(INT32 -> INT64) | | | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) | | +-Literal(type=INT64, value=1) - | +-k2#6 := + | +-$col2#6 := | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) | +-Cast(INT32 -> INT64) | | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) - | +-Literal(type=INT64, value=2) + | +-Cast(INT32 -> INT64) + | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.int32_val1#4] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-group_by_list= + +-int32_val1#4 := + +-GetProtoField + +-type=INT32 + +-expr= + | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + +-field_descriptor=int32_val1 + +-default_value=0 +== + +select int32_val1+1, int32_val1, int32_val1 k, int32_val1+2 as k2 +from TestExtraValueTable +group by k; +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#5 AS `$col1` [INT64] +| +-$groupby.k#4 AS int32_val1 [INT32] +| +-$groupby.k#4 AS k [INT32] +| +-$query.k2#6 AS k2 [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#5, $groupby.k#4, $groupby.k#4, $query.k2#6] + +-expr_list= + | +-$col1#5 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Cast(INT32 -> INT64) + | | | +-ColumnRef(type=INT32, column=$groupby.k#4) + | | +-Literal(type=INT64, value=1) + | +-k2#6 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Cast(INT32 -> INT64) + | | +-ColumnRef(type=INT32, column=$groupby.k#4) + | +-Literal(type=INT64, value=2) + +-input_scan= + +-AggregateScan + +-column_list=[$groupby.k#4] + +-input_scan= + | +-TableScan(column_list=[TestExtraValueTable.value#1], table=TestExtraValueTable, column_index_list=[0]) + +-group_by_list= + +-k#4 := + +-GetProtoField + +-type=INT32 + +-expr= + | +-ColumnRef(type=PROTO, column=TestExtraValueTable.value#1) + +-field_descriptor=int32_val1 + +-default_value=0 +== + +select int32_val1+1, int32_val1, int32_val1 k, int32_val1+2 as k2 +from TestExtraValueTable +group by 2; +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#5 AS `$col1` [INT64] +| +-$groupby.int32_val1#4 AS int32_val1 [INT32] +| +-$groupby.int32_val1#4 AS k [INT32] +| +-$query.k2#6 AS k2 [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.$col1#5, $groupby.int32_val1#4, $groupby.int32_val1#4, $query.k2#6] + +-expr_list= + | +-$col1#5 := + | | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | | +-Cast(INT32 -> INT64) + | | | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) + | | +-Literal(type=INT64, value=1) + | +-k2#6 := + | +-FunctionCall(ZetaSQL:$add(INT64, INT64) -> INT64) + | +-Cast(INT32 -> INT64) + | | +-ColumnRef(type=INT32, column=$groupby.int32_val1#4) + | +-Literal(type=INT64, value=2) +-input_scan= +-AggregateScan +-column_list=[$groupby.int32_val1#4] @@ -2668,12 +2975,15 @@ QueryStmt | +-Literal(type=INT64, value=4) +-order_by_item_list= +-OrderByItem + | +-parse_location=216-227 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.int64_key_1#4) +-OrderByItem + | +-parse_location=229-254 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.nested_int64#5) +-OrderByItem + +-parse_location=256-270 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg2#7) == @@ -2736,6 +3046,7 @@ QueryStmt | +-Literal(type=INT64, value=2) +-order_by_item_list= +-OrderByItem + +-parse_location=145-174 +-column_ref= +-ColumnRef(type=INT64, column=$orderby.$orderbycol1#4) == @@ -2873,21 +3184,27 @@ QueryStmt | +-Literal(type=INT64, value=5) +-order_by_item_list= +-OrderByItem + | +-parse_location=417-428 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.int64_key_1#6) +-OrderByItem + | +-parse_location=430-443 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.int64_key_2#7) +-OrderByItem + | +-parse_location=445-470 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.nested_int64#8) +-OrderByItem + | +-parse_location=481-508 | +-column_ref= | +-ColumnRef(type=INT64, column=$groupby.nested_int64#8) +-OrderByItem + | +-parse_location=510-524 | +-column_ref= | +-ColumnRef(type=INT64, column=$aggregate.$agg3#11) +-OrderByItem + +-parse_location=526-545 +-column_ref= +-ColumnRef(type=INT64, column=$aggregate.$agg4#12) == @@ -3075,6 +3392,426 @@ group by {{T.|}}f.d.b having {{T.|}}f.d.b = 'foo' order by concat({{T.|}}f.d.b, "b") -- +ALTERNATION GROUP: T.,T.,T.,T. +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=111-131 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUP: T.,T.,T., +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=111-129 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUPS: + T.,T.,,T. + T.,,T.,T. + T.,T.,T. +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=109-129 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUPS: + T.,T.,, + T.,,T., + T.,T., +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=109-127 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUPS: + T.,,,T. + T.,,T. + T.,T. +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=107-127 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUPS: + T.,,, + T.,, + T., +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=107-125 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUP: T. +-- +QueryStmt ++-output_column_list= +| +-$query.$col1#3 AS `$col1` [STRING] ++-query= + +-OrderByScan + +-column_list=[$query.$col1#3] + +-is_ordered=TRUE + +-input_scan= + | +-ProjectScan + | +-column_list=[$groupby.b#2, $query.$col1#3, $orderby.$orderbycol1#4] + | +-expr_list= + | | +-$orderbycol1#4 := + | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | +-Literal(type=STRING, value="b") + | +-input_scan= + | +-FilterScan + | +-column_list=[$groupby.b#2, $query.$col1#3] + | +-input_scan= + | | +-ProjectScan + | | +-column_list=[$groupby.b#2, $query.$col1#3] + | | +-expr_list= + | | | +-$col1#3 := + | | | +-FunctionCall(ZetaSQL:concat(STRING, repeated(1) STRING) -> STRING) + | | | +-ColumnRef(type=STRING, column=$groupby.b#2) + | | | +-Literal(type=STRING, value="a") + | | +-input_scan= + | | +-AggregateScan + | | +-column_list=[$groupby.b#2] + | | +-input_scan= + | | | +-TableScan(column_list=[TestNestedStructValueTable.value#1], table=TestNestedStructValueTable, column_index_list=[0], alias="T") + | | +-group_by_list= + | | +-b#2 := + | | +-GetStructField + | | +-type=STRING + | | +-expr= + | | | +-GetStructField + | | | +-type=STRUCT + | | | +-expr= + | | | | +-GetStructField + | | | | +-type=STRUCT> + | | | | +-expr= + | | | | | +-ColumnRef(type=STRUCT>>, column=TestNestedStructValueTable.value#1) + | | | | +-field_idx=1 + | | | +-field_idx=1 + | | +-field_idx=1 + | +-filter_expr= + | +-FunctionCall(ZetaSQL:$equal(STRING, STRING) -> BOOL) + | +-ColumnRef(type=STRING, column=$groupby.b#2) + | +-Literal(type=STRING, value="foo") + +-order_by_item_list= + +-OrderByItem + +-parse_location=105-125 + +-column_ref= + +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) +-- +ALTERNATION GROUP: +-- QueryStmt +-output_column_list= | +-$query.$col1#3 AS `$col1` [STRING] @@ -3127,6 +3864,7 @@ QueryStmt | +-Literal(type=STRING, value="foo") +-order_by_item_list= +-OrderByItem + +-parse_location=105-123 +-column_ref= +-ColumnRef(type=STRING, column=$orderby.$orderbycol1#4) == diff --git a/zetasql/analyzer/testdata/with_recursive.test b/zetasql/analyzer/testdata/with_recursive.test index 08b295068..560eb0fa7 100644 --- a/zetasql/analyzer/testdata/with_recursive.test +++ b/zetasql/analyzer/testdata/with_recursive.test @@ -3387,6 +3387,7 @@ QueryStmt | | +-unit=ROWS | +-order_by_item_list= | +-OrderByItem + | +-parse_location=167-168 | +-column_ref= | +-ColumnRef(type=INT64, column=t.n#6) +-recursive=TRUE @@ -3470,6 +3471,7 @@ QueryStmt | | | | +-unit=ROWS | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=236-237 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=tbl.n#5) | | +-join_expr= @@ -3562,6 +3564,7 @@ QueryStmt | | | | +-unit=ROWS | | | +-order_by_item_list= | | | +-OrderByItem + | | | +-parse_location=222-223 | | | +-column_ref= | | | +-ColumnRef(type=INT64, column=tbl.n#4) | | +-right_scan= diff --git a/zetasql/common/BUILD b/zetasql/common/BUILD index cdafb15e7..6a72a50cc 100644 --- a/zetasql/common/BUILD +++ b/zetasql/common/BUILD @@ -273,6 +273,7 @@ cc_library( deps = [ ":string_util", "//zetasql/base", + "//zetasql/base:mathlimits", "//zetasql/base:mathutil", "@com_google_absl//absl/strings", ], @@ -296,6 +297,7 @@ cc_library( "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/numeric:bits", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:compare", "@com_google_absl//absl/types:span", ], ) @@ -425,6 +427,7 @@ cc_library( "//zetasql/public:builtin_function_cc_proto", "//zetasql/public:builtin_function_options", "//zetasql/public:catalog", + "//zetasql/public:coercer", "//zetasql/public:cycle_detector", "//zetasql/public:function", "//zetasql/public:function_cc_proto", @@ -440,6 +443,7 @@ cc_library( "//zetasql/public/functions:datetime_cc_proto", "//zetasql/public/functions:differential_privacy_cc_proto", "//zetasql/public/functions:string_format", + "//zetasql/public/functions:unsupported_fields_cc_proto", "//zetasql/public/types", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", @@ -678,6 +682,16 @@ cc_library( deps = ["//zetasql/public:analyzer_options"], ) +cc_library( + name = "internal_analyzer_output_properties", + hdrs = ["internal_analyzer_output_properties.h"], + deps = [ + "//zetasql/public:analyzer_output_properties", + "//zetasql/resolved_ast", + "//zetasql/resolved_ast:target_syntax", + ], +) + cc_test( name = "function_utils_test", srcs = ["function_utils_test.cc"], @@ -743,8 +757,3 @@ cc_test( "//zetasql/base/testing:zetasql_gtest_main", ], ) - -cc_library( - name = "resolution_scope", - hdrs = ["resolution_scope.h"], -) diff --git a/zetasql/common/builtin_enum_type.cc b/zetasql/common/builtin_enum_type.cc index 3b8c3bbc5..daa5b862f 100644 --- a/zetasql/common/builtin_enum_type.cc +++ b/zetasql/common/builtin_enum_type.cc @@ -23,6 +23,14 @@ namespace zetasql { +absl::Status GetToJsonBuiltinEnumTypes( + TypeFactory* type_factory, const ZetaSQLBuiltinFunctionOptions& options, + NameToTypeMap* types) { + ZETASQL_RETURN_IF_ERROR( + InsertType(types, options, types::UnsupportedFieldsEnumType())); + return absl::OkStatus(); +} + absl::Status GetStandaloneBuiltinEnumTypes( TypeFactory* type_factory, const ZetaSQLBuiltinFunctionOptions& options, NameToTypeMap* types) { diff --git a/zetasql/common/builtin_function_distance.cc b/zetasql/common/builtin_function_distance.cc index a78436f74..2b76391ca 100644 --- a/zetasql/common/builtin_function_distance.cc +++ b/zetasql/common/builtin_function_distance.cc @@ -36,6 +36,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" +#include "absl/types/span.h" #include "zetasql/base/status_macros.h" namespace zetasql { @@ -59,7 +60,7 @@ static FunctionSignatureOptions SetDefinitionForInlining(absl::string_view sql, static std::string CheckApproximateDistanceFnProtoArguments( absl::string_view function_name, const FunctionSignature& matched_signature, - const std::vector& arguments) { + absl::Span arguments) { ABSL_CHECK_EQ(arguments.size(), matched_signature.arguments().size()); // Crash OK if (arguments.size() > 2 && matched_signature.argument(kOptionsArgIdx).type()->IsProto()) { @@ -73,7 +74,7 @@ static std::string CheckApproximateDistanceFnProtoArguments( static std::string CheckApproximateDistanceFnJsonArguments( absl::string_view function_name, const FunctionSignature& matched_signature, - const std::vector& arguments) { + absl::Span arguments) { ABSL_CHECK_EQ(arguments.size(), matched_signature.arguments().size()); // Crash OK if (arguments.size() > 2 && matched_signature.argument(kOptionsArgIdx).type()->IsJson()) { diff --git a/zetasql/common/builtin_function_internal.h b/zetasql/common/builtin_function_internal.h index 95edabf86..7c353264f 100644 --- a/zetasql/common/builtin_function_internal.h +++ b/zetasql/common/builtin_function_internal.h @@ -170,25 +170,14 @@ std::string SignatureTextForAnonCountStarFunction( const LanguageOptions& language_options, const Function& function, const FunctionSignature& signature); -std::string SupportedSignaturesForAnonCountStarFunction( - const LanguageOptions& language_options, const Function& function); - std::string SignatureTextForAnonCountStarWithReportFunction( const std::string& report_format, const LanguageOptions& language_options, const Function& function, const FunctionSignature& signature); -std::string SupportedSignaturesForAnonCountStarWithReportFunction( - const std::string& report_format, const LanguageOptions& language_options, - const Function& function); - std::string SignatureTextForAnonQuantilesWithReportFunction( const std::string& report_format, const LanguageOptions& language_options, const Function& function, const FunctionSignature& signature); -std::string SupportedSignaturesForAnonQuantilesWithReportFunction( - const std::string& report_format, const LanguageOptions& language_options, - const Function& function); - std::string AnonSumWithReportJsonFunctionSQL( const std::vector& inputs); @@ -341,9 +330,6 @@ absl::Status CheckGenerateTimestampArrayArguments( const std::vector& arguments, const LanguageOptions& language_options); -absl::Status CheckJsonArguments(const std::vector& arguments, - const LanguageOptions& options); - absl::Status CheckFormatPostResolutionArguments( const FunctionSignature& /*signature*/, const std::vector& arguments, @@ -739,6 +725,10 @@ absl::Status GetArrayZipFunctions( TypeFactory* type_factory, const ZetaSQLBuiltinFunctionOptions& options, NameToFunctionMap* functions, NameToTypeMap* types); +absl::Status GetToJsonBuiltinEnumTypes( + TypeFactory* type_factory, const ZetaSQLBuiltinFunctionOptions& options, + NameToTypeMap* types); + absl::Status GetStandaloneBuiltinEnumTypes( TypeFactory* type_factory, const ZetaSQLBuiltinFunctionOptions& options, NameToTypeMap* types); diff --git a/zetasql/common/builtin_function_internal_1.cc b/zetasql/common/builtin_function_internal_1.cc index a329be264..c33c5f5d2 100644 --- a/zetasql/common/builtin_function_internal_1.cc +++ b/zetasql/common/builtin_function_internal_1.cc @@ -171,11 +171,6 @@ std::string SignatureTextForAnonCountStarFunction( return SignatureTextForAnonCountStarFunction(); } -std::string SupportedSignaturesForAnonCountStarFunction( - const LanguageOptions& language_options, const Function& function) { - return SignatureTextForAnonCountStarFunction(); -} - std::string SignatureTextForAnonCountStarWithReportFunction( const std::string& report_format) { return absl::StrCat( @@ -190,12 +185,6 @@ std::string SignatureTextForAnonCountStarWithReportFunction( return SignatureTextForAnonCountStarWithReportFunction(report_format); } -std::string SupportedSignaturesForAnonCountStarWithReportFunction( - const std::string& report_format, const LanguageOptions& language_options, - const Function& function) { - return SignatureTextForAnonCountStarWithReportFunction(report_format); -} - std::string SignatureTextForAnonQuantilesWithReportFunction( const std::string& report_format) { return absl::StrCat( @@ -204,12 +193,6 @@ std::string SignatureTextForAnonQuantilesWithReportFunction( report_format, "))"); } -std::string SupportedSignaturesForAnonQuantilesWithReportFunction( - const std::string& report_format, const LanguageOptions& language_options, - const Function& function) { - return SignatureTextForAnonQuantilesWithReportFunction(report_format); -} - std::string SignatureTextForAnonQuantilesWithReportFunction( const std::string& report_format, const LanguageOptions& language_options, const Function& function, const FunctionSignature& signature) { @@ -344,7 +327,7 @@ std::string LikeAnyFunctionSQL(const std::vector& inputs) { std::string NotLikeAnyFunctionSQL(const std::vector& inputs) { ABSL_DCHECK_GT(inputs.size(), 1); std::vector like_list(inputs.begin() + 1, inputs.end()); - return absl::StrCat(inputs[0], "NOT LIKE ALL (", + return absl::StrCat(inputs[0], " NOT LIKE ANY (", absl::StrJoin(like_list, ", "), ")"); } std::string LikeAllFunctionSQL(const std::vector& inputs) { @@ -356,7 +339,7 @@ std::string LikeAllFunctionSQL(const std::vector& inputs) { std::string NotLikeAllFunctionSQL(const std::vector& inputs) { ABSL_DCHECK_GT(inputs.size(), 1); std::vector like_list(inputs.begin() + 1, inputs.end()); - return absl::StrCat(inputs[0], "NOT LIKE ALL (", + return absl::StrCat(inputs[0], " NOT LIKE ALL (", absl::StrJoin(like_list, ", "), ")"); } std::string CaseWithValueFunctionSQL(const std::vector& inputs) { diff --git a/zetasql/common/builtin_function_internal_2.cc b/zetasql/common/builtin_function_internal_2.cc index 842a15b9d..1dce1c5fc 100644 --- a/zetasql/common/builtin_function_internal_2.cc +++ b/zetasql/common/builtin_function_internal_2.cc @@ -1976,7 +1976,7 @@ absl::Status GetBooleanFunctions(TypeFactory* type_factory, FN_EQUAL, FunctionSignatureOptions().set_uses_operation_collation()}, {bool_type, {int64_type, uint64_type}, FN_EQUAL_INT64_UINT64}, - {bool_type, {uint64_type, int64_type}, FN_EQUAL_UINT64_INT64} + {bool_type, {uint64_type, int64_type}, FN_EQUAL_UINT64_INT64}, }, FunctionOptions() .set_supports_safe_error_mode(false) @@ -1995,7 +1995,7 @@ absl::Status GetBooleanFunctions(TypeFactory* type_factory, FN_NOT_EQUAL, FunctionSignatureOptions().set_uses_operation_collation()}, {bool_type, {int64_type, uint64_type}, FN_NOT_EQUAL_INT64_UINT64}, - {bool_type, {uint64_type, int64_type}, FN_NOT_EQUAL_UINT64_INT64} + {bool_type, {uint64_type, int64_type}, FN_NOT_EQUAL_UINT64_INT64}, }, FunctionOptions() .set_supports_safe_error_mode(false) diff --git a/zetasql/common/builtin_function_internal_3.cc b/zetasql/common/builtin_function_internal_3.cc index 1e68a9e66..c2849ba2b 100644 --- a/zetasql/common/builtin_function_internal_3.cc +++ b/zetasql/common/builtin_function_internal_3.cc @@ -18,7 +18,9 @@ #include #include #include +#include #include +#include #include #include "google/protobuf/timestamp.pb.h" @@ -33,6 +35,7 @@ #include "zetasql/public/function.h" #include "zetasql/public/function.pb.h" #include "zetasql/public/function_signature.h" +#include "zetasql/public/functions/unsupported_fields.pb.h" #include "zetasql/public/input_argument_type.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" @@ -43,9 +46,12 @@ #include "zetasql/public/types/type.h" #include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" +#include "absl/algorithm/container.h" +#include "absl/container/flat_hash_set.h" #include "absl/functional/bind_front.h" #include "zetasql/base/check.h" #include "absl/status/status.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" @@ -279,6 +285,18 @@ void GetStringFunctions(TypeFactory* type_factory, FunctionSignatureOptions().set_uses_operation_collation()}, {bytes_array_type, {bytes_type, bytes_type}, FN_SPLIT_BYTES}}); + InsertFunction( + functions, options, "split_substr", SCALAR, + { + {string_type, + {string_type, + string_type, + int64_type, + {int64_type, FunctionArgumentType::OPTIONAL}}, + FN_SPLIT_SUBSTR, + FunctionSignatureOptions().set_uses_operation_collation()}, + }); + InsertSimpleFunction( functions, options, "safe_convert_bytes_to_string", SCALAR, {{string_type, {bytes_type}, FN_SAFE_CONVERT_BYTES_TO_STRING}}); @@ -946,17 +964,20 @@ void GetMiscellaneousFunctions(TypeFactory* type_factory, InsertFunction( functions, options, "$concat_op", SCALAR, - {{string_type, - {{string_type, concat_option}, {string_type, concat_option}}, - FN_CONCAT_OP_STRING}, - {bytes_type, {bytes_type, bytes_type}, FN_CONCAT_OP_BYTES}, - {ARG_ARRAY_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, ARG_ARRAY_TYPE_ANY_1}, - FN_ARRAY_CONCAT_OP}}, + { + {string_type, + {{string_type, concat_option}, {string_type, concat_option}}, + FN_CONCAT_OP_STRING}, + {bytes_type, {bytes_type, bytes_type}, FN_CONCAT_OP_BYTES}, + {ARG_ARRAY_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, ARG_ARRAY_TYPE_ANY_1}, + FN_ARRAY_CONCAT_OP} + }, FunctionOptions() .set_supports_safe_error_mode(false) .set_sql_name("||") - .set_get_sql_callback(absl::bind_front(&InfixFunctionSQL, "||"))); + .set_get_sql_callback(absl::bind_front(&InfixFunctionSQL, "||")) + ); // RANGE_BUCKET: returns the bucket of the item in the array. InsertFunction( @@ -1238,6 +1259,7 @@ void GetJSONFunctions(TypeFactory* type_factory, const ArrayType* array_bool_type = types::BoolArrayType(); const ArrayType* array_string_type = types::StringArrayType(); const ArrayType* array_json_type = types::JsonArrayType(); + const EnumType* unsupported_fields_type = types::UnsupportedFieldsEnumType(); const Function::Mode SCALAR = Function::SCALAR; const FunctionArgumentType::ArgumentCardinality REPEATED = @@ -1337,18 +1359,26 @@ void GetJSONFunctions(TypeFactory* type_factory, {json_type, optional_json_path_argument}, FN_JSON_VALUE_ARRAY_JSON}); - InsertFunction( - functions, options, "to_json", SCALAR, - { - {json_type, - {ARG_TYPE_ANY_1, - {bool_type, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionEnums::OPTIONAL) - .set_argument_name("stringify_wide_numbers", kNamedOnly) - .set_default(values::Bool(false))}}, - FN_TO_JSON} - }); + FunctionArgumentTypeList to_json_args( + {ARG_TYPE_ANY_1, + {bool_type, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionEnums::OPTIONAL) + .set_argument_name("stringify_wide_numbers", kNamedOnly) + .set_default(values::Bool(false))}}); + if (options.language_options.LanguageFeatureEnabled( + FEATURE_TO_JSON_UNSUPPORTED_FIELDS)) { + to_json_args.push_back( + {unsupported_fields_type, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionEnums::OPTIONAL) + .set_argument_name("unsupported_fields", kNamedOnly) + .set_default(values::Enum(unsupported_fields_type, + functions::UnsupportedFields::FAIL))}); + } + InsertFunction(functions, options, "to_json", SCALAR, + {{json_type, to_json_args, FN_TO_JSON}}); + InsertFunction( functions, options, "parse_json", SCALAR, {{json_type, @@ -2987,7 +3017,7 @@ void GetTypeOfFunction(TypeFactory* type_factory, {ARG_TYPE_ARBITRARY}, FN_TYPEOF, SetRewriter(REWRITE_TYPEOF_FUNCTION) - .set_propagates_collation(false)} + .set_propagates_collation(false)}, }); } } diff --git a/zetasql/common/builtin_function_map.cc b/zetasql/common/builtin_function_map.cc index 965334d68..3a59ae302 100644 --- a/zetasql/common/builtin_function_map.cc +++ b/zetasql/common/builtin_function_map.cc @@ -37,6 +37,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_macros.h" @@ -158,15 +159,16 @@ static absl::StatusOr ComputeMapEntriesFunctionResultType( static inline absl::StatusOr GetMapTypeFromInputArg( const InputArgumentType& map_arg) { - ZETASQL_RET_CHECK(map_arg.type()->IsMap()) << "Input must be a map"; + ZETASQL_RET_CHECK(map_arg.type() != nullptr && map_arg.type()->IsMap()) + << "Input must be a map"; return map_arg.type()->AsMap(); } static inline absl::Status MakeErrorIfMapElementTypeNotOrderable( absl::string_view function_name, const Type* orderable_type, - const AnalyzerOptions& analyzer_options) { + const LanguageOptions& language_options) { std::string ordering_type_description; - if (!orderable_type->SupportsOrdering(analyzer_options.language(), + if (!orderable_type->SupportsOrdering(language_options, &ordering_type_description)) { return MakeSqlError() << function_name << ": MAP element type " << ordering_type_description << " is not orderable"; @@ -174,89 +176,25 @@ static inline absl::Status MakeErrorIfMapElementTypeNotOrderable( return absl::OkStatus(); } -static absl::StatusOr ComputeMapKeysSortedFunctionResultType( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - ZETASQL_RET_CHECK_EQ(arguments.size(), 1); - ZETASQL_ASSIGN_OR_RETURN(const MapType* map_type, - GetMapTypeFromInputArg(arguments[0])); - - ZETASQL_RETURN_IF_ERROR(MakeErrorIfMapElementTypeNotOrderable( - "MAP_KEYS_SORTED", map_type->key_type(), analyzer_options)); - - const Type* array_type; - ZETASQL_RETURN_IF_ERROR( - type_factory->MakeArrayType(map_type->key_type(), &array_type)); - return array_type; -} - -static absl::StatusOr ComputeMapKeysUnsortedFunctionResultType( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - ZETASQL_RET_CHECK_EQ(arguments.size(), 1); - ZETASQL_ASSIGN_OR_RETURN(const MapType* map_type, - GetMapTypeFromInputArg(arguments[0])); - - const Type* array_type; - ZETASQL_RETURN_IF_ERROR( - type_factory->MakeArrayType(map_type->key_type(), &array_type)); - return array_type; -} - -static absl::StatusOr ComputeMapValuesSortedFunctionResultType( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - ZETASQL_RET_CHECK_EQ(arguments.size(), 1); - ZETASQL_ASSIGN_OR_RETURN(const MapType* map_type, - GetMapTypeFromInputArg(arguments[0])); - - ZETASQL_RETURN_IF_ERROR(MakeErrorIfMapElementTypeNotOrderable( - "MAP_VALUES_SORTED", map_type->value_type(), analyzer_options)); +enum class KeyOrValueSelector { + kKey, + kValue, +}; - const Type* array_type; - ZETASQL_RETURN_IF_ERROR( - type_factory->MakeArrayType(map_type->value_type(), &array_type)); - return array_type; -} - -static absl::StatusOr ComputeMapValuesUnsortedFunctionResultType( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, +static absl::Status CheckOrderableMapArgumentConstraint( + absl::string_view fn_name, int map_arg_idx, KeyOrValueSelector key_or_value, absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - ZETASQL_RET_CHECK_EQ(arguments.size(), 1); - ZETASQL_ASSIGN_OR_RETURN(const MapType* map_type, - GetMapTypeFromInputArg(arguments[0])); - - const Type* array_type; - ZETASQL_RETURN_IF_ERROR( - type_factory->MakeArrayType(map_type->value_type(), &array_type)); - return array_type; -} - -static absl::StatusOr -ComputeMapValuesSortedByKeyFunctionResultType( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { + const LanguageOptions& language_options) { ZETASQL_RET_CHECK_EQ(arguments.size(), 1); ZETASQL_ASSIGN_OR_RETURN(const MapType* map_type, - GetMapTypeFromInputArg(arguments[0])); + GetMapTypeFromInputArg(arguments[map_arg_idx])); - ZETASQL_RETURN_IF_ERROR(MakeErrorIfMapElementTypeNotOrderable( - "MAP_VALUES_SORTED", map_type->key_type(), analyzer_options)); + const Type* orderable_type = (key_or_value == KeyOrValueSelector::kKey) + ? map_type->key_type() + : map_type->value_type(); - const Type* array_type; - ZETASQL_RETURN_IF_ERROR( - type_factory->MakeArrayType(map_type->value_type(), &array_type)); - return array_type; + return MakeErrorIfMapElementTypeNotOrderable(fn_name, orderable_type, + language_options); } void GetMapCoreFunctions(TypeFactory* type_factory, @@ -325,7 +263,7 @@ void GetMapCoreFunctions(TypeFactory* type_factory, )sql"; InsertFunction(functions, options, "map_keys_sorted", Function::SCALAR, { - {ARG_TYPE_ARBITRARY, + {ARG_ARRAY_TYPE_ANY_1, {input_map_argument_type}, FN_MAP_KEYS_SORTED, FunctionSignatureOptions().set_rewrite_options( @@ -334,27 +272,26 @@ void GetMapCoreFunctions(TypeFactory* type_factory, .set_sql(kMapKeysSortedSql))}, }, FunctionOptions() - .set_compute_result_type_callback( - &ComputeMapKeysSortedFunctionResultType) + .set_pre_resolution_argument_constraint(absl::bind_front( + CheckOrderableMapArgumentConstraint, "MAP_KEYS_SORTED", + /*map_arg_idx=*/0, KeyOrValueSelector::kKey)) .AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); constexpr absl::string_view kMapKeysUnsortedSql = R"sql( (SELECT FLATTEN(MAP_ENTRIES_UNSORTED(input_map).key)) )sql"; - InsertFunction(functions, options, "map_keys_unsorted", Function::SCALAR, - { - {ARG_TYPE_ARBITRARY, - {input_map_argument_type}, - FN_MAP_KEYS_UNSORTED, - FunctionSignatureOptions().set_rewrite_options( - FunctionSignatureRewriteOptions() - .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) - .set_sql(kMapKeysUnsortedSql))}, - }, - FunctionOptions() - .set_compute_result_type_callback( - &ComputeMapKeysUnsortedFunctionResultType) - .AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); + InsertFunction( + functions, options, "map_keys_unsorted", Function::SCALAR, + { + {ARG_ARRAY_TYPE_ANY_1, + {input_map_argument_type}, + FN_MAP_KEYS_UNSORTED, + FunctionSignatureOptions().set_rewrite_options( + FunctionSignatureRewriteOptions() + .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) + .set_sql(kMapKeysUnsortedSql))}, + }, + FunctionOptions().AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); constexpr absl::string_view kMapValuesSortedSql = R"sql( ( @@ -368,7 +305,7 @@ void GetMapCoreFunctions(TypeFactory* type_factory, )sql"; InsertFunction(functions, options, "map_values_sorted", Function::SCALAR, { - {ARG_TYPE_ARBITRARY, + {ARG_ARRAY_TYPE_ANY_2, {input_map_argument_type}, FN_MAP_VALUES_SORTED, FunctionSignatureOptions().set_rewrite_options( @@ -377,46 +314,82 @@ void GetMapCoreFunctions(TypeFactory* type_factory, .set_sql(kMapValuesSortedSql))}, }, FunctionOptions() - .set_compute_result_type_callback( - &ComputeMapValuesSortedFunctionResultType) + .set_pre_resolution_argument_constraint(absl::bind_front( + CheckOrderableMapArgumentConstraint, + "MAP_VALUES_SORTED", 0, KeyOrValueSelector::kValue)) .AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); constexpr absl::string_view kMapValuesUnsortedSql = R"sql( (SELECT FLATTEN(MAP_ENTRIES_UNSORTED(input_map).value)) )sql"; - InsertFunction(functions, options, "map_values_unsorted", Function::SCALAR, - { - {ARG_TYPE_ARBITRARY, - {input_map_argument_type}, - FN_MAP_VALUES_UNSORTED, - FunctionSignatureOptions().set_rewrite_options( - FunctionSignatureRewriteOptions() - .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) - .set_sql(kMapValuesUnsortedSql))}, - }, - FunctionOptions() - .set_compute_result_type_callback( - &ComputeMapValuesUnsortedFunctionResultType) - .AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); + InsertFunction( + functions, options, "map_values_unsorted", Function::SCALAR, + { + {ARG_ARRAY_TYPE_ANY_2, + {input_map_argument_type}, + FN_MAP_VALUES_UNSORTED, + FunctionSignatureOptions().set_rewrite_options( + FunctionSignatureRewriteOptions() + .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) + .set_sql(kMapValuesUnsortedSql))}, + }, + FunctionOptions().AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); constexpr absl::string_view kMapValuesSortedByKeySql = R"sql( (SELECT FLATTEN(MAP_ENTRIES_SORTED(input_map).value)) )sql"; - InsertFunction(functions, options, "map_values_sorted_by_key", - Function::SCALAR, - { - {ARG_TYPE_ARBITRARY, - {input_map_argument_type}, - FN_MAP_VALUES_SORTED_BY_KEY, - FunctionSignatureOptions().set_rewrite_options( - FunctionSignatureRewriteOptions() - .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) - .set_sql(kMapValuesSortedByKeySql))}, - }, - FunctionOptions() - .set_compute_result_type_callback( - &ComputeMapValuesSortedByKeyFunctionResultType) - .AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); + InsertFunction( + functions, options, "map_values_sorted_by_key", Function::SCALAR, + { + {ARG_ARRAY_TYPE_ANY_2, + {input_map_argument_type}, + FN_MAP_VALUES_SORTED_BY_KEY, + FunctionSignatureOptions().set_rewrite_options( + FunctionSignatureRewriteOptions() + .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) + .set_sql(kMapValuesSortedByKeySql))}, + }, + FunctionOptions() + .set_pre_resolution_argument_constraint(absl::bind_front( + CheckOrderableMapArgumentConstraint, "MAP_VALUES_SORTED_BY_KEY", + 0, KeyOrValueSelector::kKey)) + .AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); + + constexpr absl::string_view kMapEmptySql = R"sql( + (SELECT ARRAY_LENGTH(MAP_ENTRIES_UNSORTED(input_map)) = 0) + )sql"; + InsertFunction( + functions, options, "map_empty", Function::SCALAR, + { + {type_factory->get_bool(), + {input_map_argument_type}, + FN_MAP_EMPTY, + FunctionSignatureOptions().set_rewrite_options( + FunctionSignatureRewriteOptions() + .set_rewriter(REWRITE_BUILTIN_FUNCTION_INLINER) + .set_sql(kMapEmptySql))}, + }, + FunctionOptions().AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE)); + + const FunctionArgumentTypeList map_insert_argument_types = { + input_map_argument_type, + ARG_TYPE_ANY_1, + ARG_TYPE_ANY_2, + // The function library treats multiple repeated arguments as interleaved. + // Thus, this creates the signature of (MAP, K, V, K, V, ...). + {ARG_TYPE_ANY_1, FunctionArgumentType::REPEATED}, + {ARG_TYPE_ANY_2, FunctionArgumentType::REPEATED}, + }; + FunctionOptions map_insert_function_options = + FunctionOptions().AddRequiredLanguageFeature(FEATURE_V_1_4_MAP_TYPE); + InsertFunction( + functions, options, "map_insert", Function::SCALAR, + {{ARG_MAP_TYPE_ANY_1_2, map_insert_argument_types, FN_MAP_INSERT}}, + map_insert_function_options); + InsertFunction(functions, options, "map_insert_or_replace", Function::SCALAR, + {{ARG_MAP_TYPE_ANY_1_2, map_insert_argument_types, + FN_MAP_INSERT_OR_REPLACE}}, + map_insert_function_options); } } // namespace zetasql diff --git a/zetasql/common/builtin_function_range.cc b/zetasql/common/builtin_function_range.cc index b1bafe55b..c6bfc297b 100644 --- a/zetasql/common/builtin_function_range.cc +++ b/zetasql/common/builtin_function_range.cc @@ -26,11 +26,12 @@ #include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" #include "absl/status/status.h" +#include "absl/types/span.h" namespace zetasql { absl::Status RangeFunctionPreResolutionArgumentConstraint( - const std::vector& args, + absl::Span args, const LanguageOptions& language_options) { if (args.size() != 2) { return MakeSqlError() << "RANGE() must take exactly two arguments"; @@ -59,7 +60,7 @@ absl::Status RangeFunctionPreResolutionArgumentConstraint( // Checks that the signature doesn't have untyped NULL values in place of // RANGE values, which are prohibited. absl::Status PreResolutionArgConstraintForUntypedNullOneRangeInput( - const std::vector& args, + absl::Span args, const LanguageOptions& language_options) { // If the argument is an untyped NULL for RANGE, we return an error // message for now until RANGE supports other types like INT64. @@ -77,7 +78,7 @@ absl::Status PreResolutionArgConstraintForUntypedNullOneRangeInput( // Checks that the signature doesn't have untyped NULL values in place of // RANGE values, which are prohibited. absl::Status PreResolutionArgConstraintForUntypedNullTwoRangeInputs( - const std::vector& args, + absl::Span args, const LanguageOptions& language_options) { // If the argument is an untyped NULL for RANGE, we return an error // message for now until RANGE supports other types like INT64. diff --git a/zetasql/common/builtin_function_sketches.cc b/zetasql/common/builtin_function_sketches.cc index 1d1e65f7f..ede6492d4 100644 --- a/zetasql/common/builtin_function_sketches.cc +++ b/zetasql/common/builtin_function_sketches.cc @@ -15,7 +15,6 @@ // #include -#include #include #include "google/protobuf/timestamp.pb.h" @@ -37,6 +36,7 @@ #include "zetasql/public/types/type.h" #include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" namespace zetasql { @@ -223,14 +223,37 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, FunctionOptions().set_allow_external_usage(false); // The optional second argument of 'init', the approximation precision or - // inverse epsilon ('inv_eps'), must be an integer >= 2 and cannot be NULL. + // inverse epsilon ('inv_eps'), must be an integer >= 1 and cannot be NULL. FunctionArgumentTypeOptions init_inv_eps_arg; init_inv_eps_arg.set_is_not_aggregate(); init_inv_eps_arg.set_must_be_non_null(); init_inv_eps_arg.set_cardinality(OPTIONAL); - init_inv_eps_arg.set_min_value(2); - // Init functions include a weight parameter only if NAMED_ARGUMENTS enabled. + bool use_float64_name = options.language_options.LanguageFeatureEnabled( + FEATURE_V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS); + auto make_function_name = [use_float64_name](absl::string_view name_prefix) { + return absl::StrCat(name_prefix, use_float64_name ? "_float64" : "_double"); + }; + bool is_internal_dialect = + options.language_options.product_mode() == PRODUCT_INTERNAL; + bool add_double_alias = use_float64_name && is_internal_dialect; + // Only enable external usage when float64 is the primary function name. + bool allow_external_usage = use_float64_name; + auto prepare_function_options = [add_double_alias, allow_external_usage]( + FunctionOptions options, + absl::string_view name_prefix) { + if (add_double_alias) { + options.set_alias_name(absl::StrCat(name_prefix, "_double")); + } + + if (allow_external_usage) { + options.set_allow_external_usage(true); + } + return options; + }; + + // Init functions include a weight parameter only if NAMED_ARGUMENTS + // enabled. if (options.language_options.LanguageFeatureEnabled( zetasql::FEATURE_V_1_3_KLL_WEIGHTS)) { // Explicitly set default value for precision (detailed in (broken link)) @@ -260,13 +283,16 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, FN_KLL_QUANTILES_INIT_UINT64}}, aggregate_analytic_function_options_and_not_allow_external_usage); InsertNamespaceFunction( - functions, options, "kll_quantiles", "init_double", AGGREGATE, + functions, options, "kll_quantiles", make_function_name("init"), + AGGREGATE, {{bytes_type, {double_type, {int64_type, init_inv_eps_arg}, {int64_type, init_weights_arg}}, FN_KLL_QUANTILES_INIT_DOUBLE}}, - aggregate_analytic_function_options_and_not_allow_external_usage); + prepare_function_options( + aggregate_analytic_function_options_and_not_allow_external_usage, + "init")); } else { // init functions with no weight parameter InsertNamespaceFunction(functions, options, "kll_quantiles", "init_int64", @@ -282,11 +308,14 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, FN_KLL_QUANTILES_INIT_UINT64}}, aggregate_analytic_function_options_and_not_allow_external_usage); InsertNamespaceFunction( - functions, options, "kll_quantiles", "init_double", AGGREGATE, + functions, options, "kll_quantiles", make_function_name("init"), + AGGREGATE, {{bytes_type, {double_type, {int64_type, init_inv_eps_arg}}, FN_KLL_QUANTILES_INIT_DOUBLE}}, - aggregate_analytic_function_options_and_not_allow_external_usage); + prepare_function_options( + aggregate_analytic_function_options_and_not_allow_external_usage, + "init")); } // Merge_partial @@ -297,11 +326,11 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, // The second argument of aggregate function 'merge', the number of // equidistant quantiles that should be returned; must be a non-aggregate - // integer >= 2 and cannot be NULL. + // integer >= 1 and cannot be NULL. FunctionArgumentTypeOptions num_quantiles_merge_arg; num_quantiles_merge_arg.set_is_not_aggregate(); num_quantiles_merge_arg.set_must_be_non_null(); - num_quantiles_merge_arg.set_min_value(2); + num_quantiles_merge_arg.set_min_value(1); // Merge InsertNamespaceFunction( @@ -320,18 +349,21 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, FN_KLL_QUANTILES_MERGE_UINT64}}, aggregate_analytic_function_options_and_not_allow_external_usage); InsertNamespaceFunction( - functions, options, "kll_quantiles", "merge_double", AGGREGATE, + functions, options, "kll_quantiles", make_function_name("merge"), + AGGREGATE, {{double_array_type, {bytes_type, {int64_type, num_quantiles_merge_arg}}, FN_KLL_QUANTILES_MERGE_DOUBLE}}, - aggregate_analytic_function_options_and_not_allow_external_usage); + prepare_function_options( + aggregate_analytic_function_options_and_not_allow_external_usage, + "merge")); // The second argument of scalar function 'extract', the number of - // equidistant quantiles that should be returned; must be an integer >= 2 and + // equidistant quantiles that should be returned; must be an integer >= 1 and // cannot be NULL. FunctionArgumentTypeOptions num_quantiles_extract_arg; num_quantiles_extract_arg.set_must_be_non_null(); - num_quantiles_extract_arg.set_min_value(2); + num_quantiles_extract_arg.set_min_value(1); // Extract InsertNamespaceFunction( @@ -347,11 +379,13 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, FN_KLL_QUANTILES_EXTRACT_UINT64}}, scalar_function_options_disallow_external_usage); InsertNamespaceFunction( - functions, options, "kll_quantiles", "extract_double", SCALAR, + functions, options, "kll_quantiles", make_function_name("extract"), + SCALAR, {{double_array_type, {bytes_type, {int64_type, num_quantiles_extract_arg}}, FN_KLL_QUANTILES_EXTRACT_DOUBLE}}, - scalar_function_options_disallow_external_usage); + prepare_function_options(scalar_function_options_disallow_external_usage, + "extract")); // The second argument of aggregate function 'merge_point', phi, must be a // non-aggregate double in [0, 1] and cannot be null. @@ -375,11 +409,14 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, FN_KLL_QUANTILES_MERGE_POINT_UINT64}}, aggregate_analytic_function_options_and_not_allow_external_usage); InsertNamespaceFunction( - functions, options, "kll_quantiles", "merge_point_double", AGGREGATE, + functions, options, "kll_quantiles", make_function_name("merge_point"), + AGGREGATE, {{double_type, {bytes_type, {double_type, phi_merge_arg}}, FN_KLL_QUANTILES_MERGE_POINT_DOUBLE}}, - aggregate_analytic_function_options_and_not_allow_external_usage); + prepare_function_options( + aggregate_analytic_function_options_and_not_allow_external_usage, + "merge_point")); // The second argument of scalar function 'extract_point', phi, must be a // double in [0, 1] and cannot be null. @@ -401,12 +438,14 @@ void GetKllQuantilesFunctions(TypeFactory* type_factory, {bytes_type, {double_type, phi_extract_arg}}, FN_KLL_QUANTILES_EXTRACT_POINT_UINT64}}, scalar_function_options_disallow_external_usage); - InsertNamespaceFunction(functions, options, "kll_quantiles", - "extract_point_double", SCALAR, - {{double_type, - {bytes_type, {double_type, phi_extract_arg}}, - FN_KLL_QUANTILES_EXTRACT_POINT_DOUBLE}}, - scalar_function_options_disallow_external_usage); + InsertNamespaceFunction( + functions, options, "kll_quantiles", make_function_name("extract_point"), + SCALAR, + {{double_type, + {bytes_type, {double_type, phi_extract_arg}}, + FN_KLL_QUANTILES_EXTRACT_POINT_DOUBLE}}, + prepare_function_options(scalar_function_options_disallow_external_usage, + "extract_point")); } } // namespace zetasql diff --git a/zetasql/common/internal_analyzer_output_properties.h b/zetasql/common/internal_analyzer_output_properties.h new file mode 100644 index 000000000..28e1b79a1 --- /dev/null +++ b/zetasql/common/internal_analyzer_output_properties.h @@ -0,0 +1,54 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_COMMON_INTERNAL_ANALYZER_OUTPUT_PROPERTIES_H_ +#define ZETASQL_COMMON_INTERNAL_ANALYZER_OUTPUT_PROPERTIES_H_ + +#include "zetasql/public/analyzer_output_properties.h" +#include "zetasql/resolved_ast/resolved_node.h" +#include "zetasql/resolved_ast/target_syntax.h" + +namespace zetasql { + +// InternalAnalyzerOutputProperties only contains static methods to access +// private fields of AnalyzerOutputProperties that are not part of the public +// APIs. +class InternalAnalyzerOutputProperties { + public: + InternalAnalyzerOutputProperties() = delete; + InternalAnalyzerOutputProperties(const InternalAnalyzerOutputProperties&) = + delete; + InternalAnalyzerOutputProperties& operator=( + const InternalAnalyzerOutputProperties&) = delete; + + // Inserts the `key` and `target_syntax` pair into the target syntax map + // of `options`. + static void MarkTargetSyntax(AnalyzerOutputProperties& options, + ResolvedNode* key, + SQLBuildTargetSyntax target_syntax) { + options.target_syntax_.insert({key, target_syntax}); + } + + // Returns the target syntax map of `options`. + static const TargetSyntaxMap& GetTargetSyntax( + const AnalyzerOutputProperties& options) { + return options.target_syntax_; + } +}; + +} // namespace zetasql + +#endif // ZETASQL_COMMON_INTERNAL_ANALYZER_OUTPUT_PROPERTIES_H_ diff --git a/zetasql/common/multiprecision_int.h b/zetasql/common/multiprecision_int.h index 5e9e2be36..f3adf097b 100644 --- a/zetasql/common/multiprecision_int.h +++ b/zetasql/common/multiprecision_int.h @@ -153,6 +153,7 @@ #include "absl/base/attributes.h" #include "absl/base/optimization.h" #include "absl/strings/string_view.h" +#include "absl/types/compare.h" #include "absl/types/span.h" #include "zetasql/base/endian.h" @@ -794,6 +795,21 @@ inline bool operator>=(const FixedUint& lh, return !(lh < rh); } +#ifdef __cpp_impl_three_way_comparison +template +inline std::strong_ordering operator<=>( + const FixedUint& lh, + const FixedUint& rh) { + if (lh == rh) { + return std::strong_ordering::equal; + } + if (lh < rh) { + return std::strong_ordering::less; + } + return std::strong_ordering::greater; +} +#endif + template void FixedUint::AppendToString( std::string* result) const { @@ -1387,6 +1403,22 @@ inline bool operator>=(const FixedInt& lh, return !(lh < rh); } +#ifdef __cpp_impl_three_way_comparison +template +inline std::strong_ordering operator<=>( + const FixedInt& lh, + const FixedInt& rh) { + if (lh == rh) { + return std::strong_ordering::equal; + } + if (lh < rh) { + return std::strong_ordering::less; + } else { + return std::strong_ordering::greater; + } +} +#endif // __cpp_impl_three_way_comparison + // Equivalent to FixedUint(x) *= FixedUint(y) // except that this method is typically 50-60% faster than the above code. // This method never overflows. diff --git a/zetasql/common/multiprecision_int_test.cc b/zetasql/common/multiprecision_int_test.cc index eda0529b6..3fd0898b8 100644 --- a/zetasql/common/multiprecision_int_test.cc +++ b/zetasql/common/multiprecision_int_test.cc @@ -1644,6 +1644,9 @@ TYPED_TEST(FixedIntGeneratedDataTest, Compare) { EXPECT_EQ(tx > ty, x > y) << x << " > " << y; EXPECT_EQ(tx <= ty, x <= y) << x << " <= " << y; EXPECT_EQ(tx >= ty, x >= y) << x << " >= " << y; +#ifdef __cpp_impl_three_way_comparison + EXPECT_EQ(tx <=> ty, x <=> y) << x << " <=> " << y; +#endif } } diff --git a/zetasql/common/options_utils.cc b/zetasql/common/options_utils.cc index 02ce56969..98014cfd8 100644 --- a/zetasql/common/options_utils.cc +++ b/zetasql/common/options_utils.cc @@ -151,7 +151,7 @@ absl::StatusOr> ParseEnabledLanguageFeatures( {"ALL", GetAllLanguageFeatures()}, {"MAXIMUM", GetMaximumLanguageFeatures()}, {"DEV", GetDevLanguageFeatures()}}, - "FEATURE_", "Rewrite", options_str); + "FEATURE_", "Feature", options_str); } bool AbslParseFlag(absl::string_view text, EnabledLanguageFeatures* p, diff --git a/zetasql/common/options_utils.h b/zetasql/common/options_utils.h index 2ffe77431..9a7d93623 100644 --- a/zetasql/common/options_utils.h +++ b/zetasql/common/options_utils.h @@ -116,9 +116,9 @@ struct EnabledLanguageFeatures { values must be listed with 'FEATURE_' stripped Example: - --enabled_ast_rewrites='DEFAULT,-FLATTEN,+ANONYMIZATION' - Will enable all the default options plus ANONYMIZATION, but excluding - flatten)"; + --enabled_language_features='MAXIMUM,-TABLESAMPLE,+ANONYMIZATION' + Will enable all the ideally enabled features plus ANONYMIZATION, but excluding + TABLESAMPLE)"; }; // Helper function to parse a flag using ParseEnabledLanguageFeatures. To Use: diff --git a/zetasql/compliance/BUILD b/zetasql/compliance/BUILD index a8ab10f01..0df75ed74 100644 --- a/zetasql/compliance/BUILD +++ b/zetasql/compliance/BUILD @@ -15,6 +15,7 @@ # load("@bazel_skylib//:bzl_library.bzl", "bzl_library") + # Placeholder: load py_proto_library load( "//zetasql/compliance:builddefs.bzl", @@ -59,6 +60,7 @@ cc_library( "//zetasql/public:options_cc_proto", "//zetasql/public:parse_helpers", "//zetasql/public:simple_catalog", + "//zetasql/public:simple_catalog_util", "//zetasql/public:type", "//zetasql/public:type_cc_proto", "//zetasql/public:value", @@ -68,6 +70,7 @@ cc_library( "//zetasql/reference_impl:reference_driver", "//zetasql/resolved_ast", "//zetasql/resolved_ast:resolved_node_kind_cc_proto", + "//zetasql/resolved_ast:sql_builder", "//zetasql/testing:type_util", "@com_google_absl//absl/cleanup", "@com_google_absl//absl/container:btree", @@ -447,7 +450,6 @@ cc_library( "//zetasql/common/testing:testing_proto_util", "//zetasql/public:builtin_function", "//zetasql/public:builtin_function_options", - "//zetasql/public:catalog", "//zetasql/public:function_headers", "//zetasql/public:language_options", "//zetasql/public:simple_catalog", diff --git a/zetasql/compliance/compliance_label_extractor.cc b/zetasql/compliance/compliance_label_extractor.cc index 5dea939d9..af20f8363 100644 --- a/zetasql/compliance/compliance_label_extractor.cc +++ b/zetasql/compliance/compliance_label_extractor.cc @@ -194,8 +194,6 @@ struct TypeCastLabel { return from_type == rhs.from_type && to_type == rhs.to_type; } - bool operator!=(const TypeCastLabel& rhs) const { return !(*this == rhs); } - std::string DebugString() { return absl::StrCat(TypeKind_Name(from_type), ":", TypeKind_Name(to_type)); } diff --git a/zetasql/compliance/compliance_test_cases.cc b/zetasql/compliance/compliance_test_cases.cc index 345f5ebab..b6208058c 100644 --- a/zetasql/compliance/compliance_test_cases.cc +++ b/zetasql/compliance/compliance_test_cases.cc @@ -202,22 +202,6 @@ static std::vector WrapFunctionTestWithFeature( return wrapped_tests; } -static std::vector WrapFunctionTestWithFeatures( - absl::Span tests, - std::vector& features) { - std::vector wrapped_tests; - wrapped_tests.reserve(tests.size()); - QueryParamsWithResult::FeatureSet feature_set; - for (const LanguageFeature& feature : features) { - feature_set.insert(feature); - } - for (FunctionTestCall call : tests) { - call.params = call.params.WrapWithFeatureSet(feature_set); - wrapped_tests.emplace_back(call); - } - return wrapped_tests; -} - std::vector WrapFeatureAdditionalStringFunctions( absl::Span tests) { return WrapFunctionTestWithFeature(tests, @@ -463,7 +447,7 @@ ComplianceCodebasedTests::ComplianceCodebasedTests() ComplianceCodebasedTests::~ComplianceCodebasedTests() {} void ComplianceCodebasedTests::RunStatementTests( - const std::vector& statement_tests, + absl::Span statement_tests, absl::string_view sql_string) { return RunStatementTestsCustom( statement_tests, @@ -473,7 +457,7 @@ void ComplianceCodebasedTests::RunStatementTests( } void ComplianceCodebasedTests::RunFunctionTestsInfix( - const std::vector& function_tests, + absl::Span function_tests, absl::string_view operator_name) { return RunStatementTestsCustom( function_tests, [operator_name = std::string(operator_name)]( @@ -484,7 +468,7 @@ void ComplianceCodebasedTests::RunFunctionTestsInfix( } void ComplianceCodebasedTests::RunFunctionTestsInOperator( - const std::vector& function_tests) { + absl::Span function_tests) { return RunStatementTestsCustom( function_tests, [](const QueryParamsWithResult& p) { std::vector arg_str; @@ -993,25 +977,25 @@ SHARDED_TEST_F(ComplianceCodebasedTests, TestSafeArrayAvgFunctions, 1) { "SAFE.ARRAY_AVG"); } -SHARDED_TEST_F(ComplianceCodebasedTests, TestArrayOffsetFunctions, 2) { +SHARDED_TEST_F(ComplianceCodebasedTests, TestArrayOffsetFunctions, 3) { SetNamePrefix("ArrayOffset"); RunFunctionTestsPrefix(Shard(GetFunctionTestsArrayOffset(/*is_safe=*/false)), "ARRAY_OFFSET"); } -SHARDED_TEST_F(ComplianceCodebasedTests, TestSafeArrayOffsetFunctions, 2) { +SHARDED_TEST_F(ComplianceCodebasedTests, TestSafeArrayOffsetFunctions, 3) { SetNamePrefix("SafeArrayOffset"); RunFunctionTestsPrefix(Shard(GetFunctionTestsArrayOffset(/*is_safe=*/true)), "SAFE.ARRAY_OFFSET"); } -SHARDED_TEST_F(ComplianceCodebasedTests, TestArrayFindFunctions, 2) { +SHARDED_TEST_F(ComplianceCodebasedTests, TestArrayFindFunctions, 3) { SetNamePrefix("ArrayFind"); RunFunctionTestsPrefix(Shard(GetFunctionTestsArrayFind(/*is_safe=*/false)), "ARRAY_FIND"); } -SHARDED_TEST_F(ComplianceCodebasedTests, TestSafeArrayFindFunctions, 2) { +SHARDED_TEST_F(ComplianceCodebasedTests, TestSafeArrayFindFunctions, 3) { SetNamePrefix("SafeArrayFind"); RunFunctionTestsPrefix(Shard(GetFunctionTestsArrayFind(/*is_safe=*/true)), "SAFE.ARRAY_FIND"); @@ -2359,6 +2343,11 @@ SHARDED_TEST_F(ComplianceCodebasedTests, TestStringInitCapFunctions, 1) { Shard(WrapFeatureAdditionalStringFunctions(GetFunctionTestsInitCap()))); } +SHARDED_TEST_F(ComplianceCodebasedTests, TestStringSplitSubstrFunction, 1) { + // TODO: Add compliance tests for split_substr with collation. + RunFunctionCalls(Shard(GetFunctionTestsSplitSubstr(/*skip_collation=*/true))); +} + SHARDED_TEST_F(ComplianceCodebasedTests, TestStringParseNumericFunctions, 1) { RunFunctionCalls(Shard(GetFunctionTestsParseNumeric())); } diff --git a/zetasql/compliance/compliance_test_cases.h b/zetasql/compliance/compliance_test_cases.h index e76bc3ad9..6d378eec5 100644 --- a/zetasql/compliance/compliance_test_cases.h +++ b/zetasql/compliance/compliance_test_cases.h @@ -97,18 +97,18 @@ class ComplianceCodebasedTests : public SQLTestBase { // named @p0, @p1, etc. Examples of sql_string are '@p0 + @p1', 'sqrt(@p0)', // etc. void RunStatementTests( - const std::vector& statement_tests, + absl::Span statement_tests, absl::string_view sql_string); // Takes 'operator_name' as input and creates a sql string of the kind @p0 // op_name @p1 ... op_name @pn. void RunFunctionTestsInfix( - const std::vector& function_tests, + absl::Span function_tests, absl::string_view operator_name); // Creates a sql string of the kind @p0 IN (@p1, @p2, ...) void RunFunctionTestsInOperator( - const std::vector& function_tests); + absl::Span function_tests); // Creates a sql string of the kind @p0 IN UNNEST([@p1, @p2, ...]) void RunFunctionTestsInOperatorUnnestArray( @@ -177,19 +177,10 @@ class ComplianceCodebasedTests : public SQLTestBase { void RunFunctionTestsCustom(absl::Span function_tests, FCT get_sql_string); - // Runs a statement with the specified feature set and returns the result. - absl::StatusOr - ExecuteStatementWithFeatures( - const std::string& sql, const std::map& params, - const QueryParamsWithResult::FeatureSet& features); - // Runs a statement with required and/or prohibited features. void RunStatementOnFeatures(absl::string_view sql, const QueryParamsWithResult& params); - // Default TestDatabase used by many tests. - static TestDatabase GetDefaultTestDatabase(); - // Options that modify which specific tests we run or what behavior we // expect for a particular field in TestProtoFieldImpl. struct TestProtoFieldOptions { diff --git a/zetasql/compliance/depth_limit_detector_test_cases.cc b/zetasql/compliance/depth_limit_detector_test_cases.cc index 2759d0a8b..3bba936ff 100644 --- a/zetasql/compliance/depth_limit_detector_test_cases.cc +++ b/zetasql/compliance/depth_limit_detector_test_cases.cc @@ -477,6 +477,29 @@ AllDepthLimitDetectorTestCases() { .depth_limit_test_case_name = "repeated_minus_one", .depth_limit_template = {"SELECT 1", R({"-1"}), " AS c"}, }, + { + .depth_limit_test_case_name = "repeated_unary_plus", + .depth_limit_template = {"SELECT +", R({"+"}), "1 AS c"}, + }, + { + .depth_limit_test_case_name = "pipe_wheres", + .depth_limit_template = {"FROM (SELECT 1 c)", + R({"|> WHERE c > ", N()})}, + .depth_limit_required_features = + { + LanguageFeature::FEATURE_PIPES, + }, + }, + { + .depth_limit_test_case_name = "pipe_joins", + .depth_limit_template = {"FROM (SELECT 1 c)", + R({"|> JOIN (SELECT 2 d", N(), ") ON d", + N(), " = c"})}, + .depth_limit_required_features = + { + LanguageFeature::FEATURE_PIPES, + }, + }, { .depth_limit_test_case_name = "with_joins", .depth_limit_template = {"WITH ", diff --git a/zetasql/compliance/functions_testlib.h b/zetasql/compliance/functions_testlib.h index 409daa353..60798d010 100644 --- a/zetasql/compliance/functions_testlib.h +++ b/zetasql/compliance/functions_testlib.h @@ -257,8 +257,6 @@ std::vector GetFunctionTestsFormat(); std::vector GetFunctionTestsFormatWithExternalModeFloatType(); std::vector GetFunctionTestsArray(); std::vector GetFunctionTestsNormalize(); -std::vector GetFunctionTestsBase2(); -std::vector GetFunctionTestsBase8(); std::vector GetFunctionTestsBase32(); std::vector GetFunctionTestsBase64(); std::vector GetFunctionTestsHex(); @@ -353,6 +351,8 @@ std::vector GetFunctionTestsL1Norm(); std::vector GetFunctionTestsL2Norm(); std::vector GetFunctionTestsEditDistance(); std::vector GetFunctionTestsEditDistanceBytes(); +std::vector GetFunctionTestsSplitSubstr( + bool skip_collation = false); } // namespace zetasql diff --git a/zetasql/compliance/functions_testlib_2.cc b/zetasql/compliance/functions_testlib_2.cc index fc7284b36..abf68be28 100644 --- a/zetasql/compliance/functions_testlib_2.cc +++ b/zetasql/compliance/functions_testlib_2.cc @@ -51,6 +51,7 @@ #include "absl/status/status.h" #include "absl/strings/cord.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" #include "absl/types/span.h" namespace zetasql { @@ -2351,6 +2352,10 @@ GetOrderableTypesWithFeaturesAndValues() { {BytesType(), Bytes("0x00"), Bytes("0xAA"), Bytes("0xAA"), Bytes("0xFF")}, {TimestampType(), Timestamp(timestamp_min), Timestamp(1500000000), Timestamp(1500000000), Timestamp(timestamp_max)}, + {TimestampType(), Timestamp(absl::FromUnixNanos(15674896748561)), + Timestamp(absl::FromUnixNanos(15674896748562)), + Timestamp(absl::FromUnixNanos(15674896748562)), + Timestamp(absl::FromUnixNanos(15674896748563)), FEATURE_TIMESTAMP_NANOS}, {DateType(), Date(date_min), DateFromStr("1960-01-07"), DateFromStr("1960-01-07"), Date(date_max)}, {NumericType(), Numeric(NumericValue::MinValue()), Numeric(-3), @@ -2574,8 +2579,15 @@ static const std::vector GetArrayMinMaxTestCases( if (is_safe) { test_cases[i].AddRequiredFeature(FEATURE_V_1_2_SAFE_FUNCTION_CALL); } + + std::set required_features = v.required_features; + if (test_cases[i].result().is_null()) { + // TIMESTAMP_NANOS feature doesn't affect null results. + required_features.erase(FEATURE_TIMESTAMP_NANOS); + } + test_cases[i] - .AddRequiredFeatures(v.required_features) + .AddRequiredFeatures(required_features) .AddProhibitedFeature(FEATURE_DISABLE_ARRAY_MIN_AND_MAX); } } @@ -3512,6 +3524,13 @@ static std::vector GetArrayFindFunctionsTestCases( {v.input_array, v.target_element}, v.find_all_result)); } + // ARRAY_FIND always takes care of timestamp precision, + // FEATUE_TIMESTAMP_NANOS is not needed. + // TODO: Remove this once ARRAY_FIND reference implementation + // sanitizes the input for nanosecond precision. + std::set required_features = v.required_features; + required_features.erase(FEATURE_TIMESTAMP_NANOS); + for (size_t i = existing_num_tests; i < test_cases.size(); ++i) { if (is_safe) { test_cases[i].AddRequiredFeature(FEATURE_V_1_2_SAFE_FUNCTION_CALL); @@ -3519,7 +3538,7 @@ static std::vector GetArrayFindFunctionsTestCases( FEATURE_V_1_4_SAFE_FUNCTION_CALL_WITH_LAMBDA_ARGS); } test_cases[i] - .AddRequiredFeatures(v.required_features) + .AddRequiredFeatures(required_features) .AddRequiredFeature(FEATURE_V_1_4_ARRAY_FIND_FUNCTIONS); } } diff --git a/zetasql/compliance/functions_testlib_cast.cc b/zetasql/compliance/functions_testlib_cast.cc index 97165acb0..b82166340 100644 --- a/zetasql/compliance/functions_testlib_cast.cc +++ b/zetasql/compliance/functions_testlib_cast.cc @@ -2432,12 +2432,10 @@ std::vector GetFunctionTestsCastComplex() { &kitchen_sink_message)); absl::Cord bytes_2203; ABSL_CHECK(kitchen_sink_message.SerializeToCord(&bytes_2203)); - absl::Cord kitchen_sink_cord_3 = bytes_2203; ABSL_CHECK(google::protobuf::TextFormat::ParseFromString(kitchen_sink_string_4, &kitchen_sink_message)); absl::Cord bytes_2206; ABSL_CHECK(kitchen_sink_message.SerializeToCord(&bytes_2206)); - absl::Cord kitchen_sink_cord_4 = bytes_2206; zetasql_test__::NullableInt nullable_int_message; const std::string nullable_int_string_1("value: 1"); diff --git a/zetasql/compliance/functions_testlib_json.cc b/zetasql/compliance/functions_testlib_json.cc index 4025153d0..a9f5e4f98 100644 --- a/zetasql/compliance/functions_testlib_json.cc +++ b/zetasql/compliance/functions_testlib_json.cc @@ -39,6 +39,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" +#include "absl/types/span.h" namespace zetasql { namespace { @@ -3201,7 +3202,7 @@ std::vector GetFunctionTestsJsonStripNulls() { void JsonKeysSameResultForModes(absl::string_view json_input, std::optional max_depth, std::vector results, - const std::vector& modes, + absl::Span modes, std::vector& tests) { constexpr absl::string_view kModeArgumentName = "mode"; for (absl::string_view mode : modes) { diff --git a/zetasql/compliance/functions_testlib_string_5.cc b/zetasql/compliance/functions_testlib_string_5.cc index f8cfddd6e..b122798ef 100644 --- a/zetasql/compliance/functions_testlib_string_5.cc +++ b/zetasql/compliance/functions_testlib_string_5.cc @@ -14,13 +14,16 @@ // limitations under the License. // -#include +#include #include #include "zetasql/compliance/functions_testlib_common.h" #include "zetasql/public/value.h" #include "zetasql/testing/test_function.h" +#include "zetasql/testing/test_value.h" #include "zetasql/testing/using_test_value.cc" // NOLINT +#include "absl/status/status.h" +#include "absl/strings/string_view.h" namespace zetasql { @@ -745,4 +748,330 @@ std::vector GetFunctionTestsStringWithCollator() { return tests; } +struct CollatedResult { + std::string collation; + const ValueConstructor result; + absl::StatusCode status; + + CollatedResult(absl::string_view collation, const ValueConstructor& result, + absl::StatusCode status = absl::StatusCode::kOk) + : collation(collation), result(result), status(status) {} +}; + +struct FunctionTestWithCollator { + std::string function_name; + std::vector params; + std::vector results; +}; + +std::vector GetFunctionTestCalls( + const std::vector& tests, bool skip_collation) { + std::vector function_test_calls; + for (const auto& test : tests) { + for (const auto& result : test.results) { + if (skip_collation) { + // only push test-cases with binary collation + if (result.collation == "binary") { + QueryParamsWithResult query_params_with_result( + test.params, result.result, result.status); + function_test_calls.push_back( + {test.function_name, query_params_with_result}); + } + } else { + std::vector params_with_collator; + params_with_collator.reserve(test.params.size() + 1); + params_with_collator.push_back(result.collation); + + for (const auto& param : test.params) { + params_with_collator.push_back(param); + } + + QueryParamsWithResult query_params_with_result( + params_with_collator, result.result, result.status); + function_test_calls.push_back( + {test.function_name + "_with_collator", query_params_with_result}); + } + } + } + return function_test_calls; +} + +std::vector GetFunctionTestsSplitSubstr(bool skip_collation) { + std::vector tests{ + // Empty Delimiter + {"split_substr", + {"www.abc.com", "", 1l, 1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + // Non-UTF-8 Delimiter + {"split_substr", + {"www.abc.com", "\xc2", 1l, 1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + // Non-UTF-8 Text + {"split_substr", + {"www.abc.\xc2om", ".", 1l, 1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + // Negative Count + {"split_substr", + {"www.abc.com", ".", 1l, -1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + // Empty Text. + {"split_substr", {"", ".", 1l, 1l}, {{"binary", ""}, {"und:ci", ""}}}, + // Delimiter not present + {"split_substr", + {"www.abc.com", "#", 1l, 1l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 1l, 0l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"www.abc.com", ".", 1l, 2l}, + {{"binary", "www.abc"}, {"und:ci", "www.abc"}}}, + {"split_substr", + {"www.abc.com", ".", 1l, 1l}, + {{"binary", "www"}, {"und:ci", "www"}}}, + {"split_substr", + {"www.abc.com", ".", 1l, 2l}, + {{"binary", "www.abc"}, {"und:ci", "www.abc"}}}, + {"split_substr", + {"www.abc.com", ".", 1l, 3l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 1l, 4l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 0l, 4l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 2l, 3l}, + {{"binary", "abc.com"}, {"und:ci", "abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 3l, 3l}, + {{"binary", "com"}, {"und:ci", "com"}}}, + {"split_substr", + {"www.abc.com", ".", 4l, 1l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"www.abc.com", ".", 5l, 1l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"www.abc.com", ".", -1l, 1l}, + {{"binary", "com"}, {"und:ci", "com"}}}, + {"split_substr", + {"www.abc.com", ".", -2l, 3l}, + {{"binary", "abc.com"}, {"und:ci", "abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", -3l, 4l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", -4l, 1l}, + {{"binary", "www"}, {"und:ci", "www"}}}, + {"split_substr", + {"www.abc.com", ".", -5l, 1l}, + {{"binary", "www"}, {"und:ci", "www"}}}, + {"split_substr", + {"www.abc.com", ".", -5l, 2l}, + {{"binary", "www.abc"}, {"und:ci", "www.abc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 0l, 1l}, + {{"binary", "aaa"}, {"und:ci", "aaa"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 1l, 1l}, + {{"binary", "aaa"}, {"und:ci", "aaa"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 2l, 1l}, + {{"binary", "*bb"}, {"und:ci", "*bb"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 3l, 1l}, + {{"binary", "*cc"}, {"und:ci", "*cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 4l, 1l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"aaa***bb***cc", "**", -1l, 2l}, + {{"binary", "*cc"}, {"und:ci", "*cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -2l, 2l}, + {{"binary", "*bb***cc"}, {"und:ci", "*bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -3l, 2l}, + {{"binary", "aaa***bb"}, {"und:ci", "aaa***bb"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -4l, 2l}, + {{"binary", "aaa***bb"}, {"und:ci", "aaa***bb"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -5l, 2l}, + {{"binary", "aaa***bb"}, {"und:ci", "aaa***bb"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 100l, 2l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"aaa***bb***cc", "**", -100l, 2l}, + {{"binary", "aaa***bb"}, {"und:ci", "aaa***bb"}}}, + // Delimiter as prefix. + {"split_substr", + {"**aaa***bb***cc", "**", 1l, 1l}, + {{"binary", ""}, {"und:ci", ""}}}, + // Delimiter as suffix. + {"split_substr", + {"aaa***bb***cc**", "**", -1l, 1l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "Xx", 0l, 1l}, + {{"und:ci", "aaa"}, {"binary", "aaaXXXbbXXXcc"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "Xx", 1l, 1l}, {{"und:ci", "aaa"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "Xx", 2l, 1l}, {{"und:ci", "Xbb"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "Xx", 3l, 1l}, {{"und:ci", "Xcc"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "Xx", 4l, 1l}, {{"und:ci", ""}}}, + {"split_substr", {"aaaXXXbbXXXcc", "Xx", -1l, 2l}, {{"und:ci", "Xcc"}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "Xx", -2l, 2l}, + {{"und:ci", "XbbXXXcc"}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "Xx", -3l, 2l}, + {{"und:ci", "aaaXXXbb"}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "Xx", -4l, 2l}, + {{"und:ci", "aaaXXXbb"}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "Xx", -5l, 2l}, + {{"und:ci", "aaaXXXbb"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "Xx", 100l, 2l}, {{"und:ci", ""}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "Xx", -100l, 2l}, + {{"und:ci", "aaaXXXbb"}}}, + {"split_substr", + {"аБракадабра", "абр", 1l, 1l}, + {{"und:ci", ""}, {"binary", "аБракад"}}}, + {"split_substr", + {"аБракадабра", "абр", 2l, 1l}, + {{"und:ci", "акад"}, {"binary", "а"}}}, + {"split_substr", + {"aaa\0*\0*bb***cc", "**", 0l, 1l}, + {{"binary", "aaa\0*\0*bb"}, {"und:ci", "aaa\0"}}}, + // Delimiter as prefix. + {"split_substr", {"XXaaaXXXbbXXXcc", "Xx", 1l, 1l}, {{"und:ci", ""}}}, + // Delimiter as suffix. + {"split_substr", {"aaaXXXbbXXXccXX", "Xx", -1l, 1l}, {{"und:ci", ""}}}, + // without count argument + // // Empty Delimiter + {"split_substr", + {"www.abc.com", "", 1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + // Non-UTF-8 Delimiter + {"split_substr", + {"www.abc.com", "\xc2", 1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + // Non-UTF-8 Text + {"split_substr", + {"www.abc.\xc2om", ".", 1l}, + {{"binary", "", OUT_OF_RANGE}, {"und:ci", "", OUT_OF_RANGE}}}, + {"split_substr", + {"www.abc.com", "#", 1l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 0l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 1l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 2l}, + {{"binary", "abc.com"}, {"und:ci", "abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", 3l}, + {{"binary", "com"}, {"und:ci", "com"}}}, + {"split_substr", + {"www.abc.com", ".", 4l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"www.abc.com", ".", 5l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"www.abc.com", ".", -1l}, + {{"binary", "com"}, {"und:ci", "com"}}}, + {"split_substr", + {"www.abc.com", ".", -2l}, + {{"binary", "abc.com"}, {"und:ci", "abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", -3l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", -4l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"www.abc.com", ".", -5l}, + {{"binary", "www.abc.com"}, {"und:ci", "www.abc.com"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 0l}, + {{"binary", "aaa***bb***cc"}, {"und:ci", "aaa***bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 1l}, + {{"binary", "aaa***bb***cc"}, {"und:ci", "aaa***bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 2l}, + {{"binary", "*bb***cc"}, {"und:ci", "*bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 3l}, + {{"binary", "*cc"}, {"und:ci", "*cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 4l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"aaa***bb***cc", "**", -1l}, + {{"binary", "*cc"}, {"und:ci", "*cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -2l}, + {{"binary", "*bb***cc"}, {"und:ci", "*bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -3l}, + {{"binary", "aaa***bb***cc"}, {"und:ci", "aaa***bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -4l}, + {{"binary", "aaa***bb***cc"}, {"und:ci", "aaa***bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", -5l}, + {{"binary", "aaa***bb***cc"}, {"und:ci", "aaa***bb***cc"}}}, + {"split_substr", + {"aaa***bb***cc", "**", 100l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"aaa***bb***cc", "**", -100l}, + {{"binary", "aaa***bb***cc"}, {"und:ci", "aaa***bb***cc"}}}, + // Delimiter as prefix. + {"split_substr", + {"**aaa***bb***cc", "**", 1l}, + {{"binary", "**aaa***bb***cc"}, {"und:ci", "**aaa***bb***cc"}}}, + // Delimiter as suffix. + {"split_substr", + {"aaa***bb***cc**", "**", -1l}, + {{"binary", ""}, {"und:ci", ""}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "xX", 0l}, + {{"und:ci", "aaaXXXbbXXXcc"}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "xX", 1l}, + {{"und:ci", "aaaXXXbbXXXcc"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "xX", 2l}, {{"und:ci", "XbbXXXcc"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "xX", -1l}, {{"und:ci", "Xcc"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "xX", -2l}, {{"und:ci", "XbbXXXcc"}}}, + {"split_substr", {"aaaXXXbbXXXcc", "xX", 100l}, {{"und:ci", ""}}}, + {"split_substr", + {"aaaXXXbbXXXcc", "xX", -100l}, + {{"und:ci", "aaaXXXbbXXXcc"}}}, + {"split_substr", + {"аБракадабра", "Абр", 2l}, + {{"und:ci", "акадабра"}, {"binary", ""}}}, + {"split_substr", + {"аБракадабра", "Абр", 2l}, + {{"und:ci", "акадабра"}, {"binary", ""}}}, + {"split_substr", + {"аБракадабра", "Абр", 1l}, + {{"und:ci", "аБракадабра"}, {"binary", "аБракадабра"}}}, + {"split_substr", + {"aaa\0*\0*bb***cc", "**", 2l}, + {{"binary", "*cc"}, {"und:ci", "bb***cc"}}}, + }; + return GetFunctionTestCalls(tests, skip_collation); +} + } // namespace zetasql diff --git a/zetasql/compliance/functions_testlib_tojson.cc b/zetasql/compliance/functions_testlib_tojson.cc index 431ce29b7..2fd66a891 100644 --- a/zetasql/compliance/functions_testlib_tojson.cc +++ b/zetasql/compliance/functions_testlib_tojson.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,7 @@ #include "zetasql/common/float_margin.h" #include "zetasql/compliance/functions_testlib.h" #include "zetasql/compliance/functions_testlib_common.h" +#include "zetasql/public/civil_time.h" #include "zetasql/public/functions/date_time_util.h" #include "zetasql/public/interval_value.h" #include "zetasql/public/interval_value_test_util.h" @@ -32,6 +34,7 @@ #include "zetasql/public/options.pb.h" #include "zetasql/public/type.h" #include "zetasql/public/types/array_type.h" +#include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" #include "zetasql/testing/test_function.h" #include "zetasql/testing/using_test_value.cc" @@ -534,6 +537,105 @@ void AddIntervalValueTestCases(bool is_to_json, } } +// A helper function to add test cases for type Range to the result test +// set . If is true, adds TO_JSON test cases. Otherwise +// adds TO_JSON_STRING test cases. +void AddRangeTestCases(bool is_to_json, + std::vector& all_tests) { + Value datetime_start = + Datetime(DatetimeValue::FromYMDHMSAndMicros(2024, 4, 23, 10, 00, 00, 99)); + Value datetime_end = + Datetime(DatetimeValue::FromYMDHMSAndMicros(2024, 4, 24, 10, 00, 00, 99)); + const std::vector> + range_test_cases = { + {Range(Date(1), Date(2)), "1970-01-02", "1970-01-03"}, + {Range(NullDate(), Date(2)), "null", "1970-01-03"}, + {Range(Date(1), NullDate()), "1970-01-02", "null"}, + {Range(NullDate(), NullDate()), "null", "null"}, + {Range(datetime_start, datetime_end), "2024-04-23T10:00:00.000099", + "2024-04-24T10:00:00.000099"}, + {Range(NullDatetime(), datetime_end), "null", + "2024-04-24T10:00:00.000099"}, + {Range(datetime_start, NullDatetime()), "2024-04-23T10:00:00.000099", + "null"}, + {Range(NullDatetime(), NullDatetime()), "null", "null"}, + {Range(Timestamp(1), Timestamp(2)), "1970-01-01T00:00:00.000001Z", + "1970-01-01T00:00:00.000002Z"}, + {Range(NullTimestamp(), Timestamp(2)), "null", + "1970-01-01T00:00:00.000002Z"}, + {Range(Timestamp(1), NullTimestamp()), "1970-01-01T00:00:00.000001Z", + "null"}, + {Range(NullTimestamp(), NullTimestamp()), "null", "null"}, + }; + + for (const auto& test_case : range_test_cases) { + QueryParamsWithResult::FeatureSet required_features{FEATURE_RANGE_TYPE}; + const Value value = std::get<0>(test_case); + std::string start = std::get<1>(test_case); + if (start != "null") start = absl::StrCat("\"", start, "\""); + std::string end = std::get<2>(test_case); + if (end != "null") end = absl::StrCat("\"", end, "\""); + if (value.type()->AsRange()->element_type()->kind() == + TypeKind::TYPE_DATETIME) { + required_features.insert(FEATURE_V_1_2_CIVIL_TIME); + } + if (is_to_json) { + all_tests.emplace_back( + "to_json", + QueryParamsWithResult( + {value}, values::Json(*JSONValue::ParseJSONString(absl::StrCat( + R"({"end":)", end, R"(,"start":)", start, "}")))) + .AddRequiredFeatures(required_features) + .AddRequiredFeature(FEATURE_JSON_TYPE)); + } else { + all_tests.emplace_back( + "to_json_string", + QueryParamsWithResult( + {value}, String(absl::StrCat(R"({"start":)", start, R"(,"end":)", + end, "}"))) + .AddRequiredFeatures(required_features)); + } + } + + if (is_to_json) { + all_tests.emplace_back( + "to_json", + QueryParamsWithResult({Value::Null(types::DateRangeType())}, + Value::Value::Json(JSONValue())) + .AddRequiredFeatures({FEATURE_JSON_TYPE, FEATURE_RANGE_TYPE})); + all_tests.emplace_back( + "to_json", + QueryParamsWithResult({Value::Null(types::DatetimeRangeType())}, + Value::Value::Json(JSONValue())) + .AddRequiredFeatures({FEATURE_JSON_TYPE, FEATURE_RANGE_TYPE, + FEATURE_V_1_2_CIVIL_TIME})); + all_tests.emplace_back( + "to_json", + QueryParamsWithResult({Value::Null(types::TimestampRangeType())}, + Value::Value::Json(JSONValue())) + .AddRequiredFeatures({FEATURE_JSON_TYPE, FEATURE_RANGE_TYPE})); + } else { + all_tests.emplace_back( + "to_json_string", + QueryParamsWithResult({Value::Null(types::DateRangeType())}, + String("null")) + .AddRequiredFeatures({ + FEATURE_RANGE_TYPE, + })); + all_tests.emplace_back( + "to_json_string", + QueryParamsWithResult({Value::Null(types::DatetimeRangeType())}, + String("null")) + .AddRequiredFeatures( + {FEATURE_RANGE_TYPE, FEATURE_V_1_2_CIVIL_TIME})); + all_tests.emplace_back( + "to_json_string", + QueryParamsWithResult({Value::Null(types::TimestampRangeType())}, + String("null")) + .AddRequiredFeatures({FEATURE_RANGE_TYPE})); + } +} + void AddJsonTestCases(bool is_to_json, std::vector& all_tests) { constexpr absl::string_view kJsonValueString = @@ -809,11 +911,12 @@ std::vector GetFunctionTestsToJsonString() { AddCivilAndNanoTestCases(/*is_to_json=*/false, all_tests); AddIntervalValueTestCases(/*is_to_json=*/false, all_tests); AddJsonTestCases(/*is_to_json=*/false, all_tests); + AddRangeTestCases(/*is_to_json=*/false, all_tests); return all_tests; } // TODO: Unify primitive test cases when to_json_string supports -// stringify_wide_numbers if it keeps readablity and reduces duplication. +// stringify_wide_numbers if it keeps readability and reduces duplication. // Gets TO_JSON test cases, including nano_timestamp test cases. std::vector GetFunctionTestsToJson() { std::vector all_tests = { @@ -1059,6 +1162,7 @@ std::vector GetFunctionTestsToJson() { AddCivilAndNanoTestCases(/*is_to_json=*/true, all_tests); AddIntervalValueTestCases(/*is_to_json=*/true, all_tests); AddJsonTestCases(/*is_to_json=*/true, all_tests); + AddRangeTestCases(/*is_to_json=*/true, all_tests); return all_tests; } diff --git a/zetasql/compliance/runtime_expected_errors.cc b/zetasql/compliance/runtime_expected_errors.cc index 18b05f0c6..014cabd83 100644 --- a/zetasql/compliance/runtime_expected_errors.cc +++ b/zetasql/compliance/runtime_expected_errors.cc @@ -242,6 +242,16 @@ std::unique_ptr> RuntimeExpectedErrorMatcher( absl::StatusCode::kOutOfRange, "Invalid BIGNUMERIC value")); error_matchers.emplace_back(std::make_unique( absl::StatusCode::kOutOfRange, "Invalid INTERVAL value")); + // Casting strings to proto. + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Error parsing proto: Message type .* has no field named.*")); + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Error parsing proto: Expected identifier, got.*")); + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Protocol buffer missing required field.*")); // Regex/Like Errors // @@ -385,6 +395,12 @@ std::unique_ptr> RuntimeExpectedErrorMatcher( error_matchers.emplace_back(std::make_unique( absl::StatusCode::kOutOfRange, "Interval overflow during multiplication")); + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Unsupported date part (" + "DAYOFWEEK|DAYOFYEAR|QUARTER|DATE|WEEK|DATETIME|TIME|ISOYEAR|ISOWEEK|" + "WEEK_MONDAY|WEEK_TUESDAY|WEEK_WEDNESDAY|WEEK_THURSDAY|WEEK_FRIDAY|" + "WEEK_SATURDAY) in EXTRACT FROM INTERVAL")); error_matchers.emplace_back(std::make_unique( absl::StatusCode::kOutOfRange, "Illegal non-space trailing data")); @@ -417,6 +433,20 @@ std::unique_ptr> RuntimeExpectedErrorMatcher( absl::StatusCode::kUnimplemented, "Unsupported argument type for percentile_disc.")); + // Expected errors for SPLIT_SUBSTR function. + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Delimiter of SPLIT_SUBSTR function must be non-empty")); + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Text input to SPLIT_SUBSTR function must be valid UTF-8")); + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Delimiter of SPLIT_SUBSTR function must be valid UTF-8")); + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, + "Count of SPLIT_SUBSTR function must be non-negative")); + // Parsing and analysis errors. // error_matchers.emplace_back(std::make_unique( diff --git a/zetasql/compliance/sql_test_base.cc b/zetasql/compliance/sql_test_base.cc index f2283e7e3..dfce879df 100644 --- a/zetasql/compliance/sql_test_base.cc +++ b/zetasql/compliance/sql_test_base.cc @@ -35,6 +35,7 @@ #include #include + #include "zetasql/base/logging.h" #include "zetasql/base/path.h" #include "google/protobuf/text_format.h" @@ -60,6 +61,7 @@ #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_helpers.h" #include "zetasql/public/simple_catalog.h" +#include "zetasql/public/simple_catalog_util.h" #include "zetasql/public/type.pb.h" #include "zetasql/public/types/type.h" #include "zetasql/public/types/type_factory.h" @@ -69,6 +71,7 @@ #include "zetasql/reference_impl/reference_driver.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "zetasql/resolved_ast/resolved_node_kind.pb.h" +#include "zetasql/resolved_ast/sql_builder.h" #include "zetasql/testing/type_util.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -672,7 +675,7 @@ bool CompareStatementResult(const StatementResult& expected, if (actual.result.ok()) { if (InternalValue::Equals( expected.result.value(), actual.result.value(), - {.interval_compare_mode = IntervalCompareMode::kAllPartsEqual, + {.interval_compare_mode = IntervalCompareMode::kSqlEquals, .float_margin = float_margin, .reason = reason})) { return true; @@ -780,7 +783,7 @@ MATCHER_P2(ReturnsStatusOrValue, expected, float_margin, std::holds_alternative(arg.value())) { passed = InternalValue::Equals( std::get(expected.value()), std::get(arg.value()), - {.interval_compare_mode = IntervalCompareMode::kAllPartsEqual, + {.interval_compare_mode = IntervalCompareMode::kSqlEquals, .float_margin = float_margin, .reason = &reason}); } else if (std::holds_alternative(expected.value()) && @@ -1033,7 +1036,8 @@ std::string SQLTestBase::GenerateFailureReport(const std::string& expected, FloatMargin SQLTestBase::GetFloatEqualityMargin( absl::StatusOr actual, - absl::StatusOr expected, int max_ulp_bits) { + absl::StatusOr expected, int max_ulp_bits, + QueryResultStats* stats) { FloatMargin expanded_float_margin = kDefaultFloatMargin; int ulp_bits = 0; ::testing::StringMatchResultListener listener; @@ -1046,6 +1050,9 @@ FloatMargin SQLTestBase::GetFloatEqualityMargin( // file to see all unsuccessful tries. if (ReturnsCheckOnly(actual, expanded_float_margin) .MatchAndExplain(expected, &listener)) { + if (stats != nullptr) { + stats->verified_count += 1; + } break; } @@ -1671,18 +1678,19 @@ SQLTestBase::TestResults SQLTestBase::ExecuteTestCase() { absl::StatusOr result; std::optional is_deterministic_output = std::nullopt; if (IsVerifyingGoldens()) { - bool uses_unsupported_type = false; // unused AutoLanguageOptions options_cleanup(reference_driver()); LanguageOptions options_with_shadow_parsing = reference_driver_->language_options(); options_with_shadow_parsing.EnableLanguageFeature(FEATURE_SHADOW_PARSING); reference_driver()->SetLanguageOptions(options_with_shadow_parsing); if (script_mode_) { + is_deterministic_output = true; + ReferenceDriver::ExecuteScriptAuxOutput aux_output; result = reference_driver()->ExecuteScriptForReferenceDriver( sql_, parameters_, GetExecuteStatementOptions(), - execute_statement_type_factory(), &uses_unsupported_type); + execute_statement_type_factory(), aux_output); + is_deterministic_output = aux_output.is_deterministic_output; } else { - is_deterministic_output = true; ReferenceDriver::ExecuteStatementAuxOutput aux_output; result = reference_driver()->ExecuteStatementForReferenceDriver( sql_, parameters_, GetExecuteStatementOptions(), @@ -2152,9 +2160,11 @@ void SQLTestBase::StepExecuteStatementCheckResult() { bool uses_unsupported_type = false; absl::StatusOr ref_result; if (script_mode_) { + ReferenceDriver::ExecuteScriptAuxOutput aux_output; ref_result = reference_driver()->ExecuteScriptForReferenceDriver( sql_, parameters_, GetExecuteStatementOptions(), - execute_statement_type_factory(), &uses_unsupported_type); + execute_statement_type_factory(), aux_output); + uses_unsupported_type = aux_output.uses_unsupported_type.value_or(false); } else { ReferenceDriver::ExecuteStatementAuxOutput aux_output; ref_result = reference_driver()->ExecuteStatementForReferenceDriver( @@ -2563,8 +2573,9 @@ std::string SQLTestBase::ToString( ABSL_CHECK(value.is_valid()); if (format_value_content_options_ == nullptr) { result_string = InternalValue::FormatInternal( - value, - absl::GetFlag(FLAGS_zetasql_compliance_print_array_orderedness)); + value, {.force_type_at_top_level = true, + .include_array_ordereness = absl::GetFlag( + FLAGS_zetasql_compliance_print_array_orderedness)}); } else { result_string = InternalValue::FormatInternal(value, *format_value_content_options_); @@ -2594,9 +2605,13 @@ std::string SQLTestBase::ToString( const std::map& parameters) { std::vector param_strs; param_strs.reserve(parameters.size()); + InternalValue::FormatValueContentOptions format_options; + format_options.mode = + InternalValue::FormatValueContentOptions::Mode::kSQLExpression; for (const std::pair& entry : parameters) { - param_strs.emplace_back(absl::StrCat(" @", entry.first, " = ", - entry.second.FullDebugString())); + param_strs.emplace_back(absl::StrCat( + " @", entry.first, " = ", + InternalValue::FormatInternal(entry.second, format_options))); } std::sort(param_strs.begin(), param_strs.end()); return absl::StrJoin(param_strs, "\n"); diff --git a/zetasql/compliance/sql_test_base.h b/zetasql/compliance/sql_test_base.h index 21797547d..55d08ce60 100644 --- a/zetasql/compliance/sql_test_base.h +++ b/zetasql/compliance/sql_test_base.h @@ -134,6 +134,13 @@ class SQLTestCase { ParamsMap params_; }; +// Counts query result properties across multiple runs. +struct QueryResultStats { + int verified_count = 0; + int nonempty_result_count = 0; + int ordered_result_count = 0; +}; + class SQLTestBase : public ::testing::TestWithParam { public: using ComplianceTestCaseResult = std::variant; @@ -515,7 +522,8 @@ class SQLTestBase : public ::testing::TestWithParam { // two ComplianceTestCaseResult so that result can be considered equal. FloatMargin GetFloatEqualityMargin( absl::StatusOr actual, - absl::StatusOr expected, int max_ulp_bits); + absl::StatusOr expected, int max_ulp_bits, + QueryResultStats* stats = nullptr); protected: SQLTestBase(); @@ -639,6 +647,9 @@ class SQLTestBase : public ::testing::TestWithParam { // Accesses RegisterTestCaseInspector friend class UniqueNameTestEnvironment; + // Accesses sql_, parameters_ and full_name_ for generating a report. + friend class RQGBasedSqlBuilderTest; + // KnownErrorFilter needs to use stats_ to record failed statements. It also // needs to read sql_, location_, full_name_, and known_error_mode() for // statements. @@ -984,6 +995,21 @@ class UniversalPrinter> { } }; +// Teaches googletest to print StatusOr. +// This is important to make RQG test failures print a human consumable diff +// between expected and actual results. +template <> +class UniversalPrinter< + absl::StatusOr<::zetasql::SQLTestBase::ComplianceTestCaseResult>> { + public: + static void Print( + const absl::StatusOr<::zetasql::SQLTestBase::ComplianceTestCaseResult>& + status_or, + ::std::ostream* os) { + *os << ::zetasql::SQLTestBase::ToString(status_or); + } +}; + } // namespace internal } // namespace testing diff --git a/zetasql/compliance/test_database_catalog.cc b/zetasql/compliance/test_database_catalog.cc index e445c1844..5f314f5f5 100644 --- a/zetasql/compliance/test_database_catalog.cc +++ b/zetasql/compliance/test_database_catalog.cc @@ -31,7 +31,6 @@ #include "zetasql/public/language_options.h" #include "zetasql/public/simple_catalog.h" #include "zetasql/public/type.h" -#include "zetasql/public/types/type_deserializer.h" #include "zetasql/base/check.h" #include "absl/log/log.h" #include "absl/status/status.h" @@ -202,8 +201,6 @@ absl::Status TestDatabaseCatalog::SetTestDatabase(const TestDatabase& test_db) { const TestTable& test_table = t.second; AddTable(table_name, test_table); } - - // Add functions to the catalog. return absl::OkStatus(); } diff --git a/zetasql/compliance/test_driver.cc b/zetasql/compliance/test_driver.cc index 51342f74c..15783ce88 100644 --- a/zetasql/compliance/test_driver.cc +++ b/zetasql/compliance/test_driver.cc @@ -21,6 +21,7 @@ #include #include +#include "zetasql/compliance/test_driver.pb.h" #include "zetasql/public/annotation.pb.h" #include "zetasql/public/types/annotation.h" #include "zetasql/public/types/type.h" diff --git a/zetasql/compliance/test_driver.h b/zetasql/compliance/test_driver.h index 27530c94d..7dd78981f 100644 --- a/zetasql/compliance/test_driver.h +++ b/zetasql/compliance/test_driver.h @@ -225,7 +225,8 @@ struct TestDatabase { // Returns true if empty. bool empty() const { return proto_files.empty() && proto_names.empty() && enum_names.empty() && - tables.empty(); + tables.empty() + ; } // LINT.IfChange // File paths (*.proto) relative to the build workspace diff --git a/zetasql/compliance/testdata/aggregation_queries.test b/zetasql/compliance/testdata/aggregation_queries.test index 971ca946f..8406e554c 100644 --- a/zetasql/compliance/testdata/aggregation_queries.test +++ b/zetasql/compliance/testdata/aggregation_queries.test @@ -1386,6 +1386,19 @@ ARRAY>[ } ] == +[required_features=TIMESTAMP_NANOS] +[name=timestamp_min_max_nano_precision] +SELECT min(ts), max(ts) FROM ( + SELECT TIMESTAMP '1955-01-01 12:00:15.789567123 UTC' as ts UNION ALL + SELECT TIMESTAMP '2055-02-02 10:45:00.789567123 UTC') +-- +ARRAY>[ + { + 1955-01-01 12:00:15.789567123+00, + 2055-02-02 10:45:00.789567123+00 + } +] +== [name=timestamp_NULL_min_max] SELECT MAX(CAST(NULL AS TIMESTAMP)) AS max_NULL, MIN(CAST(NULL AS TIMESTAMP)) AS min_NULL @@ -3380,3 +3393,16 @@ ARRAY>[{1}] +== +[parameters="x" as val] +[required_features=V_1_1_LIMIT_IN_AGGREGATE] +[name=string_agg_with_parameters_and_limit] +select string_agg(@val limit 100) from (select 1) +-- +ARRAY>[{"x"}] +== +[required_features=V_1_1_LIMIT_IN_AGGREGATE] +[name=string_agg_with_constants_and_limit] +select array_agg("x" limit 100) from (select 1) +-- +ARRAY>>[{ARRAY["x"]}] diff --git a/zetasql/compliance/testdata/array_aggregation.test b/zetasql/compliance/testdata/array_aggregation.test index 938cce981..291d9ee58 100644 --- a/zetasql/compliance/testdata/array_aggregation.test +++ b/zetasql/compliance/testdata/array_aggregation.test @@ -466,6 +466,18 @@ ARRAY>>[ {ARRAY>[unknown order:{2}, {1}, {3}]} ] == +[required_features=V_1_1_ORDER_BY_IN_AGGREGATE] +[name=array_agg_of_subquery_with_order_by] +SELECT ARRAY_AGG((SELECT AS STRUCT a AS element) ORDER BY a) b +FROM ( + SELECT 1 AS a UNION ALL + SELECT 2 UNION ALL + SELECT 3) +-- +ARRAY>>[ + {ARRAY>[known order:{1}, {2}, {3}]} +] +== [name=array_agg_of_struct_expr_anonymous_fields] SELECT ARRAY_AGG((a, b)) c FROM @@ -1798,3 +1810,55 @@ ARRAY>>[{ARRAY[true]}] +== +[required_features=V_1_1_LIMIT_IN_AGGREGATE,V_1_1_ORDER_BY_IN_AGGREGATE] +[name=array_agg_distinct_subquery_with_limit_and_order_by] +select array_agg(distinct array_includes([1,2,3], x) order by array_includes([1,2,3], x) limit 123) +from unnest([1,2,3]) as x +-- +ARRAY>>[{ARRAY[true]}] +== +[required_features=V_1_1_ORDER_BY_IN_AGGREGATE] +[name=array_agg_distinct_subquery_with_order_by_two_vars] +select array_agg(distinct x+y order by x+y) +from unnest([1,2,3]) as x, unnest([5,6,7]) as y +-- +ARRAY>>[{ARRAY[known order:6, 7, 8, 9, 10]}] +== +[required_features=V_1_1_LIMIT_IN_AGGREGATE,V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE] +[name=array_agg_with_correlated_column_and_limit] +select w from +unnest([1,2,3]) as w +where ((select array_agg(w ignore nulls limit 100) from (select 1)) is not null) +order by w +-- +ARRAY>[known order:{1}, {2}, {3}] +== +[required_features=V_1_1_LIMIT_IN_AGGREGATE,V_1_1_ORDER_BY_IN_AGGREGATE,V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE] +[name=array_agg_with_correlated_column_and_limit_and_order_by] +select w from +unnest([1,2,3]) as w +where ((select array_agg(w ignore nulls order by w limit 100) from (select 1)) is not null) +order by w +-- +ARRAY>[known order:{1}, {2}, {3}] +== +[parameters=1 as val] +[required_features=V_1_1_LIMIT_IN_AGGREGATE] +[name=array_agg_with_parameters_and_limit] +select array_agg(@val limit 100) from (select 1) +-- +ARRAY>>[{ARRAY[1]}] +== +[required_features=V_1_1_LIMIT_IN_AGGREGATE] +[name=array_agg_with_constants_and_limit] +select array_agg(1 limit 100) from (select 1) +-- +ARRAY>>[{ARRAY[1]}] diff --git a/zetasql/compliance/testdata/interval.test b/zetasql/compliance/testdata/interval.test index a1270d49a..8604c8d60 100644 --- a/zetasql/compliance/testdata/interval.test +++ b/zetasql/compliance/testdata/interval.test @@ -961,3 +961,107 @@ ARRAY>[unknown order: {0-0 0 1:59:59.999999, false}, {0-0 0 -0:0:0.000001, false} ] +== +[required_features=INTERVAL_TYPE] +[name=justify_interval] +SELECT JUSTIFY_INTERVAL(i) AS justified_interval, + EXTRACT(YEAR FROM JUSTIFY_INTERVAL(i)) AS year, + EXTRACT(MONTH FROM JUSTIFY_INTERVAL(i)) AS month, + EXTRACT(DAY FROM JUSTIFY_INTERVAL(i)) AS day, + EXTRACT(HOUR FROM JUSTIFY_INTERVAL(i)) AS hour, + EXTRACT(MINUTE FROM JUSTIFY_INTERVAL(i)) AS minute, + EXTRACT(SECOND FROM JUSTIFY_INTERVAL(i)) AS second, + EXTRACT(MILLISECOND FROM JUSTIFY_INTERVAL(i)) AS milli, + EXTRACT(MICROSECOND FROM JUSTIFY_INTERVAL(i)) AS micro +FROM UNNEST([INTERVAL '1-2 3 4:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 35 4:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 -35 4:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 12 40:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 -12 -40:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 48 75:7:4.657' YEAR TO SECOND, + INTERVAL '1-2 -48 -75:7:4.657' YEAR TO SECOND]) AS i; +-- +ARRAY> +[unknown order: + {1-3 5 4:5:6.789, 1, 3, 5, 4, 5, 6, 789, 789000}, + {1-2 13 16:5:6.789, 1, 2, 13, 16, 5, 6, 789, 789000}, + {1-3 21 3:7:4.657, 1, 3, 21, 3, 7, 4, 657, 657000}, + {1-2 3 4:5:6.789, 1, 2, 3, 4, 5, 6, 789, 789000}, + {1-0 25 4:5:6.789, 1, 0, 25, 4, 5, 6, 789, 789000}, + {1-1 16 7:54:53.211, 1, 1, 16, 7, 54, 53, 211, 211000}, + {1-0 8 20:52:55.343, 1, 0, 8, 20, 52, 55, 343, 343000} +] +== +[required_features=INTERVAL_TYPE] +[name=justify_days] +SELECT JUSTIFY_DAYS(i) AS justified_days, + EXTRACT(YEAR FROM JUSTIFY_DAYS(i)) AS year, + EXTRACT(MONTH FROM JUSTIFY_DAYS(i)) AS month, + EXTRACT(DAY FROM JUSTIFY_DAYS(i)) AS day, + EXTRACT(HOUR FROM JUSTIFY_DAYS(i)) AS hour, + EXTRACT(MINUTE FROM JUSTIFY_DAYS(i)) AS minute, + EXTRACT(SECOND FROM JUSTIFY_DAYS(i)) AS second, + EXTRACT(MILLISECOND FROM JUSTIFY_DAYS(i)) AS milli, + EXTRACT(MICROSECOND FROM JUSTIFY_DAYS(i)) AS micro +FROM UNNEST([INTERVAL '1-2 3 4:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 35 48:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 -35 -48:5:6.789' YEAR TO SECOND]) AS i; +-- +ARRAY> +[unknown order: + {1-3 5 48:5:6.789, 1, 3, 5, 48, 5, 6, 789, 789000}, + {1-2 3 4:5:6.789, 1, 2, 3, 4, 5, 6, 789, 789000}, + {1-0 25 -48:5:6.789, 1, 0, 25, -48, -5, -6, -789, -789000} +] +== +[required_features=INTERVAL_TYPE] +[name=justify_hours] +SELECT JUSTIFY_HOURS(i) AS justified_hours, + EXTRACT(YEAR FROM JUSTIFY_HOURS(i)) AS year, + EXTRACT(MONTH FROM JUSTIFY_HOURS(i)) AS month, + EXTRACT(DAY FROM JUSTIFY_HOURS(i)) AS day, + EXTRACT(HOUR FROM JUSTIFY_HOURS(i)) AS hour, + EXTRACT(MINUTE FROM JUSTIFY_HOURS(i)) AS minute, + EXTRACT(SECOND FROM JUSTIFY_HOURS(i)) AS second, + EXTRACT(MILLISECOND FROM JUSTIFY_HOURS(i)) AS milli, + EXTRACT(MICROSECOND FROM JUSTIFY_HOURS(i)) AS micro +FROM UNNEST([INTERVAL '1-2 3 4:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 35 49:5:6.789' YEAR TO SECOND, + INTERVAL '1-2 -35 -49:5:6.789' YEAR TO SECOND]) AS i; +-- +ARRAY> +[unknown order: + {1-2 37 1:5:6.789, 1, 2, 37, 1, 5, 6, 789, 789000}, + {1-2 3 4:5:6.789, 1, 2, 3, 4, 5, 6, 789, 789000}, + {1-2 -37 -1:5:6.789, 1, 2, -37, -1, -5, -6, -789, -789000} +] diff --git a/zetasql/compliance/testdata/map_functions.test b/zetasql/compliance/testdata/map_functions.test index a44e6905b..c3d087d2e 100644 --- a/zetasql/compliance/testdata/map_functions.test +++ b/zetasql/compliance/testdata/map_functions.test @@ -565,3 +565,159 @@ ARRAY, ARRAY[known order:2, 1], ARRAY[known order:3, 2, 1] }] +== + +[name=map_empty] +SELECT + MAP_EMPTY(MAP_FROM_ARRAY([('a', 1)])) as one_element_map, + MAP_EMPTY(MAP_FROM_ARRAY([('a', 1), ('b', 2)])) as two_element_map, + MAP_EMPTY(MAP_FROM_ARRAY([(NULL, NULL)])) as null_element_map, + MAP_EMPTY(MAP_FROM_ARRAY(CAST([] AS ARRAY>))) as empty_map, + MAP_EMPTY(MAP_FROM_ARRAY(CAST(NULL AS ARRAY>))) as null_map; +-- +ARRAY>[{false, false, false, true, NULL}] +== + +[name=map_insert_and_map_insert_or_replace] +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', 2) as i_one_element_one_added, + MAP_INSERT(MAP_FROM_ARRAY([('a', 1), ('b', 2)]), 'c', 3) as i_two_elements_one_added, + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, 'c', 3) as i_one_element_two_added, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', 2) as r_one_element_one_added, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1), ('b', 2)]), 'c', 3) as r_two_elements_one_added, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, 'c', 3) as r_one_element_two_added; +-- +ARRAY, + i_two_elements_one_added MAP, + i_one_element_two_added MAP, + r_one_element_one_added MAP, + r_two_elements_one_added MAP, + r_one_element_two_added MAP + >> +[{ + {"a": 1, "b": 2}, + {"a": 1, "b": 2, "c": 3}, + {"a": 1, "b": 2, "c": 3}, + {"a": 1, "b": 2}, + {"a": 1, "b": 2, "c": 3}, + {"a": 1, "b": 2, "c": 3} + }] +== + +[name=map_insert_and_map_insert_or_replace_null_map] +SELECT + MAP_INSERT(NULL, 'a', 1) as i_one_added, + MAP_INSERT(NULL, 'b', 2, 'c', 3) as i_two_added, + MAP_INSERT(NULL, NULL, NULL) as i_null_key_one_added, + MAP_INSERT(NULL, 'b', 2, CAST(NULL as STRING), CAST(NULL as INT64)) as i_null_key_two_added, + MAP_INSERT_OR_REPLACE(NULL, 'a', 1) as r_one_added, + MAP_INSERT_OR_REPLACE(NULL, 'b', 2, 'c', 3) as r_two_added, + MAP_INSERT_OR_REPLACE(NULL, NULL, NULL) as r_null_key_one_added, + MAP_INSERT_OR_REPLACE(NULL, 'b', 2, CAST(NULL as STRING), CAST(NULL as INT64)) as r_null_key_two_added, +-- +ARRAY, + i_two_added MAP, + i_null_key_one_added MAP, + i_null_key_two_added MAP, + r_one_added MAP, + r_two_added MAP, + r_null_key_one_added MAP, + r_null_key_two_added MAP + >>[{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}] +== + +[name=map_insert_and_map_insert_or_replace_null_elements] +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), NULL, 2) as i_one_element_one_added_null_key, + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', NULL) as i_one_element_one_added_null_value, + MAP_INSERT(MAP_FROM_ARRAY([('a', 1), (NULL, 2)]), 'b', NULL) as i_two_elements_one_added_null_value, + MAP_INSERT(MAP_FROM_ARRAY([('a', 1), ('b', NULL)]), NULL, NULL) as i_two_elements_one_added_null_key, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), NULL, 2) as r_one_element_one_added_null_key, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', NULL) as r_one_element_one_added_null_value, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1), (NULL, 2)]), 'b', NULL) as r_two_elements_one_added_null_value, + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1), ('b', NULL)]), NULL, NULL) as r_two_elements_one_added_null_key, +-- +ARRAY, + i_one_element_one_added_null_value MAP, + i_two_elements_one_added_null_value MAP, + i_two_elements_one_added_null_key MAP, + r_one_element_one_added_null_key MAP, + r_one_element_one_added_null_value MAP, + r_two_elements_one_added_null_value MAP, + r_two_elements_one_added_null_key MAP + >> +[{ + {NULL: 2, "a": 1}, + {"a": 1, "b": NULL}, + {NULL: 2, "a": 1, "b": NULL}, + {NULL: NULL, "a": 1, "b": NULL}, + {NULL: 2, "a": 1}, + {"a": 1, "b": NULL}, + {NULL: 2, "a": 1, "b": NULL}, + {NULL: NULL, "a": 1, "b": NULL} + }] +== + +[name=map_insert_duplicate_key] +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1), ('b', 2)]), 'a', 3); +-- +ERROR: generic::out_of_range: Key already exists in map: "a" +== + +[name=map_insert_or_replace_duplicate_key] +SELECT + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1), ('b', 2)]), 'a', 3); +-- +ARRAY>>[{{"a": 3, "b": 2}}] +== + +[name=map_insert_duplicate_null_key] +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1), (NULL, 2)]), NULL, NULL); +-- +ERROR: generic::out_of_range: Key already exists in map: NULL +== + +[name=map_insert_or_replace_duplicate_null_key] +SELECT + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1), (NULL, 2)]), NULL, NULL); +-- +ARRAY>>[{{NULL: NULL, "a": 1}}] +== + +[name=map_insert_duplicate_key_in_args] +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, 'b', 2); +-- +ERROR: generic::out_of_range: Key provided more than once as argument: "b" +== + +[name=map_insert_or_replace_duplicate_key_in_args] +SELECT + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), 'b', 2, 'b', 2); +-- +ERROR: generic::out_of_range: Key provided more than once as argument: "b" +== + +[name=map_insert_duplicate_null_key_in_args] +SELECT + MAP_INSERT(MAP_FROM_ARRAY([('a', 1)]), NULL, 2, CAST(NULL AS STRING), 2); +-- +ERROR: generic::out_of_range: Key provided more than once as argument: NULL +== + +[name=map_insert_or_replace_duplicate_null_key_in_args] +SELECT + MAP_INSERT_OR_REPLACE(MAP_FROM_ARRAY([('a', 1)]), NULL, 2, CAST(NULL AS STRING), 2); +-- +ERROR: generic::out_of_range: Key provided more than once as argument: NULL diff --git a/zetasql/compliance/testdata/pipe_assert.test b/zetasql/compliance/testdata/pipe_assert.test new file mode 100644 index 000000000..b9d6e90f2 --- /dev/null +++ b/zetasql/compliance/testdata/pipe_assert.test @@ -0,0 +1,216 @@ +[prepare_database] +CREATE TABLE KeyValue +AS +SELECT 1 AS key, "value1" AS value +UNION ALL +SELECT 2, "value2" +-- +ARRAY>[{1, "value1"}, {2, "value2"}] +== + +[default required_features=PIPES,PIPE_ASSERT] +[name=assertion_succeeded_with_no_payload_result_order_is_unknown] +FROM UNNEST([1, 1, 1]) AS x +|> ASSERT x >= 1 +-- +ARRAY[unknown order:1, 1, 1] +== + +[name=assertion_succeeded_with_no_payload] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x >= 1 +|> ORDER BY x +-- +ARRAY[known order:1, 2, 3] +== + +[name=assertion_failed_with_no_payload] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x >= 2 +-- +ERROR: generic::out_of_range: Assert failed: x >= 2 +== + +[name=assert_condition_itself_errors] +FROM UNNEST([0, 1]) AS x +|> ASSERT 1 / 0 > 0 +-- +ERROR: generic::out_of_range: division by zero: 1 / 0 +== + +[name=assertion_succeeded_with_one_payload] +FROM KeyValue +|> ASSERT key >= 1, value +|> ORDER BY key +-- +ARRAY>[known order: + {1, "value1"}, + {2, "value2"} +] +== + +[name=assertion_failed_with_one_payload] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x > 2, "This is a payload" +-- +ERROR: generic::out_of_range: Assert failed: This is a payload +== + +[name=assertion_succeeded_with_multiple_payloads] +FROM KeyValue +|> ASSERT key > 0, value, ERROR("This should not trigger") +|> ORDER BY key +-- +ARRAY>[known order: + {1, "value1"}, + {2, "value2"} +] +== + +[name=assertion_failed_with_multiple_payloads] +FROM KeyValue +|> ASSERT FALSE, value, "some random payload" +-- +ERROR: generic::out_of_range: Assert failed: value2 some random payload +== + +[name=assertion_failed_with_general_expressions_as_payloads] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x > 1, 1 + 1 * 2, STRUCT(1 AS a, 2 AS b).a +-- +ERROR: generic::out_of_range: Assert failed: 3 1 +== + +[name=assertion_failed_error_as_payload] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x > 2, ERROR("An additional error is triggered") +-- +ERROR: generic::out_of_range: An additional error is triggered +== + +[name=assertion_failed_payload_evaluation_errors] +FROM UNNEST([0, 1]) AS x +|> ASSERT x > 2, CAST(1 / 0 AS STRING) +-- +ERROR: generic::out_of_range: division by zero: 1 / 0 +== + +[name=assertion_failed_error_as_payload_with_additional_payloads] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x > 2, "payload1", ERROR("An additional error is triggered"), "payload2" +-- +ERROR: generic::out_of_range: An additional error is triggered +== + +[name=assertion_failed_null_payload] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT FALSE, NULL, NULL, NULL +-- +ERROR: generic::out_of_range: Assert failed: NULL NULL NULL +== + +[name=assertion_failed_null_payload_different_types] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT FALSE, CAST(NULL AS INT64), CAST(NULL AS DOUBLE), CAST(NULL AS BYTES) +-- +ERROR: generic::out_of_range: Assert failed: NULL NULL NULL +== + +[name=assertion_failed_mixed_null_and_non_null_payloads] +SELECT 1 AS x, NULL AS y, CAST(NULL AS DOUBLE) AS z +|> ASSERT false, x, y, z, 'xyz' +-- +ERROR: generic::out_of_range: Assert failed: 1 NULL NULL xyz +== + +[name=assertion_failed_null_condition] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT NULL +-- +ERROR: generic::out_of_range: Assert failed: NULL +== + +[name=assertion_failed_null_condition_with_payload] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT NULL, "some random payload" +-- +ERROR: generic::out_of_range: Assert failed: some random payload +== + +[name=empty_scan_do_not_fail_assert] +FROM UNNEST([]) AS X +|> ASSERT x > 1 +-- +ARRAY[] +== + +[name=empty_scan_do_not_fail_assert_with_payload] +FROM KeyValue +|> WHERE FALSE +|> ASSERT FALSE, "some random payload" +-- +ARRAY>[] +== + +[name=no_filter_push_down_assert_failed] +FROM UNNEST([1, 2, 3]) AS x +|> ASSERT x > 1 +|> WHERE x > 1 +-- +ERROR: generic::out_of_range: Assert failed: x > 1 +== + +[name=no_filter_push_down_assert_succeeded] +FROM UNNEST([1, 2, 3]) AS x +|> WHERE x > 1 +|> ASSERT x > 1 +|> ORDER BY x +-- +ARRAY[known order:2, 3] +== + +[name=correlated_subquery_as_assert_input] +SELECT ( + FROM UNNEST([1, 2, 3]) AS x + |> WHERE x > key + |> ASSERT x > 1 + |> AGGREGATE COUNT(*) +) +FROM KeyValue +ORDER BY key +-- +ARRAY>[known order:{2}, {1}] +== + +[name=correlated_column_in_assert_condition_assert_succeeded] +SELECT ( + FROM UNNEST([3, 4]) AS x + |> ASSERT x > key, "payload" + |> AGGREGATE COUNT(*) +) +FROM KeyValue +-- +ARRAY>[unknown order:{2}, {2}] +== + +[name=correlated_column_in_assert_assert_failed] +SELECT ( + FROM UNNEST([1, 2, 3]) AS x + |> ASSERT x >= key, "key =", key, ", x =", x + |> AGGREGATE COUNT(*) +) +FROM KeyValue +-- +ERROR: generic::out_of_range: Assert failed: key = 2 , x = 1 +== + +[name=ok_for_the_whole_assert_to_be_optimized_away] +SELECT ( + FROM UNNEST([1, 2, 3]) AS x + |> ASSERT FALSE + |> AGGREGATE COUNT(*) +) +FROM KeyValue +WHERE FALSE +-- +ARRAY>[] diff --git a/zetasql/compliance/testdata/strings.test b/zetasql/compliance/testdata/strings.test index c15a6ae7e..e11b8d7dc 100644 --- a/zetasql/compliance/testdata/strings.test +++ b/zetasql/compliance/testdata/strings.test @@ -1355,3 +1355,128 @@ ARRAY>>[{ARRAY(NULL)}] SELECT JSON_EXTRACT_STRING_ARRAY('{"a" : [{"b" : [{"c": [1,2],"d": [3,4] } ]}, {"b" : [{"c" : [5,6],"d" : [7,8]},{"c" : [9,10],"d" : [11,12]}]}]}', "$.a[0].b[1].c") -- ARRAY>>[{ARRAY(NULL)}] +== +[name=split_substr_with_null_arguments] +SELECT split_substr(null, ".", 1) AS col1, +split_substr("foo", null, 1) AS col2, +split_substr("foo", ".", null) AS col3, +split_substr("foo", ".", 1, null) AS col4; +-- +ARRAY>[ + {NULL, NULL, NULL, NULL} +] +== +[name=split_substr_with_no_matches] +SELECT split_substr("www.abc.com", "#", 1) AS col1, +split_substr("www.abc.com", "#", 1, 2) AS col2, +split_substr("www.abc.com", "#", 2, 2) AS col3, +split_substr("www.abc.com", "#", -1, 2) AS col4, +split_substr("www.abc.com", "#", -2, 2) AS col5 +-- +ARRAY>[ + {"www.abc.com", "www.abc.com", "", "www.abc.com", "www.abc.com"} +] +== +[name=split_substr] +SELECT split_substr("www.abc.com", ".", 1) AS col1, +split_substr("www.abc.com", ".", 1, 2) AS col2, +split_substr("www.abc.com", ".", 2, 2) AS col3, +split_substr("www.abc.com", ".", 3, 2) AS col4, +split_substr("www.abc.com", ".", 4, 1) AS col5, +split_substr("www.abc.com", ".", 5, 1) AS col6 +-- +ARRAY>[ + {"www.abc.com", "www.abc", "abc.com", "com", "", ""} +] +== +[name=split_substr_with_zero_position] +SELECT split_substr("www.abc.com", ".", 0) AS col1, +split_substr("www.abc.com", ".", 0, 1) AS col2, +split_substr("www.abc.com", ".", 0, 2) AS col3, +split_substr("www.abc.com", ".", 0, 3) AS col4 +-- +ARRAY>[ + {"www.abc.com", "www", "www.abc", "www.abc.com"} +] +== +[name=split_substr_with_negative_position] +SELECT split_substr("www.abc.com", ".", 1) AS col1, +split_substr("www.abc.com", ".", -1, 2) AS col2, +split_substr("www.abc.com", ".", -2, 2) AS col3, +split_substr("www.abc.com", ".", -3, 2) AS col4, +split_substr("www.abc.com", ".", -4, 1) AS col5, +split_substr("www.abc.com", ".", -5, 1) AS col6, +-- +ARRAY>[ + {"www.abc.com", "com", "abc.com", "www.abc", "www", "www"} +] +== +[name=split_substr_with_delimiter_as_prefix] +SELECT split_substr("##tagA##tagB", "##", 1, 2) AS col1, +split_substr("##tagA##tagB", "##", 1, 1) AS col2, +split_substr("##tagA##tagB", "##", 2, 1) AS col3, +split_substr("##tagA##tagB", "##", 3, 1) AS col4, +split_substr("##tagA##tagB", "##", -1, 1) AS col5, +split_substr("##tagA##tagB", "##", -2, 1) AS col6, +split_substr("##tagA##tagB", "##", -3, 1) AS col7 +-- +ARRAY>[{"##tagA", "", "tagA", "tagB", "tagB", "tagA", ""}] +== +[name=split_substr_with_delimiter_as_suffix] +SELECT split_substr("tagA##tagB##", "##", 1, 2) AS col1, +split_substr("tagA##tagB##", "##", 1, 1) AS col2, +split_substr("tagA##tagB##", "##", 2, 1) AS col3, +split_substr("tagA##tagB##", "##", 3, 1) AS col4, +split_substr("tagA##tagB##", "##", -1, 1) AS col5, +split_substr("tagA##tagB##", "##", -2, 1) AS col6, +split_substr("tagA##tagB##", "##", -3, 1) AS col7 +-- +ARRAY>[ + {"tagA##tagB", "tagA", "tagB", "", "", "tagB", "tagA"} +] +== +[name=split_substr_with_overlapping_matches] +SELECT split_substr("###tagA###tagB###tagC###", "##", 1, 2) AS col1, +split_substr("###tagA###tagB###tagC###", "##", 1, 1) AS col2, +split_substr("###tagA###tagB###tagC###", "##", 2, 1) AS col3, +split_substr("###tagA###tagB###tagC###", "##", 3, 1) AS col4, +split_substr("###tagA###tagB###tagC###", "##", -1, 1) AS col5, +split_substr("###tagA###tagB###tagC###", "##", -2, 1) AS col6, +split_substr("###tagA###tagB###tagC###", "##", -3, 1) AS col7 +-- +ARRAY>[ + {"###tagA", "", "#tagA", "#tagB", "#", "#tagC", "#tagB"} +] +== +[name=split_substr_with_overlapping_matches_with_negative_position] +SELECT split_substr("###tagA###tagB###tagC###", "##", -2, 3) +-- +ARRAY>[{"#tagC###"}] diff --git a/zetasql/examples/tpch/README.md b/zetasql/examples/tpch/README.md new file mode 100644 index 000000000..e724ed94f --- /dev/null +++ b/zetasql/examples/tpch/README.md @@ -0,0 +1,81 @@ +This directory contains examples using the TPC-H schema and dataset. + +The `catalog/` directory implements a `zetasql::Catalog` containing TPC-H +tables, including a generated 1MB dataset. + +These tables can be queried using the `execute_query` tool (in +`tools/execute_query`) by using `--catalog=tpch` or selecting the `tpch` +catalog in the web UI (running with `--web`). + +There are examples in these files: + +* [`describe.sql`](describe.sql): `DESCRIBE` statements to show the TPC-H + schemas. + +* [`describe.txt`](describe.txt): Output of those `DESCRIBE`s, showing the + table schemas. + +* `queries/*.sql`: The standard TPC-H benchmark queries. A couple are slightly + modified to produce more output and to run faster on the reference + implementation. + +* [`all_queries.sql`](all_queries.sql): `queries/*.sql` in one file. + +* `pipe_queries/*.sql`: The TPCH-H queries rewritten using pipe syntax. + +* [`all_pipe_queries.sql`](all_pipe_queries.sql): `pipe_queries/*.sql` in one + file. + +The pipe syntax queries were converted by hand. + +* They still do joins mostly using comma joins with conditions in the `WHERE` + clause like the original queries. +* Query 13 is the only that used `JOIN` syntax (because it has an outer join). +* TPC-H tables can't use `JOIN USING` because the corresponding column names are + different in every table - not a recommended style. + +Some good examples of queries simplified with pipe syntax: + +* [`1.sql`](pipe_queries/1.sql): A simple aggregate query with grouping and + ordering collapses to just an `AGGREGATE` call using `GROUP AND ORDER BY` + shorthand. + +* [`4.sql`](pipe_queries/4.sql): The simple outer aggregate involved repeating + columns across `SELECT`, `GROUP BY` and `ORDERY`. In pipe syntax, all of + that is done just once with `AGGREGATE`, using `GROUP AND ORDER BY`. Also, + the `EXISTS` subquery is written without a placeholder `SELECT` list. + +* [`5.sql`](pipe_queries/5.sql): Uses the `DESC` ordering shorthand in + `AGGREGATE` instead of repeating columns in `ORDER BY`. + +* [`7.sql`](pipe_queries/7.sql): Removes a subquery in favor of linear flow, + with `SELECT` to compute expressions before the `AGGREGATE`. The final + `SELECT` is unnecessary since it just selects the grouping and aggregate + columns with `AGGREGATE` already produces. + +* [`8.sql`](pipe_queries/8.sql): The subquery is unnecessary. Linear pipe flow + works without it. Selecting `n2.n_name` and aliasing it is unnecessary + since the original columns (and their table aliases) are still present after + using `EXTEND` to compute expressions. + +* [`9.sql`](pipe_queries/9.sql): Removes unnecessary subquery and internal + `SELECT` and aliasing of `n_name` column by using `EXTEND` to compute + expressions. + +* [`11.sql`](pipe_queries/11.sql): `HAVING` is replaced by just a `WHERE` + after the aggregate. It can use the `value` column rather than repeating the + aggregate expression. + +* [`13.sql`](pipe_queries/13.sql): This is much shorter because it can apply + `AGGREGATE` twice for two-level aggregation without needing a subquery. This + uses pipe `JOIN`. Using `JOIN` inside `FROM` would also work. + +* [`15.sql`](pipe_queries/15.sql): Has a `WITH` clause, combined with pipe + syntax queries. + +* [`21.sql`](pipe_queries/21.sql): `EXISTS` subqueries omit the placeholder + `SELECT`s. + +* [`22.sql`](pipe_queries/22.sql): A subquery isn't needed to compute an + expression column so it can be referenced multiple times. It can just be + computed directly in `GROUP BY` and given an alias. diff --git a/zetasql/examples/tpch/all_pipe_queries.sql b/zetasql/examples/tpch/all_pipe_queries.sql new file mode 100644 index 000000000..f00aea48f --- /dev/null +++ b/zetasql/examples/tpch/all_pipe_queries.sql @@ -0,0 +1,580 @@ +# TPCH 1 +# A simple aggregate query with grouping and ordering collapses +# to just an AGGREGATE call using GROUP AND ORDER BY shorthand. +FROM lineitem +|> WHERE l_shipdate <= date_sub(date '1998-12-01', INTERVAL 74 day) +|> AGGREGATE + sum(l_quantity) AS sum_qty, + sum(l_extendedprice) AS sum_base_price, + sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + avg(l_quantity) AS avg_qty, + avg(l_extendedprice) AS avg_price, + avg(l_discount) AS avg_disc, + COUNT(*) AS count_order + GROUP + AND ORDER BY + l_returnflag, + l_linestatus; + +# TPCH 2 +FROM + part, + supplier, + partsupp, + nation, + region +|> WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND p_size = 19 + AND p_type LIKE '%COPPER' + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + AND ps_supplycost = ( + FROM + partsupp, + supplier, + nation, + region + |> WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + |> AGGREGATE + min(ps_supplycost)) +|> SELECT + s_acctbal, + s_name, + n_name, + p_partkey, + p_mfgr, + s_address, + s_phone, + s_comment +|> ORDER BY + s_acctbal DESC, + n_name, + s_name, + p_partkey +|> LIMIT 100; + +# TPCH 3 +FROM + customer, + orders, + lineitem +|> WHERE + c_mktsegment = 'FURNITURE' + AND c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate < date '1995-03-29' + AND l_shipdate > date '1995-03-29' +|> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS revenue, + GROUP BY + l_orderkey, + o_orderdate, + o_shippriority +|> SELECT + l_orderkey, + revenue, + o_orderdate, + o_shippriority +|> ORDER BY + revenue DESC, + o_orderdate +|> LIMIT 10; + +# TPCH 4 +# The simple outer aggregate involved repeating columns across +# SELECT, GROUP BY and ORDERY. In pipe syntax, all of that is +# just once with AGGREGATE, using GROUP AND ORDER BY. +# Also, the EXISTS subquery is written without a placeholder SELECT list. +FROM + orders +|> WHERE + o_orderdate >= date '1997-06-01' + AND o_orderdate < date_add(date '1997-06-01', INTERVAL 3 month) + AND EXISTS( + FROM lineitem + |> WHERE + l_orderkey = o_orderkey + AND l_commitdate < l_receiptdate) +|> AGGREGATE COUNT(*) AS order_count + GROUP AND ORDER BY o_orderpriority; + +# TPCH 5 +FROM + customer, + orders, + lineitem, + supplier, + nation, + region +|> WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND l_suppkey = s_suppkey + AND c_nationkey = s_nationkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'AFRICA' + AND o_orderdate >= date '1994-01-01' + AND o_orderdate < date_add(date '1994-01-01', INTERVAL 1 year) +|> AGGREGATE sum(l_extendedprice * (1 - l_discount)) AS revenue DESC + GROUP BY n_name; + +# TPCH 6 +FROM + lineitem +|> WHERE + l_shipdate >= date '1994-01-01' + AND l_shipdate < date_add(date '1994-01-01', INTERVAL 1 year) + AND l_discount BETWEEN 0.08 - 0.01 AND 0.08 + 0.01 + AND l_quantity < 25 +|> AGGREGATE + sum(l_extendedprice * l_discount) AS revenue; + +# TPCH 7 +# Removes a subquery in favor of linear flow, with SELECT to compute +# expressions before the AGGREGATE. +# The final SELECT is unnecessary since it just selects the grouping and +# aggregate columns with AGGREGATE already produces. +FROM + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2 +|> WHERE + s_suppkey = l_suppkey + AND o_orderkey = l_orderkey + AND c_custkey = o_custkey + AND s_nationkey = n1.n_nationkey + AND c_nationkey = n2.n_nationkey + AND ( + (n1.n_name = 'SAUDI ARABIA' AND n2.n_name = 'UNITED KINGDOM') + OR (n1.n_name = 'UNITED KINGDOM' AND n2.n_name = 'SAUDI ARABIA')) + AND l_shipdate BETWEEN date '1995-01-01' AND date '1996-12-31' +|> SELECT + n1.n_name AS supp_nation, + n2.n_name AS cust_nation, + EXTRACT(year FROM l_shipdate) AS l_year, + l_extendedprice * (1 - l_discount) AS volume +|> AGGREGATE + sum(volume) AS revenue + GROUP + AND ORDER BY + supp_nation, + cust_nation, + l_year; + +# TPCH 8 +# The subquery is unnecessary. Linear pipe flow works without it. +# Selecting n2.n_name and aliasing it is unnecessary since the original +# columns (and their table aliases) are still present after using EXTEND +# to compute expressions. +FROM + part, + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2, + region +|> WHERE + p_partkey = l_partkey + AND s_suppkey = l_suppkey + AND l_orderkey = o_orderkey + AND o_custkey = c_custkey + AND c_nationkey = n1.n_nationkey + AND n1.n_regionkey = r_regionkey + AND r_name = 'AMERICA' + AND s_nationkey = n2.n_nationkey + AND o_orderdate BETWEEN date '1993-01-01' AND date '1997-12-31' + AND p_type = 'STANDARD POLISHED TIN' +|> EXTEND + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) AS volume +|> AGGREGATE + sum(CASE WHEN n2.n_name = 'PERU' THEN volume ELSE 0 END) / sum(volume) AS mkt_share + GROUP BY + o_year ASC; + +# TPCH 9 +# Removes unnecessary subquery and internal SELECT and aliasing of +# n_name column by using EXTEND to compute expressions. +FROM + part, + supplier, + lineitem, + partsupp, + orders, + nation +|> WHERE + s_suppkey = l_suppkey + AND ps_suppkey = l_suppkey + AND ps_partkey = l_partkey + AND p_partkey = l_partkey + AND o_orderkey = l_orderkey + AND s_nationkey = n_nationkey + AND p_name LIKE '%tomato%' +|> EXTEND + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount +|> AGGREGATE + sum(amount) AS sum_profit + GROUP BY + n_name AS nation ASC, + o_year DESC; + +# TPCH 10 +# This is written with SELECT after ORDER BY and LIMIT. +# The other order would work too since SELECT preserves order. +FROM + customer, + orders, + lineitem, + nation +|> WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate >= date '1994-02-01' + AND o_orderdate < date_add(date '1994-02-01', INTERVAL 3 month) + AND l_returnflag = 'R' + AND c_nationkey = n_nationkey +|> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS revenue, + GROUP BY + c_custkey, + c_name, + c_acctbal, + c_phone, + n_name, + c_address, + c_comment +|> ORDER BY revenue DESC +|> LIMIT 20 +|> SELECT + c_custkey, + c_name, + revenue, + c_acctbal, + n_name, + c_address, + c_phone, + c_comment; + +# TPCH 11 +# HAVING is replaced by just a WHERE after the aggregate. +# It can use the `value` column rather than repeating the aggregate expression. +FROM + partsupp, + supplier, + nation +|> WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +|> AGGREGATE sum(ps_supplycost * ps_availqty) AS value + GROUP BY ps_partkey +|> WHERE + value > ( + FROM + partsupp, + supplier, + nation + |> WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' + |> AGGREGATE sum(ps_supplycost * ps_availqty) * 0.0001000000) +|> ORDER BY value DESC; + +# TPCH 12 +FROM + orders, + lineitem +|> WHERE + o_orderkey = l_orderkey + AND l_shipmode IN ('MAIL', 'AIR') + AND l_commitdate < l_receiptdate + AND l_shipdate < l_commitdate + AND l_receiptdate >= date '1997-01-01' + AND l_receiptdate < date_add(date '1997-01-01', INTERVAL 1 year) +|> AGGREGATE + sum( + CASE + WHEN + o_orderpriority = '1-URGENT' + OR o_orderpriority = '2-HIGH' + THEN 1 + ELSE 0 + END) AS high_line_count, + sum( + CASE + WHEN + o_orderpriority <> '1-URGENT' + AND o_orderpriority <> '2-HIGH' + THEN 1 + ELSE 0 + END) AS low_line_count + GROUP AND ORDER BY l_shipmode; + +# TPCH 13 +# This is much shorter because it can apply AGGREGATE twice for +# two-level aggregation without needing a subquery. +# This uses pipe JOIN. Using JOIN inside FROM would also work. +FROM customer +|> LEFT OUTER JOIN orders ON c_custkey = o_custkey AND o_comment NOT LIKE '%unusual%packages%' +|> AGGREGATE COUNT(o_orderkey) c_count + GROUP BY c_custkey +|> AGGREGATE COUNT(*) AS custdist + GROUP BY c_count +|> ORDER BY + custdist DESC, + c_count DESC; + +# TPCH 14 +FROM + lineitem, + part +|> WHERE + l_partkey = p_partkey + AND l_shipdate >= date '1994-03-01' + AND l_shipdate < date_add(date '1994-03-01', INTERVAL 1 month) +|> AGGREGATE + 100.00 + * sum( + CASE + WHEN p_type LIKE 'PROMO%' + THEN l_extendedprice * (1 - l_discount) + ELSE 0 + END) + / sum(l_extendedprice * (1 - l_discount)) AS promo_revenue; + +# TPCH 15 +# This uses a WITH clause, with the subquery in pipe syntax. +# It aliases the grouping column like the original query, although +# this is unnecessary. +WITH + revenue AS ( + FROM lineitem + |> WHERE + l_shipdate >= date '1997-05-01' + AND l_shipdate < date_add(date '1997-05-01', INTERVAL 3 month) + |> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS total_revenue + GROUP BY l_suppkey AS supplier_no + ) +FROM + supplier, + revenue +|> WHERE + s_suppkey = supplier_no + AND total_revenue = ( + SELECT max(total_revenue) + FROM revenue + ) +|> SELECT + s_suppkey, + s_name, + s_address, + s_phone, + total_revenue +|> ORDER BY s_suppkey; + +# TPCH 16 +FROM + partsupp, + part +|> WHERE + p_partkey = ps_partkey + AND p_brand <> 'Brand#13' + AND p_type NOT LIKE 'LARGE BURNISHED%' + AND p_size IN (39, 47, 37, 5, 20, 11, 25, 27) + AND ps_suppkey NOT IN ( + SELECT s_suppkey + FROM supplier + WHERE s_comment LIKE '%Customer%Complaints%' + ) +|> AGGREGATE + COUNT(DISTINCT ps_suppkey) AS supplier_cnt + GROUP BY + p_brand, + p_type, + p_size +|> ORDER BY + supplier_cnt DESC, + p_brand, + p_type, + p_size; + +# TPCH 17 +FROM + lineitem, + part +|> WHERE + p_partkey = l_partkey + AND p_brand = 'Brand#33' + AND p_container = 'LG DRUM' + AND l_quantity < ( + FROM lineitem + |> WHERE l_partkey = p_partkey + |> AGGREGATE 0.2 * avg(l_quantity)) +|> AGGREGATE sum(l_extendedprice) / 7.0 AS avg_yearly; + +# TPCH 18 +FROM + customer, + orders, + lineitem, + ( + FROM lineitem + |> AGGREGATE sum(l_quantity) AS sum_quantity + GROUP BY l_orderkey AS selected_l_orderkey + |> WHERE sum_quantity > 230 + ) +|> WHERE + o_orderkey = selected_l_orderkey + AND c_custkey = o_custkey + AND o_orderkey = l_orderkey +|> AGGREGATE sum(l_quantity) + GROUP BY + c_name, + c_custkey, + o_orderkey, + o_orderdate, + o_totalprice +|> ORDER BY + o_totalprice DESC, + o_orderdate +|> LIMIT 100; + +# TPCH 19 +FROM + lineitem, + part +|> WHERE + # Added this because optimizer is needed to pull this out of the OR. + p_partkey = l_partkey + AND ( + ( + p_partkey = l_partkey + AND p_brand = 'Brand#53' + # and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') + AND l_quantity >= 5 + AND l_quantity <= 5 + 10 + AND p_size BETWEEN 1 AND 5 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#41' + # and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') + AND l_quantity >= 15 + AND l_quantity <= 15 + 10 + AND p_size BETWEEN 1 AND 10 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#21' + # and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') + AND l_quantity >= 29 + AND l_quantity <= 29 + 10 + AND p_size BETWEEN 1 AND 15 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON')) +|> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS revenue; + +# TPCH 20 +# All layers of expression subqueries are rewritten to pipe syntax here, +# although this is not required. +FROM + supplier, + nation +|> WHERE + s_suppkey IN ( + FROM + partsupp, + part + |> WHERE p_name LIKE 'tan%' + |> WHERE + ps_partkey = p_partkey + AND ps_availqty > ( + FROM lineitem + |> WHERE + l_partkey = ps_partkey + AND l_suppkey = ps_suppkey + AND l_shipdate >= date '1996-01-01' + AND l_shipdate < date_add(date '1996-01-01', INTERVAL 1 year) + |> AGGREGATE 0.5 * sum(l_quantity)) + |> SELECT ps_suppkey) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +|> SELECT s_name, s_address +|> ORDER BY s_name; + +# TPCH 21 +# EXISTS subqueries omit the placeholder SELECTs. +FROM + supplier, + lineitem l1, + orders, + nation +|> WHERE + s_suppkey = l1.l_suppkey + AND o_orderkey = l1.l_orderkey + AND o_orderstatus = 'F' + AND l1.l_receiptdate > l1.l_commitdate + AND EXISTS( + FROM lineitem l2 + |> WHERE + l2.l_orderkey = l1.l_orderkey + AND l2.l_suppkey <> l1.l_suppkey) + AND NOT EXISTS( + FROM lineitem l3 + |> WHERE + l3.l_orderkey = l1.l_orderkey + AND l3.l_suppkey <> l1.l_suppkey + AND l3.l_receiptdate > l3.l_commitdate) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +|> AGGREGATE COUNT(*) AS numwait + GROUP BY s_name +|> ORDER BY + numwait DESC, + s_name +|> LIMIT 100; + +# TPCH 22 +# A subquery isn't needed to compute an expression column so it +# can be referenced multiple times. +# It can just be computed directly in GROUP BY and given an alias. +FROM customer +|> WHERE + substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + AND c_acctbal > ( + SELECT avg(c_acctbal) + FROM customer + WHERE + c_acctbal > 0.00 + AND substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + ) + AND NOT EXISTS( + FROM orders + |> WHERE o_custkey = c_custkey + ) +|> AGGREGATE + COUNT(*) AS numcust, + sum(c_acctbal) AS totacctbal + GROUP AND ORDER BY substr(c_phone, 1, 2) AS cntrycode; + diff --git a/zetasql/examples/tpch/all_queries.sql b/zetasql/examples/tpch/all_queries.sql new file mode 100644 index 000000000..f0ff58696 --- /dev/null +++ b/zetasql/examples/tpch/all_queries.sql @@ -0,0 +1,653 @@ +# TPCH 1 +SELECT + l_returnflag, + l_linestatus, + sum(l_quantity) AS sum_qty, + sum(l_extendedprice) AS sum_base_price, + sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + avg(l_quantity) AS avg_qty, + avg(l_extendedprice) AS avg_price, + avg(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM + lineitem +WHERE + l_shipdate <= date_sub(date '1998-12-01', INTERVAL 74 day) +GROUP BY + l_returnflag, + l_linestatus +ORDER BY + l_returnflag, + l_linestatus; + +# TPCH 2 +SELECT + s_acctbal, + s_name, + n_name, + p_partkey, + p_mfgr, + s_address, + s_phone, + s_comment +FROM + part, + supplier, + partsupp, + nation, + region +WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND p_size = 19 + AND p_type LIKE '%COPPER' + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + AND ps_supplycost = ( + SELECT + min(ps_supplycost) + FROM + partsupp, + supplier, + nation, + region + WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + ) +ORDER BY + s_acctbal DESC, + n_name, + s_name, + p_partkey +LIMIT 100; + +# TPCH 3 +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) AS revenue, + o_orderdate, + o_shippriority +FROM + customer, + orders, + lineitem +WHERE + c_mktsegment = 'FURNITURE' + AND c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate < date '1995-03-29' + AND l_shipdate > date '1995-03-29' +GROUP BY + l_orderkey, + o_orderdate, + o_shippriority +ORDER BY + revenue DESC, + o_orderdate +LIMIT 10; + +# TPCH 4 +SELECT + o_orderpriority, + COUNT(*) AS order_count +FROM + orders +WHERE + o_orderdate >= date '1997-06-01' + AND o_orderdate < date_add(date '1997-06-01', INTERVAL 3 month) + AND EXISTS( + SELECT + * + FROM + lineitem + WHERE + l_orderkey = o_orderkey + AND l_commitdate < l_receiptdate + ) +GROUP BY + o_orderpriority +ORDER BY + o_orderpriority; + +# TPCH 5 +SELECT + n_name, + sum(l_extendedprice * (1 - l_discount)) AS revenue +FROM + customer, + orders, + lineitem, + supplier, + nation, + region +WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND l_suppkey = s_suppkey + AND c_nationkey = s_nationkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'AFRICA' + AND o_orderdate >= date '1994-01-01' + AND o_orderdate < date_add(date '1994-01-01', INTERVAL 1 year) +GROUP BY + n_name +ORDER BY + revenue DESC; + +# TPCH 6 +SELECT + sum(l_extendedprice * l_discount) AS revenue +FROM + lineitem +WHERE + l_shipdate >= date '1994-01-01' + AND l_shipdate < date_add(date '1994-01-01', INTERVAL 1 year) + AND l_discount BETWEEN 0.08 - 0.01 AND 0.08 + 0.01 + AND l_quantity < 25; + +# TPCH 7 +SELECT + supp_nation, + cust_nation, + l_year, + sum(volume) AS revenue +FROM + ( + SELECT + n1.n_name AS supp_nation, + n2.n_name AS cust_nation, + EXTRACT(year FROM l_shipdate) AS l_year, + l_extendedprice * (1 - l_discount) AS volume + FROM + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2 + WHERE + s_suppkey = l_suppkey + AND o_orderkey = l_orderkey + AND c_custkey = o_custkey + AND s_nationkey = n1.n_nationkey + AND c_nationkey = n2.n_nationkey + AND ( + (n1.n_name = 'SAUDI ARABIA' AND n2.n_name = 'UNITED KINGDOM') + OR (n1.n_name = 'UNITED KINGDOM' AND n2.n_name = 'SAUDI ARABIA')) + AND l_shipdate BETWEEN date '1995-01-01' AND date '1996-12-31' + ) AS shipping +GROUP BY + supp_nation, + cust_nation, + l_year +ORDER BY + supp_nation, + cust_nation, + l_year; + +# TPCH 8 +SELECT + o_year, + sum(CASE WHEN nation = 'PERU' THEN volume ELSE 0 END) / sum(volume) AS mkt_share +FROM + ( + SELECT + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) AS volume, + n2.n_name AS nation + FROM + part, + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2, + region + WHERE + p_partkey = l_partkey + AND s_suppkey = l_suppkey + AND l_orderkey = o_orderkey + AND o_custkey = c_custkey + AND c_nationkey = n1.n_nationkey + AND n1.n_regionkey = r_regionkey + AND r_name = 'AMERICA' + AND s_nationkey = n2.n_nationkey + AND o_orderdate BETWEEN date '1993-01-01' AND date '1997-12-31' + AND p_type = 'STANDARD POLISHED TIN' + ) AS all_nations +GROUP BY + o_year +ORDER BY + o_year; + +# TPCH 9 +SELECT + nation, + o_year, + sum(amount) AS sum_profit +FROM + ( + SELECT + n_name AS nation, + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount + FROM + part, + supplier, + lineitem, + partsupp, + orders, + nation + WHERE + s_suppkey = l_suppkey + AND ps_suppkey = l_suppkey + AND ps_partkey = l_partkey + AND p_partkey = l_partkey + AND o_orderkey = l_orderkey + AND s_nationkey = n_nationkey + AND p_name LIKE '%tomato%' + ) AS profit +GROUP BY + nation, + o_year +ORDER BY + nation, + o_year DESC; + +# TPCH 10 +SELECT + c_custkey, + c_name, + sum(l_extendedprice * (1 - l_discount)) AS revenue, + c_acctbal, + n_name, + c_address, + c_phone, + c_comment +FROM + customer, + orders, + lineitem, + nation +WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate >= date '1994-02-01' + AND o_orderdate < date_add(date '1994-02-01', INTERVAL 3 month) + AND l_returnflag = 'R' + AND c_nationkey = n_nationkey +GROUP BY + c_custkey, + c_name, + c_acctbal, + c_phone, + n_name, + c_address, + c_comment +ORDER BY + revenue DESC +LIMIT 20; + +# TPCH 11 +SELECT + ps_partkey, + sum(ps_supplycost * ps_availqty) AS value +FROM + partsupp, + supplier, + nation +WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +GROUP BY ps_partkey +HAVING + sum(ps_supplycost * ps_availqty) + > ( + SELECT sum(ps_supplycost * ps_availqty) * 0.0001000000 + FROM + partsupp, + supplier, + nation + WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' + ) +ORDER BY value DESC; + +# TPCH 12 +SELECT + l_shipmode, + sum( + CASE + WHEN + o_orderpriority = '1-URGENT' + OR o_orderpriority = '2-HIGH' + THEN 1 + ELSE 0 + END) AS high_line_count, + sum( + CASE + WHEN + o_orderpriority <> '1-URGENT' + AND o_orderpriority <> '2-HIGH' + THEN 1 + ELSE 0 + END) AS low_line_count +FROM + orders, + lineitem +WHERE + o_orderkey = l_orderkey + AND l_shipmode IN ('MAIL', 'AIR') + AND l_commitdate < l_receiptdate + AND l_shipdate < l_commitdate + AND l_receiptdate >= date '1997-01-01' + AND l_receiptdate < date_add(date '1997-01-01', INTERVAL 1 year) +GROUP BY + l_shipmode +ORDER BY + l_shipmode; + +# TPCH 13 +SELECT + c_count, + COUNT(*) AS custdist +FROM + ( + SELECT + c_custkey, + COUNT(o_orderkey) c_count + FROM + customer + LEFT OUTER JOIN orders + ON + c_custkey = o_custkey + AND o_comment NOT LIKE '%unusual%packages%' + GROUP BY + c_custkey + ) AS c_orders +GROUP BY + c_count +ORDER BY + custdist DESC, + c_count DESC; + +# TPCH 14 +SELECT + 100.00 + * sum( + CASE + WHEN p_type LIKE 'PROMO%' + THEN l_extendedprice * (1 - l_discount) + ELSE 0 + END) + / sum(l_extendedprice * (1 - l_discount)) AS promo_revenue +FROM + lineitem, + part +WHERE + l_partkey = p_partkey + AND l_shipdate >= date '1994-03-01' + AND l_shipdate < date_add(date '1994-03-01', INTERVAL 1 month); + +# TPCH 15 +WITH + revenue AS ( + SELECT + l_suppkey AS supplier_no, + sum(l_extendedprice * (1 - l_discount)) AS total_revenue + FROM lineitem + WHERE + l_shipdate >= date '1997-05-01' + AND l_shipdate < date_add(date '1997-05-01', INTERVAL 3 month) + GROUP BY l_suppkey + ) +SELECT + s_suppkey, + s_name, + s_address, + s_phone, + total_revenue +FROM + supplier, + revenue +WHERE + s_suppkey = supplier_no + AND total_revenue = ( + SELECT max(total_revenue) + FROM revenue + ) +ORDER BY s_suppkey; + +# TPCH 16 +SELECT + p_brand, + p_type, + p_size, + COUNT(DISTINCT ps_suppkey) AS supplier_cnt +FROM + partsupp, + part +WHERE + p_partkey = ps_partkey + AND p_brand <> 'Brand#13' + AND p_type NOT LIKE 'LARGE BURNISHED%' + AND p_size IN (39, 47, 37, 5, 20, 11, 25, 27) + AND ps_suppkey NOT IN ( + SELECT s_suppkey + FROM supplier + WHERE s_comment LIKE '%Customer%Complaints%' + ) +GROUP BY + p_brand, + p_type, + p_size +ORDER BY + supplier_cnt DESC, + p_brand, + p_type, + p_size; + +# TPCH 17 +SELECT + sum(l_extendedprice) / 7.0 AS avg_yearly +FROM + lineitem, + part +WHERE + p_partkey = l_partkey + AND p_brand = 'Brand#33' + AND p_container = 'LG DRUM' + AND l_quantity < ( + SELECT + 0.2 * avg(l_quantity) + FROM + lineitem + WHERE + l_partkey = p_partkey + ); + +# TPCH 18 +SELECT + c_name, + c_custkey, + o_orderkey, + o_orderdate, + o_totalprice, + sum(l_quantity) +FROM + customer, + orders, + lineitem, + # Moved IN subquery to a table subquery and a join, since there's no + # optimizer to do it automatically. + ( + SELECT l_orderkey AS selected_l_orderkey + FROM lineitem + GROUP BY l_orderkey + HAVING sum(l_quantity) > 230 + ) +WHERE + o_orderkey = selected_l_orderkey + AND c_custkey = o_custkey + AND o_orderkey = l_orderkey +GROUP BY + c_name, + c_custkey, + o_orderkey, + o_orderdate, + o_totalprice +ORDER BY + o_totalprice DESC, + o_orderdate +LIMIT 100; + +# TPCH 19 +SELECT + sum(l_extendedprice * (1 - l_discount)) AS revenue +FROM + lineitem, + part +WHERE + # Added this because optimizer is needed to pull this out of the OR. + p_partkey = l_partkey + AND ( + ( + p_partkey = l_partkey + AND p_brand = 'Brand#53' + # and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') + AND l_quantity >= 5 + AND l_quantity <= 5 + 10 + AND p_size BETWEEN 1 AND 5 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#41' + # and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') + AND l_quantity >= 15 + AND l_quantity <= 15 + 10 + AND p_size BETWEEN 1 AND 10 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#21' + # and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') + AND l_quantity >= 29 + AND l_quantity <= 29 + 10 + AND p_size BETWEEN 1 AND 15 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON')); + +# TPCH 20 +SELECT + s_name, + s_address +FROM + supplier, + nation +WHERE + s_suppkey IN ( + SELECT ps_suppkey + FROM + partsupp, + ( + SELECT p_partkey + FROM part + WHERE p_name LIKE 'tan%' + ) AS selected_parts + WHERE + ps_partkey = p_partkey + AND ps_availqty > ( + SELECT 0.5 * sum(l_quantity) + FROM lineitem + WHERE + l_partkey = ps_partkey + AND l_suppkey = ps_suppkey + AND l_shipdate >= date '1996-01-01' + AND l_shipdate < date_add(date '1996-01-01', INTERVAL 1 year) + ) + ) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +ORDER BY + s_name; + +# TPCH 21 +SELECT + s_name, + COUNT(*) AS numwait +FROM + supplier, + lineitem l1, + orders, + nation +WHERE + s_suppkey = l1.l_suppkey + AND o_orderkey = l1.l_orderkey + AND o_orderstatus = 'F' + AND l1.l_receiptdate > l1.l_commitdate + AND EXISTS( + SELECT * + FROM lineitem l2 + WHERE + l2.l_orderkey = l1.l_orderkey + AND l2.l_suppkey <> l1.l_suppkey + ) + AND NOT EXISTS( + SELECT * + FROM lineitem l3 + WHERE + l3.l_orderkey = l1.l_orderkey + AND l3.l_suppkey <> l1.l_suppkey + AND l3.l_receiptdate > l3.l_commitdate + ) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +GROUP BY + s_name +ORDER BY + numwait DESC, + s_name +LIMIT 100; + +# TPCH 22 +SELECT + cntrycode, + COUNT(*) AS numcust, + sum(c_acctbal) AS totacctbal +FROM + ( + SELECT + substr(c_phone, 1, 2) AS cntrycode, + c_acctbal + FROM customer + WHERE + substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + AND c_acctbal > ( + SELECT avg(c_acctbal) + FROM customer + WHERE + c_acctbal > 0.00 + AND substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + ) + AND NOT EXISTS( + SELECT * + FROM orders + WHERE o_custkey = c_custkey + ) + ) AS custsale +GROUP BY cntrycode +ORDER BY cntrycode; + diff --git a/zetasql/examples/tpch/catalog/BUILD b/zetasql/examples/tpch/catalog/BUILD new file mode 100644 index 000000000..baed48292 --- /dev/null +++ b/zetasql/examples/tpch/catalog/BUILD @@ -0,0 +1,127 @@ +# +# Copyright 2019 Google LLC +# +# Licensed 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 +# +# 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. +# + +load("//zetasql/tools/execute_query/web:embed_files.bzl", "gen_string_constant_header_file") + +package( + default_visibility = ["//zetasql/base:zetasql_implementation"], +) + +gen_string_constant_header_file( + name = "customer_tbl", + src = "customer.tbl", + variable = "kTpchData_customer", +) + +gen_string_constant_header_file( + name = "lineitem_tbl", + src = "lineitem.tbl", + variable = "kTpchData_lineitem", +) + +gen_string_constant_header_file( + name = "nation_tbl", + src = "nation.tbl", + variable = "kTpchData_nation", +) + +gen_string_constant_header_file( + name = "orders_tbl", + src = "orders.tbl", + variable = "kTpchData_orders", +) + +gen_string_constant_header_file( + name = "part_tbl", + src = "part.tbl", + variable = "kTpchData_part", +) + +gen_string_constant_header_file( + name = "partsupp_tbl", + src = "partsupp.tbl", + variable = "kTpchData_partsupp", +) + +gen_string_constant_header_file( + name = "region_tbl", + src = "region.tbl", + variable = "kTpchData_region", +) + +gen_string_constant_header_file( + name = "supplier_tbl", + src = "supplier.tbl", + variable = "kTpchData_supplier", +) + +cc_library( + name = "catalog", + srcs = [ + "customer.tbl.h", + "lineitem.tbl.h", + "nation.tbl.h", + "orders.tbl.h", + "part.tbl.h", + "partsupp.tbl.h", + "region.tbl.h", + "supplier.tbl.h", + "tpch_catalog.cc", + ], + hdrs = ["tpch_catalog.h"], + deps = [ + "//zetasql/base:clock", + "//zetasql/base:map_util", + "//zetasql/base:ret_check", + "//zetasql/base:status", + "//zetasql/common:simple_evaluator_table_iterator", + "//zetasql/public:analyzer_options", + "//zetasql/public:analyzer_output", + "//zetasql/public:catalog", + "//zetasql/public:evaluator_table_iterator", + "//zetasql/public:simple_catalog", + "//zetasql/public:simple_catalog_util", + "//zetasql/public:type", + "//zetasql/public:value", + "//zetasql/public/functions:date_time_util", + "//zetasql/public/types", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/synchronization", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:span", + "@com_google_riegeli//riegeli/base:initializer", + "@com_google_riegeli//riegeli/bytes:string_reader", + "@com_google_riegeli//riegeli/csv:csv_reader", + ], +) + +cc_test( + name = "tpch_catalog_test", + srcs = ["tpch_catalog_test.cc"], + deps = [ + ":catalog", + "//zetasql/base/testing:status_matchers", + "//zetasql/base/testing:zetasql_gtest_main", + "//zetasql/public:catalog", + "//zetasql/public:evaluator_table_iterator", + "//zetasql/public:simple_catalog", + ], +) diff --git a/zetasql/examples/tpch/catalog/README b/zetasql/examples/tpch/catalog/README new file mode 100644 index 000000000..0443dcb7f --- /dev/null +++ b/zetasql/examples/tpch/catalog/README @@ -0,0 +1,5 @@ +Data (in *.tbl) generated by running + +./dbgen -s .001 + +in third_party/tpch/v_3_0_0/dbgen to make a 1MB dataset. diff --git a/zetasql/examples/tpch/catalog/customer.tbl b/zetasql/examples/tpch/catalog/customer.tbl new file mode 100644 index 000000000..5d39c801e --- /dev/null +++ b/zetasql/examples/tpch/catalog/customer.tbl @@ -0,0 +1,150 @@ +1|Customer#000000001|IVhzIApeRb ot,c,E|15|25-989-741-2988|711.56|BUILDING|to the even, regular platelets. regular, ironic epitaphs nag e| +2|Customer#000000002|XSTf4,NCwDVaWNe6tEgvwfmRchLXak|13|23-768-687-3665|121.65|AUTOMOBILE|l accounts. blithely ironic theodolites integrate boldly: caref| +3|Customer#000000003|MG9kdTD2WBHm|1|11-719-748-3364|7498.12|AUTOMOBILE| deposits eat slyly ironic, even instructions. express foxes detect slyly. blithely even accounts abov| +4|Customer#000000004|XxVSJsLAGtn|4|14-128-190-5944|2866.83|MACHINERY| requests. final, regular ideas sleep final accou| +5|Customer#000000005|KvpyuHCplrB84WgAiGV6sYpZq7Tj|3|13-750-942-6364|794.47|HOUSEHOLD|n accounts will have to unwind. foxes cajole accor| +6|Customer#000000006|sKZz0CsnMD7mp4Xd0YrBvx,LREYKUWAh yVn|20|30-114-968-4951|7638.57|AUTOMOBILE|tions. even deposits boost according to the slyly bold packages. final accounts cajole requests. furious| +7|Customer#000000007|TcGe5gaZNgVePxU5kRrvXBfkasDTea|18|28-190-982-9759|9561.95|AUTOMOBILE|ainst the ironic, express theodolites. express, even pinto beans among the exp| +8|Customer#000000008|I0B10bB0AymmC, 0PrRYBCP1yGJ8xcBPmWhl5|17|27-147-574-9335|6819.74|BUILDING|among the slyly regular theodolites kindle blithely courts. carefully even theodolites haggle slyly along the ide| +9|Customer#000000009|xKiAFTjUsCuxfeleNqefumTrjS|8|18-338-906-3675|8324.07|FURNITURE|r theodolites according to the requests wake thinly excuses: pending requests haggle furiousl| +10|Customer#000000010|6LrEaV6KR6PLVcgl2ArL Q3rqzLzcT1 v2|5|15-741-346-9870|2753.54|HOUSEHOLD|es regular deposits haggle. fur| +11|Customer#000000011|PkWS 3HlXqwTuzrKg633BEi|23|33-464-151-3439|-272.60|BUILDING|ckages. requests sleep slyly. quickly even pinto beans promise above the slyly regular pinto beans. | +12|Customer#000000012|9PWKuhzT4Zr1Q|13|23-791-276-1263|3396.49|HOUSEHOLD| to the carefully final braids. blithely regular requests nag. ironic theodolites boost quickly along| +13|Customer#000000013|nsXQu0oVjD7PM659uC3SRSp|3|13-761-547-5974|3857.34|BUILDING|ounts sleep carefully after the close frays. carefully bold notornis use ironic requests. blithely| +14|Customer#000000014|KXkletMlL2JQEA |1|11-845-129-3851|5266.30|FURNITURE|, ironic packages across the unus| +15|Customer#000000015|YtWggXoOLdwdo7b0y,BZaGUQMLJMX1Y,EC,6Dn|23|33-687-542-7601|2788.52|HOUSEHOLD| platelets. regular deposits detect asymptotes. blithely unusual packages nag slyly at the fluf| +16|Customer#000000016|cYiaeMLZSMAOQ2 d0W,|10|20-781-609-3107|4681.03|FURNITURE|kly silent courts. thinly regular theodolites sleep fluffily after | +17|Customer#000000017|izrh 6jdqtp2eqdtbkswDD8SG4SzXruMfIXyR7|2|12-970-682-3487|6.34|AUTOMOBILE|packages wake! blithely even pint| +18|Customer#000000018|3txGO AiuFux3zT0Z9NYaFRnZt|6|16-155-215-1315|5494.43|BUILDING|s sleep. carefully even instructions nag furiously alongside of t| +19|Customer#000000019|uc,3bHIx84H,wdrmLOjVsiqXCq2tr|18|28-396-526-5053|8914.71|HOUSEHOLD| nag. furiously careful packages are slyly at the accounts. furiously regular in| +20|Customer#000000020|JrPk8Pqplj4Ne|22|32-957-234-8742|7603.40|FURNITURE|g alongside of the special excuses-- fluffily enticing packages wake | +21|Customer#000000021|XYmVpr9yAHDEn|8|18-902-614-8344|1428.25|MACHINERY| quickly final accounts integrate blithely furiously u| +22|Customer#000000022|QI6p41,FNs5k7RZoCCVPUTkUdYpB|3|13-806-545-9701|591.98|MACHINERY|s nod furiously above the furiously ironic ideas. | +23|Customer#000000023|OdY W13N7Be3OC5MpgfmcYss0Wn6TKT|3|13-312-472-8245|3332.02|HOUSEHOLD|deposits. special deposits cajole slyly. fluffily special deposits about the furiously | +24|Customer#000000024|HXAFgIAyjxtdqwimt13Y3OZO 4xeLe7U8PqG|13|23-127-851-8031|9255.67|MACHINERY|into beans. fluffily final ideas haggle fluffily| +25|Customer#000000025|Hp8GyFQgGHFYSilH5tBfe|12|22-603-468-3533|7133.70|FURNITURE|y. accounts sleep ruthlessly according to the regular theodolites. unusual instructions sleep. ironic, final| +26|Customer#000000026|8ljrc5ZeMl7UciP|22|32-363-455-4837|5182.05|AUTOMOBILE|c requests use furiously ironic requests. slyly ironic dependencies us| +27|Customer#000000027|IS8GIyxpBrLpMT0u7|3|13-137-193-2709|5679.84|BUILDING| about the carefully ironic pinto beans. accoun| +28|Customer#000000028|iVyg0daQ,Tha8x2WPWA9m2529m|8|18-774-241-1462|1007.18|FURNITURE| along the regular deposits. furiously final pac| +29|Customer#000000029|sJ5adtfyAkCK63df2,vF25zyQMVYE34uh|0|10-773-203-7342|7618.27|FURNITURE|its after the carefully final platelets x-ray against | +30|Customer#000000030|nJDsELGAavU63Jl0c5NKsKfL8rIJQQkQnYL2QJY|1|11-764-165-5076|9321.01|BUILDING|lithely final requests. furiously unusual account| +31|Customer#000000031|LUACbO0viaAv6eXOAebryDB xjVst|23|33-197-837-7094|5236.89|HOUSEHOLD|s use among the blithely pending depo| +32|Customer#000000032|jD2xZzi UmId,DCtNBLXKj9q0Tlp2iQ6ZcO3J|15|25-430-914-2194|3471.53|BUILDING|cial ideas. final, furious requests across the e| +33|Customer#000000033|qFSlMuLucBmx9xnn5ib2csWUweg D|17|27-375-391-1280|-78.56|AUTOMOBILE|s. slyly regular accounts are furiously. carefully pending requests| +34|Customer#000000034|Q6G9wZ6dnczmtOx509xgE,M2KV|15|25-344-968-5422|8589.70|HOUSEHOLD|nder against the even, pending accounts. even| +35|Customer#000000035|TEjWGE4nBzJL2|17|27-566-888-7431|1228.24|HOUSEHOLD|requests. special, express requests nag slyly furiousl| +36|Customer#000000036|3TvCzjuPzpJ0,DdJ8kW5U|21|31-704-669-5769|4987.27|BUILDING|haggle. enticing, quiet platelets grow quickly bold sheaves. carefully regular acc| +37|Customer#000000037|7EV4Pwh,3SboctTWt|8|18-385-235-7162|-917.75|FURNITURE|ilent packages are carefully among the deposits. furiousl| +38|Customer#000000038|a5Ee5e9568R8RLP 2ap7|12|22-306-880-7212|6345.11|HOUSEHOLD|lar excuses. closely even asymptotes cajole blithely excuses. carefully silent pinto beans sleep carefully fin| +39|Customer#000000039|nnbRg,Pvy33dfkorYE FdeZ60|2|12-387-467-6509|6264.31|AUTOMOBILE|tions. slyly silent excuses slee| +40|Customer#000000040|gOnGWAyhSV1ofv|3|13-652-915-8939|1335.30|BUILDING|rges impress after the slyly ironic courts. foxes are. blithely | +41|Customer#000000041|IM9mzmyoxeBmvNw8lA7G3Ydska2nkZF|10|20-917-711-4011|270.95|HOUSEHOLD|ly regular accounts hang bold, silent packages. unusual foxes haggle slyly above the special, final depo| +42|Customer#000000042|ziSrvyyBke|5|15-416-330-4175|8727.01|BUILDING|ssly according to the pinto beans: carefully special requests across the even, pending accounts wake special| +43|Customer#000000043|ouSbjHk8lh5fKX3zGso3ZSIj9Aa3PoaFd|19|29-316-665-2897|9904.28|MACHINERY|ial requests: carefully pending foxes detect quickly. carefully final courts cajole quickly. carefully| +44|Customer#000000044|Oi,dOSPwDu4jo4x,,P85E0dmhZGvNtBwi|16|26-190-260-5375|7315.94|AUTOMOBILE|r requests around the unusual, bold a| +45|Customer#000000045|4v3OcpFgoOmMG,CbnF,4mdC|9|19-715-298-9917|9983.38|AUTOMOBILE|nto beans haggle slyly alongside of t| +46|Customer#000000046|eaTXWWm10L9|6|16-357-681-2007|5744.59|AUTOMOBILE|ctions. accounts sleep furiously even requests. regular, regular accounts cajole blithely around the final pa| +47|Customer#000000047|b0UgocSqEW5 gdVbhNT|2|12-427-271-9466|274.58|BUILDING|ions. express, ironic instructions sleep furiously ironic ideas. furi| +48|Customer#000000048|0UU iPhBupFvemNB|0|10-508-348-5882|3792.50|BUILDING|re fluffily pending foxes. pending, bold platelets sleep slyly. even platelets cajo| +49|Customer#000000049|cNgAeX7Fqrdf7HQN9EwjUa4nxT,68L FKAxzl|10|20-908-631-4424|4573.94|FURNITURE|nusual foxes! fluffily pending packages maintain to the regular | +50|Customer#000000050|9SzDYlkzxByyJ1QeTI o|6|16-658-112-3221|4266.13|MACHINERY|ts. furiously ironic accounts cajole furiously slyly ironic dinos.| +51|Customer#000000051|uR,wEaiTvo4|12|22-344-885-4251|855.87|FURNITURE|eposits. furiously regular requests integrate carefully packages. furious| +52|Customer#000000052|7 QOqGqqSy9jfV51BC71jcHJSD0|11|21-186-284-5998|5630.28|HOUSEHOLD|ic platelets use evenly even accounts. stealthy theodolites cajole furiou| +53|Customer#000000053|HnaxHzTfFTZs8MuCpJyTbZ47Cm4wFOOgib|15|25-168-852-5363|4113.64|HOUSEHOLD|ar accounts are. even foxes are blithely. fluffily pending deposits boost| +54|Customer#000000054|,k4vf 5vECGWFy,hosTE,|4|14-776-370-4745|868.90|AUTOMOBILE|sual, silent accounts. furiously express accounts cajole special deposits. final, final accounts use furi| +55|Customer#000000055|zIRBR4KNEl HzaiV3a i9n6elrxzDEh8r8pDom|10|20-180-440-8525|4572.11|MACHINERY|ully unusual packages wake bravely bold packages. unusual requests boost deposits! blithely ironic packages ab| +56|Customer#000000056|BJYZYJQk4yD5B|10|20-895-685-6920|6530.86|FURNITURE|. notornis wake carefully. carefully fluffy requests are furiously even accounts. slyly expre| +57|Customer#000000057|97XYbsuOPRXPWU|21|31-835-306-1650|4151.93|AUTOMOBILE|ove the carefully special packages. even, unusual deposits sleep slyly pend| +58|Customer#000000058|g9ap7Dk1Sv9fcXEWjpMYpBZIRUohi T|13|23-244-493-2508|6478.46|HOUSEHOLD|ideas. ironic ideas affix furiously express, final instructions. regular excuses use quickly e| +59|Customer#000000059|zLOCP0wh92OtBihgspOGl4|1|11-355-584-3112|3458.60|MACHINERY|ously final packages haggle blithely after the express deposits. furiou| +60|Customer#000000060|FyodhjwMChsZmUz7Jz0H|12|22-480-575-5866|2741.87|MACHINERY|latelets. blithely unusual courts boost furiously about the packages. blithely final instruct| +61|Customer#000000061|9kndve4EAJxhg3veF BfXr7AqOsT39o gtqjaYE|17|27-626-559-8599|1536.24|FURNITURE|egular packages shall have to impress along the | +62|Customer#000000062|upJK2Dnw13,|7|17-361-978-7059|595.61|MACHINERY|kly special dolphins. pinto beans are slyly. quickly regular accounts are furiously a| +63|Customer#000000063|IXRSpVWWZraKII|21|31-952-552-9584|9331.13|AUTOMOBILE|ithely even accounts detect slyly above the fluffily ir| +64|Customer#000000064|MbCeGY20kaKK3oalJD,OT|3|13-558-731-7204|-646.64|BUILDING|structions after the quietly ironic theodolites cajole be| +65|Customer#000000065|RGT yzQ0y4l0H90P783LG4U95bXQFDRXbWa1sl,X|23|33-733-623-5267|8795.16|AUTOMOBILE|y final foxes serve carefully. theodolites are carefully. pending i| +66|Customer#000000066|XbsEqXH1ETbJYYtA1A|22|32-213-373-5094|242.77|HOUSEHOLD|le slyly accounts. carefully silent packages benea| +67|Customer#000000067|rfG0cOgtr5W8 xILkwp9fpCS8|9|19-403-114-4356|8166.59|MACHINERY|indle furiously final, even theodo| +68|Customer#000000068|o8AibcCRkXvQFh8hF,7o|12|22-918-832-2411|6853.37|HOUSEHOLD| pending pinto beans impress realms. final dependencies | +69|Customer#000000069|Ltx17nO9Wwhtdbe9QZVxNgP98V7xW97uvSH1prEw|9|19-225-978-5670|1709.28|HOUSEHOLD|thely final ideas around the quickly final dependencies affix carefully quickly final theodolites. final accounts c| +70|Customer#000000070|mFowIuhnHjp2GjCiYYavkW kUwOjIaTCQ|22|32-828-107-2832|4867.52|FURNITURE|fter the special asymptotes. ideas after the unusual frets cajole quickly regular pinto be| +71|Customer#000000071|TlGalgdXWBmMV,6agLyWYDyIz9MKzcY8gl,w6t1B|7|17-710-812-5403|-611.19|HOUSEHOLD|g courts across the regular, final pinto beans are blithely pending ac| +72|Customer#000000072|putjlmskxE,zs,HqeIA9Wqu7dhgH5BVCwDwHHcf|2|12-759-144-9689|-362.86|FURNITURE|ithely final foxes sleep always quickly bold accounts. final wat| +73|Customer#000000073|8IhIxreu4Ug6tt5mog4|0|10-473-439-3214|4288.50|BUILDING|usual, unusual packages sleep busily along the furiou| +74|Customer#000000074|IkJHCA3ZThF7qL7VKcrU nRLl,kylf |4|14-199-862-7209|2764.43|MACHINERY|onic accounts. blithely slow packages would haggle carefully. qui| +75|Customer#000000075|Dh 6jZ,cwxWLKQfRKkiGrzv6pm|18|28-247-803-9025|6684.10|AUTOMOBILE| instructions cajole even, even deposits. finally bold deposits use above the even pains. slyl| +76|Customer#000000076|m3sbCvjMOHyaOofH,e UkGPtqc4|0|10-349-718-3044|5745.33|FURNITURE|pecial deposits. ironic ideas boost blithely according to the closely ironic theodolites! furiously final deposits n| +77|Customer#000000077|4tAE5KdMFGD4byHtXF92vx|17|27-269-357-4674|1738.87|BUILDING|uffily silent requests. carefully ironic asymptotes among the ironic hockey players are carefully bli| +78|Customer#000000078|HBOta,ZNqpg3U2cSL0kbrftkPwzX|9|19-960-700-9191|7136.97|FURNITURE|ests. blithely bold pinto beans h| +79|Customer#000000079|n5hH2ftkVRwW8idtD,BmM2|15|25-147-850-4166|5121.28|MACHINERY|es. packages haggle furiously. regular, special requests poach after the quickly express ideas. blithely pending re| +80|Customer#000000080|K,vtXp8qYB |0|10-267-172-7101|7383.53|FURNITURE|tect among the dependencies. bold accounts engage closely even pinto beans. ca| +81|Customer#000000081|SH6lPA7JiiNC6dNTrR|20|30-165-277-3269|2023.71|BUILDING|r packages. fluffily ironic requests cajole fluffily. ironically regular theodolit| +82|Customer#000000082|zhG3EZbap4c992Gj3bK,3Ne,Xn|18|28-159-442-5305|9468.34|AUTOMOBILE|s wake. bravely regular accounts are furiously. regula| +83|Customer#000000083|HnhTNB5xpnSF20JBH4Ycs6psVnkC3RDf|22|32-817-154-4122|6463.51|BUILDING|ccording to the quickly bold warhorses. final, regular foxes integrate carefully. bold packages nag blithely ev| +84|Customer#000000084|lpXz6Fwr9945rnbtMc8PlueilS1WmASr CB|11|21-546-818-3802|5174.71|FURNITURE|ly blithe foxes. special asymptotes haggle blithely against the furiously regular depo| +85|Customer#000000085|siRerlDwiolhYR 8FgksoezycLj|5|15-745-585-8219|3386.64|FURNITURE|ronic ideas use above the slowly pendin| +86|Customer#000000086|US6EGGHXbTTXPL9SBsxQJsuvy|0|10-677-951-2353|3306.32|HOUSEHOLD|quests. pending dugouts are carefully aroun| +87|Customer#000000087|hgGhHVSWQl 6jZ6Ev|23|33-869-884-7053|6327.54|FURNITURE|hely ironic requests integrate according to the ironic accounts. slyly regular pla| +88|Customer#000000088|wtkjBN9eyrFuENSMmMFlJ3e7jE5KXcg|16|26-516-273-2566|8031.44|AUTOMOBILE|s are quickly above the quickly ironic instructions; even requests about the carefully final deposi| +89|Customer#000000089|dtR, y9JQWUO6FoJExyp8whOU|14|24-394-451-5404|1530.76|FURNITURE|counts are slyly beyond the slyly final accounts. quickly final ideas wake. r| +90|Customer#000000090|QxCzH7VxxYUWwfL7|16|26-603-491-1238|7354.23|BUILDING|sly across the furiously even | +91|Customer#000000091|S8OMYFrpHwoNHaGBeuS6E 6zhHGZiprw1b7 q|8|18-239-400-3677|4643.14|AUTOMOBILE|onic accounts. fluffily silent pinto beans boost blithely according to the fluffily exp| +92|Customer#000000092|obP PULk2LH LqNF,K9hcbNqnLAkJVsl5xqSrY,|2|12-446-416-8471|1182.91|MACHINERY|. pinto beans hang slyly final deposits. ac| +93|Customer#000000093|EHXBr2QGdh|7|17-359-388-5266|2182.52|MACHINERY|press deposits. carefully regular platelets r| +94|Customer#000000094|IfVNIN9KtkScJ9dUjK3Pg5gY1aFeaXewwf|9|19-953-499-8833|5500.11|HOUSEHOLD|latelets across the bold, final requests sleep according to the fluffily bold accounts. unusual deposits amon| +95|Customer#000000095|EU0xvmWvOmUUn5J,2z85DQyG7QCJ9Xq7|15|25-923-255-2929|5327.38|MACHINERY|ithely. ruthlessly final requests wake slyly alongside of the furiously silent pinto beans. even the| +96|Customer#000000096|vWLOrmXhRR|8|18-422-845-1202|6323.92|AUTOMOBILE|press requests believe furiously. carefully final instructions snooze carefully. | +97|Customer#000000097|OApyejbhJG,0Iw3j rd1M|17|27-588-919-5638|2164.48|AUTOMOBILE|haggle slyly. bold, special ideas are blithely above the thinly bold theo| +98|Customer#000000098|7yiheXNSpuEAwbswDW|12|22-885-845-6889|-551.37|BUILDING|ages. furiously pending accounts are quickly carefully final foxes: busily pe| +99|Customer#000000099|szsrOiPtCHVS97Lt|15|25-515-237-9232|4088.65|HOUSEHOLD|cajole slyly about the regular theodolites! furiously bold requests nag along the pending, regular packages. somas| +100|Customer#000000100|fptUABXcmkC5Wx|20|30-749-445-4907|9889.89|FURNITURE|was furiously fluffily quiet deposits. silent, pending requests boost against | +101|Customer#000000101|sMmL2rNeHDltovSm Y|2|12-514-298-3699|7470.96|MACHINERY| sleep. pending packages detect slyly ironic pack| +102|Customer#000000102|UAtflJ06 fn9zBfKjInkQZlWtqaA|19|29-324-978-8538|8462.17|BUILDING|ously regular dependencies nag among the furiously express dinos. blithely final| +103|Customer#000000103|8KIsQX4LJ7QMsj6DrtFtXu0nUEdV,8a|9|19-216-107-2107|2757.45|BUILDING|furiously pending notornis boost slyly around the blithely ironic ideas? final, even instructions cajole fl| +104|Customer#000000104|9mcCK L7rt0SwiYtrbO88DiZS7U d7M|10|20-966-284-8065|-588.38|FURNITURE|rate carefully slyly special pla| +105|Customer#000000105|4iSJe4L SPjg7kJj98Yz3z0B|10|20-793-553-6417|9091.82|MACHINERY|l pains cajole even accounts. quietly final instructi| +106|Customer#000000106|xGCOEAUjUNG|1|11-751-989-4627|3288.42|MACHINERY|lose slyly. ironic accounts along the evenly regular theodolites wake about the special, final gifts. | +107|Customer#000000107|Zwg64UZ,q7GRqo3zm7P1tZIRshBDz|15|25-336-529-9919|2514.15|AUTOMOBILE|counts cajole slyly. regular requests wake. furiously regular deposits about the blithely final fo| +108|Customer#000000108|GPoeEvpKo1|5|15-908-619-7526|2259.38|BUILDING|refully ironic deposits sleep. regular, unusual requests wake slyly| +109|Customer#000000109|OOOkYBgCMzgMQXUmkocoLb56rfrdWp2NE2c|16|26-992-422-8153|-716.10|BUILDING|es. fluffily final dependencies sleep along the blithely even pinto beans. final deposits haggle furiously furiou| +110|Customer#000000110|mymPfgphaYXNYtk|10|20-893-536-2069|7462.99|AUTOMOBILE|nto beans cajole around the even, final deposits. quickly bold packages according to the furiously regular dept| +111|Customer#000000111|CBSbPyOWRorloj2TBvrK9qp9tHBs|22|32-582-283-7528|6505.26|MACHINERY|ly unusual instructions detect fluffily special deposits-- theodolites nag carefully during the ironic dependencies| +112|Customer#000000112|RcfgG3bO7QeCnfjqJT1|19|29-233-262-8382|2953.35|FURNITURE|rmanently unusual multipliers. blithely ruthless deposits are furiously along the| +113|Customer#000000113|eaOl5UBXIvdY57rglaIzqvfPD,MYfK|12|22-302-930-4756|2912.00|BUILDING|usly regular theodolites boost furiously doggedly pending instructio| +114|Customer#000000114|xAt 5f5AlFIU|14|24-805-212-7646|1027.46|FURNITURE|der the carefully express theodolites are after the packages. packages are. bli| +115|Customer#000000115|0WFt1IXENmUT2BgbsB0ShVKJZt0HCBCbFl0aHc|8|18-971-699-1843|7508.92|HOUSEHOLD|sits haggle above the carefully ironic theodolite| +116|Customer#000000116|yCuVxIgsZ3,qyK2rloThy3u|16|26-632-309-5792|8403.99|BUILDING|as. quickly final sauternes haggle slyly carefully even packages. brave, ironic pinto beans are above the furious| +117|Customer#000000117|uNhM,PzsRA3S,5Y Ge5Npuhi|24|34-403-631-3505|3950.83|FURNITURE|affix. instructions are furiously sl| +118|Customer#000000118|OVnFuHygK9wx3xpg8|18|28-639-943-7051|3582.37|AUTOMOBILE|uick packages alongside of the furiously final deposits haggle above the fluffily even foxes. blithely dogged dep| +119|Customer#000000119|M1ETOIecuvH8DtM0Y0nryXfW|7|17-697-919-8406|3930.35|FURNITURE|express ideas. blithely ironic foxes thrash. special acco| +120|Customer#000000120|zBNna00AEInqyO1|12|22-291-534-1571|363.75|MACHINERY| quickly. slyly ironic requests cajole blithely furiously final dependen| +121|Customer#000000121|tv nCR2YKupGN73mQudO|17|27-411-990-2959|6428.32|BUILDING|uriously stealthy ideas. carefully final courts use carefully| +122|Customer#000000122|yp5slqoNd26lAENZW3a67wSfXA6hTF|3|13-702-694-4520|7865.46|HOUSEHOLD| the special packages hinder blithely around the permanent requests. bold depos| +123|Customer#000000123|YsOnaaER8MkvK5cpf4VSlq|5|15-817-151-1168|5897.83|BUILDING|ependencies. regular, ironic requests are fluffily regu| +124|Customer#000000124|aTbyVAW5tCd,v09O|18|28-183-750-7809|1842.49|AUTOMOBILE|le fluffily even dependencies. quietly s| +125|Customer#000000125|,wSZXdVR xxIIfm9s8ITyLl3kgjT6UC07GY0Y|19|29-261-996-3120|-234.12|FURNITURE|x-ray finally after the packages? regular requests c| +126|Customer#000000126|ha4EHmbx3kg DYCsP6DFeUOmavtQlHhcfaqr|22|32-755-914-7592|1001.39|HOUSEHOLD|s about the even instructions boost carefully furiously ironic pearls. ruthless, | +127|Customer#000000127|Xyge4DX2rXKxXyye1Z47LeLVEYMLf4Bfcj|21|31-101-672-2951|9280.71|MACHINERY|ic, unusual theodolites nod silently after the final, ironic instructions: pending r| +128|Customer#000000128|AmKUMlJf2NRHcKGmKjLS|4|14-280-874-8044|-986.96|HOUSEHOLD|ing packages integrate across the slyly unusual dugouts. blithely silent ideas sublate carefully. blithely expr| +129|Customer#000000129|q7m7rbMM0BpaCdmxloCgBDRCleXsXkdD8kf|7|17-415-148-7416|9127.27|HOUSEHOLD| unusual deposits boost carefully furiously silent ideas. pending accounts cajole slyly across| +130|Customer#000000130|RKPx2OfZy0Vn 8wGWZ7F2EAvmMORl1k8iH|9|19-190-993-9281|5073.58|HOUSEHOLD|ix slowly. express packages along the furiously ironic requests integrate daringly deposits. fur| +131|Customer#000000131|jyN6lAjb1FtH10rMC,XzlWyCBrg75|11|21-840-210-3572|8595.53|HOUSEHOLD|jole special packages. furiously final dependencies about the furiously speci| +132|Customer#000000132|QM5YabAsTLp9|4|14-692-150-9717|162.57|HOUSEHOLD|uickly carefully special theodolites. carefully regular requests against the blithely unusual instructions | +133|Customer#000000133|IMCuXdpIvdkYO92kgDGuyHgojcUs88p|17|27-408-997-8430|2314.67|AUTOMOBILE|t packages. express pinto beans are blithely along the unusual, even theodolites. silent packages use fu| +134|Customer#000000134|sUiZ78QCkTQPICKpA9OBzkUp2FM|11|21-200-159-5932|4608.90|BUILDING|yly fluffy foxes boost final ideas. b| +135|Customer#000000135|oZK,oC0 fdEpqUML|19|29-399-293-6241|8732.91|FURNITURE| the slyly final accounts. deposits cajole carefully. carefully sly packag| +136|Customer#000000136|QoLsJ0v5C1IQbh,DS1|7|17-501-210-4726|-842.39|FURNITURE|ackages sleep ironic, final courts. even requests above the blithely bold requests g| +137|Customer#000000137|cdW91p92rlAEHgJafqYyxf1Q|16|26-777-409-5654|7838.30|HOUSEHOLD|carefully regular theodolites use. silent dolphins cajo| +138|Customer#000000138|5uyLAeY7HIGZqtu66Yn08f|5|15-394-860-4589|430.59|MACHINERY|ts doze on the busy ideas. regular| +139|Customer#000000139|3ElvBwudHKL02732YexGVFVt |9|19-140-352-1403|7897.78|MACHINERY|nstructions. quickly ironic ideas are carefully. bold, | +140|Customer#000000140|XRqEPiKgcETII,iOLDZp5jA|4|14-273-885-6505|9963.15|MACHINERY|ies detect slyly ironic accounts. slyly ironic theodolites hag| +141|Customer#000000141|5IW,WROVnikc3l7DwiUDGQNGsLBGOL6Dc0|1|11-936-295-6204|6706.14|FURNITURE|packages nag furiously. carefully unusual accounts snooze according to the fluffily regular pinto beans. slyly spec| +142|Customer#000000142|AnJ5lxtLjioClr2khl9pb8NLxG2,|9|19-407-425-2584|2209.81|AUTOMOBILE|. even, express theodolites upo| +143|Customer#000000143|681r22uL452zqk 8By7I9o9enQfx0|16|26-314-406-7725|2186.50|MACHINERY|across the blithely unusual requests haggle theodo| +144|Customer#000000144|VxYZ3ebhgbltnetaGjNC8qCccjYU05 fePLOno8y|1|11-717-379-4478|6417.31|MACHINERY|ges. slyly regular accounts are slyly. bold, idle reque| +145|Customer#000000145|kQjHmt2kcec cy3hfMh969u|13|23-562-444-8454|9748.93|HOUSEHOLD|ests? express, express instructions use. blithely fina| +146|Customer#000000146|GdxkdXG9u7iyI1,,y5tq4ZyrcEy|3|13-835-723-3223|3328.68|FURNITURE|ffily regular dinos are slyly unusual requests. slyly specia| +147|Customer#000000147|6VvIwbVdmcsMzuu,C84GtBWPaipGfi7DV|18|28-803-187-4335|8071.40|AUTOMOBILE|ress packages above the blithely regular packages sleep fluffily blithely ironic accounts. | +148|Customer#000000148|BhSPlEWGvIJyT9swk vCWE|11|21-562-498-6636|2135.60|HOUSEHOLD|ing to the carefully ironic requests. carefully regular dependencies about the theodolites wake furious| +149|Customer#000000149|3byTHCp2mNLPigUrrq|19|29-797-439-6760|8959.65|AUTOMOBILE|al instructions haggle against the slyly bold w| +150|Customer#000000150|zeoGShTjCwGPplOWFkLURrh41O0AZ8dwNEEN4 |18|28-328-564-7630|3849.48|MACHINERY|ole blithely among the furiously pending packages. furiously bold ideas wake fluffily ironic idea| diff --git a/zetasql/examples/tpch/catalog/lineitem.tbl b/zetasql/examples/tpch/catalog/lineitem.tbl new file mode 100644 index 000000000..58d47c625 --- /dev/null +++ b/zetasql/examples/tpch/catalog/lineitem.tbl @@ -0,0 +1,6005 @@ +1|156|4|1|17|17954.55|0.04|0.02|N|O|1996-03-13|1996-02-12|1996-03-22|DELIVER IN PERSON|TRUCK|egular courts above the| +1|68|9|2|36|34850.16|0.09|0.06|N|O|1996-04-12|1996-02-28|1996-04-20|TAKE BACK RETURN|MAIL|ly final dependencies: slyly bold | +1|64|5|3|8|7712.48|0.10|0.02|N|O|1996-01-29|1996-03-05|1996-01-31|TAKE BACK RETURN|REG AIR|riously. regular, express dep| +1|3|6|4|28|25284.00|0.09|0.06|N|O|1996-04-21|1996-03-30|1996-05-16|NONE|AIR|lites. fluffily even de| +1|25|8|5|24|22200.48|0.10|0.04|N|O|1996-03-30|1996-03-14|1996-04-01|NONE|FOB| pending foxes. slyly re| +1|16|3|6|32|29312.32|0.07|0.02|N|O|1996-01-30|1996-02-07|1996-02-03|DELIVER IN PERSON|MAIL|arefully slyly ex| +2|107|2|1|38|38269.80|0.00|0.05|N|O|1997-01-28|1997-01-14|1997-02-02|TAKE BACK RETURN|RAIL|ven requests. deposits breach a| +3|5|2|1|45|40725.00|0.06|0.00|R|F|1994-02-02|1994-01-04|1994-02-23|NONE|AIR|ongside of the furiously brave acco| +3|20|10|2|49|45080.98|0.10|0.00|R|F|1993-11-09|1993-12-20|1993-11-24|TAKE BACK RETURN|RAIL| unusual accounts. eve| +3|129|8|3|27|27786.24|0.06|0.07|A|F|1994-01-16|1993-11-22|1994-01-23|DELIVER IN PERSON|SHIP|nal foxes wake. | +3|30|5|4|2|1860.06|0.01|0.06|A|F|1993-12-04|1994-01-07|1994-01-01|NONE|TRUCK|y. fluffily pending d| +3|184|5|5|28|30357.04|0.04|0.00|R|F|1993-12-14|1994-01-10|1994-01-01|TAKE BACK RETURN|FOB|ages nag slyly pending| +3|63|8|6|26|25039.56|0.10|0.02|A|F|1993-10-29|1993-12-18|1993-11-04|TAKE BACK RETURN|RAIL|ges sleep after the caref| +4|89|10|1|30|29672.40|0.03|0.08|N|O|1996-01-10|1995-12-14|1996-01-18|DELIVER IN PERSON|REG AIR|- quickly regular packages sleep. idly| +5|109|10|1|15|15136.50|0.02|0.04|R|F|1994-10-31|1994-08-31|1994-11-20|NONE|AIR|ts wake furiously | +5|124|5|2|26|26627.12|0.07|0.08|R|F|1994-10-16|1994-09-25|1994-10-19|NONE|FOB|sts use slyly quickly special instruc| +5|38|4|3|50|46901.50|0.08|0.03|A|F|1994-08-08|1994-10-13|1994-08-26|DELIVER IN PERSON|AIR|eodolites. fluffily unusual| +6|140|6|1|37|38485.18|0.08|0.03|A|F|1992-04-27|1992-05-15|1992-05-02|TAKE BACK RETURN|TRUCK|p furiously special foxes| +7|183|4|1|12|12998.16|0.07|0.03|N|O|1996-05-07|1996-03-13|1996-06-03|TAKE BACK RETURN|FOB|ss pinto beans wake against th| +7|146|3|2|9|9415.26|0.08|0.08|N|O|1996-02-01|1996-03-02|1996-02-19|TAKE BACK RETURN|SHIP|es. instructions| +7|95|8|3|46|45774.14|0.10|0.07|N|O|1996-01-15|1996-03-27|1996-02-03|COLLECT COD|MAIL| unusual reques| +7|164|5|4|28|29796.48|0.03|0.04|N|O|1996-03-21|1996-04-08|1996-04-20|NONE|FOB|. slyly special requests haggl| +7|152|4|5|38|39981.70|0.08|0.01|N|O|1996-02-11|1996-02-24|1996-02-18|DELIVER IN PERSON|TRUCK|ns haggle carefully ironic deposits. bl| +7|80|10|6|35|34302.80|0.06|0.03|N|O|1996-01-16|1996-02-23|1996-01-22|TAKE BACK RETURN|FOB|jole. excuses wake carefully alongside of | +7|158|3|7|5|5290.75|0.04|0.02|N|O|1996-02-10|1996-03-26|1996-02-13|NONE|FOB|ithely regula| +32|83|4|1|28|27526.24|0.05|0.08|N|O|1995-10-23|1995-08-27|1995-10-26|TAKE BACK RETURN|TRUCK|sleep quickly. req| +32|198|10|2|32|35142.08|0.02|0.00|N|O|1995-08-14|1995-10-07|1995-08-27|COLLECT COD|AIR|lithely regular deposits. fluffily | +32|45|2|3|2|1890.08|0.09|0.02|N|O|1995-08-07|1995-10-07|1995-08-23|DELIVER IN PERSON|AIR| express accounts wake according to the| +32|3|8|4|4|3612.00|0.09|0.03|N|O|1995-08-04|1995-10-01|1995-09-03|NONE|REG AIR|e slyly final pac| +32|86|7|5|44|43387.52|0.05|0.06|N|O|1995-08-28|1995-08-20|1995-09-14|DELIVER IN PERSON|AIR|symptotes nag according to the ironic depo| +32|12|6|6|6|5472.06|0.04|0.03|N|O|1995-07-21|1995-09-23|1995-07-25|COLLECT COD|RAIL| gifts cajole carefully.| +33|62|7|1|31|29823.86|0.09|0.04|A|F|1993-10-29|1993-12-19|1993-11-08|COLLECT COD|TRUCK|ng to the furiously ironic package| +33|61|8|2|32|30753.92|0.02|0.05|A|F|1993-12-09|1994-01-04|1993-12-28|COLLECT COD|MAIL|gular theodolites| +33|138|4|3|5|5190.65|0.05|0.03|A|F|1993-12-09|1993-12-25|1993-12-23|TAKE BACK RETURN|AIR|. stealthily bold exc| +33|34|5|4|41|38295.23|0.09|0.00|R|F|1993-11-09|1994-01-24|1993-11-11|TAKE BACK RETURN|MAIL|unusual packages doubt caref| +34|89|10|1|13|12858.04|0.00|0.07|N|O|1998-10-23|1998-09-14|1998-11-06|NONE|REG AIR|nic accounts. deposits are alon| +34|90|1|2|22|21781.98|0.08|0.06|N|O|1998-10-09|1998-10-16|1998-10-12|NONE|FOB|thely slyly p| +34|170|7|3|6|6421.02|0.02|0.06|N|O|1998-10-30|1998-09-20|1998-11-05|NONE|FOB|ar foxes sleep | +35|1|4|1|24|21624.00|0.02|0.00|N|O|1996-02-21|1996-01-03|1996-03-18|TAKE BACK RETURN|FOB|, regular tithe| +35|162|1|2|34|36113.44|0.06|0.08|N|O|1996-01-22|1996-01-06|1996-01-27|DELIVER IN PERSON|RAIL|s are carefully against the f| +35|121|4|3|7|7147.84|0.06|0.04|N|O|1996-01-19|1995-12-22|1996-01-29|NONE|MAIL| the carefully regular | +35|86|7|4|25|24652.00|0.06|0.05|N|O|1995-11-26|1995-12-25|1995-12-21|DELIVER IN PERSON|SHIP| quickly unti| +35|120|7|5|34|34684.08|0.08|0.06|N|O|1995-11-08|1996-01-15|1995-11-26|COLLECT COD|MAIL|. silent, unusual deposits boost| +35|31|7|6|28|26068.84|0.03|0.02|N|O|1996-02-01|1995-12-24|1996-02-28|COLLECT COD|RAIL|ly alongside of | +36|120|1|1|42|42845.04|0.09|0.00|N|O|1996-02-03|1996-01-21|1996-02-23|COLLECT COD|SHIP| careful courts. special | +37|23|8|1|40|36920.80|0.09|0.03|A|F|1992-07-21|1992-08-01|1992-08-15|NONE|REG AIR|luffily regular requests. slyly final acco| +37|127|6|2|39|40057.68|0.05|0.02|A|F|1992-07-02|1992-08-18|1992-07-28|TAKE BACK RETURN|RAIL|the final requests. ca| +37|13|7|3|43|39259.43|0.05|0.08|A|F|1992-07-10|1992-07-06|1992-08-02|DELIVER IN PERSON|TRUCK|iously ste| +38|176|5|1|44|47351.48|0.04|0.02|N|O|1996-09-29|1996-11-17|1996-09-30|COLLECT COD|MAIL|s. blithely unusual theodolites am| +39|3|10|1|44|39732.00|0.09|0.06|N|O|1996-11-14|1996-12-15|1996-12-12|COLLECT COD|RAIL|eodolites. careful| +39|187|8|2|26|28266.68|0.08|0.04|N|O|1996-11-04|1996-10-20|1996-11-20|NONE|FOB|ckages across the slyly silent| +39|68|3|3|46|44530.76|0.06|0.08|N|O|1996-09-26|1996-12-19|1996-10-26|DELIVER IN PERSON|AIR|he carefully e| +39|21|6|4|32|29472.64|0.07|0.05|N|O|1996-10-02|1996-12-19|1996-10-14|COLLECT COD|MAIL|heodolites sleep silently pending foxes. ac| +39|55|10|5|43|41067.15|0.01|0.01|N|O|1996-10-17|1996-11-14|1996-10-26|COLLECT COD|MAIL|yly regular i| +39|95|7|6|40|39803.60|0.06|0.05|N|O|1996-12-08|1996-10-22|1997-01-01|COLLECT COD|AIR|quickly ironic fox| +64|86|7|1|21|20707.68|0.05|0.02|R|F|1994-09-30|1994-09-18|1994-10-26|DELIVER IN PERSON|REG AIR|ch slyly final, thin platelets.| +65|60|5|1|26|24961.56|0.03|0.03|A|F|1995-04-20|1995-04-25|1995-05-13|NONE|TRUCK|pending deposits nag even packages. ca| +65|74|3|2|22|21429.54|0.00|0.05|N|O|1995-07-17|1995-06-04|1995-07-19|COLLECT COD|FOB| ideas. special, r| +65|2|5|3|21|18942.00|0.09|0.07|N|O|1995-07-06|1995-05-14|1995-07-31|DELIVER IN PERSON|RAIL|bove the even packages. accounts nag carefu| +66|116|10|1|31|31499.41|0.00|0.08|R|F|1994-02-19|1994-03-11|1994-02-20|TAKE BACK RETURN|RAIL|ut the unusual accounts sleep at the bo| +66|174|5|2|41|44040.97|0.04|0.07|A|F|1994-02-21|1994-03-01|1994-03-18|COLLECT COD|AIR| regular de| +67|22|5|1|4|3688.08|0.09|0.04|N|O|1997-04-17|1997-01-31|1997-04-20|NONE|SHIP| cajole thinly expres| +67|21|10|2|12|11052.24|0.09|0.05|N|O|1997-01-27|1997-02-21|1997-02-22|NONE|REG AIR| even packages cajole| +67|174|4|3|5|5370.85|0.03|0.07|N|O|1997-02-20|1997-02-12|1997-02-21|DELIVER IN PERSON|TRUCK|y unusual packages thrash pinto | +67|88|9|4|44|43475.52|0.08|0.06|N|O|1997-03-18|1997-01-29|1997-04-13|DELIVER IN PERSON|RAIL|se quickly above the even, express reques| +67|41|10|5|23|21643.92|0.05|0.07|N|O|1997-04-19|1997-02-14|1997-05-06|DELIVER IN PERSON|REG AIR|ly regular deposit| +67|179|9|6|29|31295.93|0.02|0.05|N|O|1997-01-25|1997-01-27|1997-01-27|DELIVER IN PERSON|FOB|ultipliers | +68|8|1|1|3|2724.00|0.05|0.02|N|O|1998-07-04|1998-06-05|1998-07-21|NONE|RAIL|fully special instructions cajole. furious| +68|176|4|2|46|49503.82|0.02|0.05|N|O|1998-06-26|1998-06-07|1998-07-05|NONE|MAIL| requests are unusual, regular pinto | +68|35|1|3|46|43011.38|0.04|0.05|N|O|1998-08-13|1998-07-08|1998-08-29|NONE|RAIL|egular dependencies affix ironically along | +68|95|9|4|20|19901.80|0.07|0.01|N|O|1998-06-27|1998-05-23|1998-07-02|NONE|REG AIR| excuses integrate fluffily | +68|83|4|5|27|26543.16|0.03|0.06|N|O|1998-06-19|1998-06-25|1998-06-29|DELIVER IN PERSON|SHIP|ccounts. deposits use. furiously| +68|103|6|6|30|30093.00|0.05|0.06|N|O|1998-08-11|1998-07-11|1998-08-14|NONE|RAIL|oxes are slyly blithely fin| +68|140|6|7|41|42645.74|0.09|0.08|N|O|1998-06-24|1998-06-27|1998-07-06|NONE|SHIP|eposits nag special ideas. furiousl| +69|116|10|1|48|48773.28|0.01|0.07|A|F|1994-08-17|1994-08-11|1994-09-08|NONE|TRUCK|regular epitaphs. carefully even ideas hag| +69|105|10|2|32|32163.20|0.08|0.06|A|F|1994-08-24|1994-08-17|1994-08-31|NONE|REG AIR|s sleep carefully bold, | +69|138|4|3|17|17648.21|0.09|0.00|A|F|1994-07-02|1994-07-07|1994-07-03|TAKE BACK RETURN|AIR|final, pending instr| +69|38|9|4|3|2814.09|0.09|0.04|R|F|1994-06-06|1994-07-27|1994-06-15|NONE|MAIL| blithely final d| +69|93|6|5|42|41709.78|0.07|0.04|R|F|1994-07-31|1994-07-26|1994-08-28|DELIVER IN PERSON|REG AIR|tect regular, speci| +69|19|3|6|23|21137.23|0.05|0.00|A|F|1994-10-03|1994-08-06|1994-10-24|NONE|SHIP|nding accounts ca| +70|65|2|1|8|7720.48|0.03|0.08|R|F|1994-01-12|1994-02-27|1994-01-14|TAKE BACK RETURN|FOB|ggle. carefully pending dependenc| +70|197|10|2|13|14263.47|0.06|0.06|A|F|1994-03-03|1994-02-13|1994-03-26|COLLECT COD|AIR|lyly special packag| +70|180|8|3|1|1080.18|0.03|0.05|R|F|1994-01-26|1994-03-05|1994-01-28|TAKE BACK RETURN|RAIL|quickly. fluffily unusual theodolites c| +70|46|9|4|11|10406.44|0.01|0.05|A|F|1994-03-17|1994-03-17|1994-03-27|NONE|MAIL|alongside of the deposits. fur| +70|38|9|5|37|34707.11|0.09|0.04|R|F|1994-02-13|1994-03-16|1994-02-21|COLLECT COD|MAIL|n accounts are. q| +70|56|8|6|19|18164.95|0.06|0.03|A|F|1994-01-26|1994-02-17|1994-02-06|TAKE BACK RETURN|SHIP| packages wake pending accounts.| +71|62|3|1|25|24051.50|0.09|0.07|N|O|1998-04-10|1998-04-22|1998-04-11|COLLECT COD|FOB|ckly. slyly| +71|66|1|2|3|2898.18|0.09|0.07|N|O|1998-05-23|1998-04-03|1998-06-02|COLLECT COD|SHIP|y. pinto beans haggle after the| +71|35|1|3|45|42076.35|0.00|0.07|N|O|1998-02-23|1998-03-20|1998-03-24|DELIVER IN PERSON|SHIP| ironic packages believe blithely a| +71|97|9|4|33|32903.97|0.00|0.01|N|O|1998-04-12|1998-03-20|1998-04-15|NONE|FOB| serve quickly fluffily bold deposi| +71|104|7|5|39|39159.90|0.08|0.06|N|O|1998-01-29|1998-04-07|1998-02-18|DELIVER IN PERSON|RAIL|l accounts sleep across the pack| +71|196|9|6|34|37270.46|0.04|0.01|N|O|1998-03-05|1998-04-22|1998-03-30|DELIVER IN PERSON|TRUCK|s cajole. | +96|124|7|1|23|23554.76|0.10|0.06|A|F|1994-07-19|1994-06-29|1994-07-25|DELIVER IN PERSON|TRUCK|ep-- carefully reg| +96|136|7|2|30|31083.90|0.01|0.06|R|F|1994-06-03|1994-05-29|1994-06-22|DELIVER IN PERSON|TRUCK|e quickly even ideas. furiou| +97|120|4|1|13|13261.56|0.00|0.02|R|F|1993-04-01|1993-04-04|1993-04-08|NONE|TRUCK|ayers cajole against the furiously| +97|50|7|2|37|35151.85|0.02|0.06|A|F|1993-04-13|1993-03-30|1993-04-14|DELIVER IN PERSON|SHIP|ic requests boost carefully quic| +97|78|6|3|19|18583.33|0.06|0.08|R|F|1993-05-14|1993-03-05|1993-05-25|TAKE BACK RETURN|RAIL|gifts. furiously ironic packages cajole. | +98|41|2|1|28|26349.12|0.06|0.07|A|F|1994-12-24|1994-10-25|1995-01-16|COLLECT COD|REG AIR| pending, regular accounts s| +98|110|7|2|1|1010.11|0.00|0.00|A|F|1994-12-01|1994-12-12|1994-12-15|DELIVER IN PERSON|TRUCK|. unusual instructions against| +98|45|6|3|14|13230.56|0.05|0.02|A|F|1994-12-30|1994-11-22|1995-01-27|COLLECT COD|AIR| cajole furiously. blithely ironic ideas | +98|168|9|4|10|10681.60|0.03|0.03|A|F|1994-10-23|1994-11-08|1994-11-09|COLLECT COD|RAIL| carefully. quickly ironic ideas| +99|88|9|1|10|9880.80|0.02|0.01|A|F|1994-05-18|1994-06-03|1994-05-23|COLLECT COD|RAIL|kages. requ| +99|124|5|2|5|5120.60|0.02|0.07|R|F|1994-05-06|1994-05-28|1994-05-20|TAKE BACK RETURN|RAIL|ests cajole fluffily waters. blithe| +99|135|1|3|42|43475.46|0.02|0.02|A|F|1994-04-19|1994-05-18|1994-04-20|NONE|RAIL|kages are fluffily furiously ir| +99|109|2|4|36|36327.60|0.09|0.02|A|F|1994-07-04|1994-04-17|1994-07-30|DELIVER IN PERSON|AIR|slyly. slyly e| +100|63|4|1|28|26965.68|0.04|0.05|N|O|1998-05-08|1998-05-13|1998-06-07|COLLECT COD|TRUCK|sts haggle. slowl| +100|116|10|2|22|22354.42|0.00|0.07|N|O|1998-06-24|1998-04-12|1998-06-29|DELIVER IN PERSON|SHIP|nto beans alongside of the fi| +100|47|4|3|46|43563.84|0.03|0.04|N|O|1998-05-02|1998-04-10|1998-05-22|TAKE BACK RETURN|SHIP|ular accounts. even| +100|39|10|4|14|13146.42|0.06|0.03|N|O|1998-05-22|1998-05-01|1998-06-03|COLLECT COD|MAIL|y. furiously ironic ideas gr| +100|54|6|5|37|35299.85|0.05|0.00|N|O|1998-03-06|1998-04-16|1998-03-31|TAKE BACK RETURN|TRUCK|nd the quickly s| +101|119|9|1|49|49936.39|0.10|0.00|N|O|1996-06-21|1996-05-27|1996-06-29|DELIVER IN PERSON|REG AIR|ts-- final packages sleep furiousl| +101|164|9|2|36|38309.76|0.00|0.01|N|O|1996-05-19|1996-05-01|1996-06-04|DELIVER IN PERSON|AIR|tes. blithely pending dolphins x-ray f| +101|139|5|3|12|12469.56|0.06|0.02|N|O|1996-03-29|1996-04-20|1996-04-12|COLLECT COD|MAIL|. quickly regular| +102|89|10|1|37|36595.96|0.06|0.00|N|O|1997-07-24|1997-08-02|1997-08-07|TAKE BACK RETURN|SHIP|ully across the ideas. final deposit| +102|170|5|2|34|36385.78|0.03|0.08|N|O|1997-08-09|1997-07-28|1997-08-26|TAKE BACK RETURN|SHIP|eposits cajole across| +102|183|4|3|25|27079.50|0.01|0.01|N|O|1997-07-31|1997-07-24|1997-08-17|NONE|RAIL|bits. ironic accoun| +102|62|7|4|15|14430.90|0.07|0.07|N|O|1997-06-02|1997-07-13|1997-06-04|DELIVER IN PERSON|SHIP|final packages. carefully even excu| +103|195|9|1|6|6571.14|0.03|0.05|N|O|1996-10-11|1996-07-25|1996-10-28|NONE|FOB|cajole. carefully ex| +103|11|5|2|37|33707.37|0.02|0.07|N|O|1996-09-17|1996-07-27|1996-09-20|TAKE BACK RETURN|MAIL|ies. quickly ironic requests use blithely| +103|29|10|3|23|21367.46|0.01|0.04|N|O|1996-09-11|1996-09-18|1996-09-26|NONE|FOB|ironic accou| +103|30|9|4|32|29760.96|0.01|0.07|N|O|1996-07-30|1996-08-06|1996-08-04|NONE|RAIL|kages doze. special, regular deposit| +128|107|10|1|38|38269.80|0.06|0.01|A|F|1992-09-01|1992-08-27|1992-10-01|TAKE BACK RETURN|FOB| cajole careful| +129|3|6|1|46|41538.00|0.08|0.02|R|F|1993-02-15|1993-01-24|1993-03-05|COLLECT COD|TRUCK|uietly bold theodolites. fluffil| +129|186|7|2|36|39102.48|0.01|0.02|A|F|1992-11-25|1992-12-25|1992-12-09|TAKE BACK RETURN|REG AIR|packages are care| +129|40|6|3|33|31021.32|0.04|0.06|A|F|1993-01-08|1993-02-14|1993-01-29|COLLECT COD|SHIP|sts nag bravely. fluffily| +129|136|7|4|34|35228.42|0.00|0.01|R|F|1993-01-29|1993-02-14|1993-02-10|COLLECT COD|MAIL|quests. express ideas| +129|32|8|5|24|22368.72|0.06|0.00|A|F|1992-12-07|1993-01-02|1992-12-11|TAKE BACK RETURN|FOB|uests. foxes cajole slyly after the ca| +129|78|6|6|22|21517.54|0.06|0.01|R|F|1993-02-15|1993-01-31|1993-02-24|COLLECT COD|SHIP|e. fluffily regular | +129|169|6|7|1|1069.16|0.05|0.04|R|F|1993-01-26|1993-01-08|1993-02-24|DELIVER IN PERSON|FOB|e carefully blithely bold dolp| +130|129|10|1|14|14407.68|0.08|0.05|A|F|1992-08-15|1992-07-25|1992-09-13|COLLECT COD|RAIL| requests. final instruction| +130|2|5|2|48|43296.00|0.03|0.02|R|F|1992-07-01|1992-07-12|1992-07-24|NONE|AIR|lithely alongside of the regu| +130|12|3|3|18|16416.18|0.04|0.08|A|F|1992-07-04|1992-06-14|1992-07-29|DELIVER IN PERSON|MAIL| slyly ironic decoys abou| +130|116|6|4|13|13209.43|0.09|0.02|R|F|1992-06-26|1992-07-29|1992-07-05|NONE|FOB| pending dolphins sleep furious| +130|70|7|5|31|30072.17|0.06|0.05|R|F|1992-09-01|1992-07-18|1992-09-02|TAKE BACK RETURN|RAIL|thily about the ruth| +131|168|7|1|45|48067.20|0.10|0.02|R|F|1994-09-14|1994-09-02|1994-10-04|NONE|FOB|ironic, bold accounts. careful| +131|45|8|2|50|47252.00|0.02|0.04|A|F|1994-09-17|1994-08-10|1994-09-21|NONE|SHIP|ending requests. final, ironic pearls slee| +131|190|1|3|4|4360.76|0.04|0.03|A|F|1994-09-20|1994-08-30|1994-09-23|COLLECT COD|REG AIR| are carefully slyly i| +132|141|8|1|18|18740.52|0.00|0.08|R|F|1993-07-10|1993-08-05|1993-07-13|NONE|TRUCK|ges. platelets wake furio| +132|120|1|2|43|43865.16|0.01|0.08|R|F|1993-09-01|1993-08-16|1993-09-22|NONE|TRUCK|y pending theodolites| +132|115|6|3|32|32483.52|0.04|0.04|A|F|1993-07-12|1993-08-05|1993-08-05|COLLECT COD|TRUCK|d instructions hagg| +132|29|2|4|23|21367.46|0.10|0.00|A|F|1993-06-16|1993-08-27|1993-06-23|DELIVER IN PERSON|AIR|refully blithely bold acco| +133|104|7|1|27|27110.70|0.00|0.02|N|O|1997-12-21|1998-02-23|1997-12-27|TAKE BACK RETURN|MAIL|yly even gifts after the sl| +133|177|5|2|12|12926.04|0.02|0.06|N|O|1997-12-02|1998-01-15|1997-12-29|DELIVER IN PERSON|REG AIR|ts cajole fluffily quickly i| +133|118|8|3|29|29525.19|0.09|0.08|N|O|1998-02-28|1998-01-30|1998-03-09|DELIVER IN PERSON|RAIL| the carefully regular theodoli| +133|90|1|4|11|10890.99|0.06|0.01|N|O|1998-03-21|1998-01-15|1998-04-04|DELIVER IN PERSON|REG AIR|e quickly across the dolphins| +134|1|2|1|21|18921.00|0.00|0.03|A|F|1992-07-17|1992-07-08|1992-07-26|COLLECT COD|SHIP|s. quickly regular| +134|165|2|2|35|37280.60|0.06|0.07|A|F|1992-08-23|1992-06-01|1992-08-24|NONE|MAIL|ajole furiously. instructio| +134|189|10|3|26|28318.68|0.09|0.06|A|F|1992-06-20|1992-07-12|1992-07-16|NONE|RAIL| among the pending depos| +134|145|6|4|47|49121.58|0.05|0.00|A|F|1992-08-16|1992-07-06|1992-08-28|NONE|REG AIR|s! carefully unusual requests boost careful| +134|36|7|5|12|11232.36|0.05|0.02|A|F|1992-07-03|1992-06-01|1992-07-11|COLLECT COD|TRUCK|nts are quic| +134|134|10|6|12|12409.56|0.00|0.00|A|F|1992-08-08|1992-07-07|1992-08-20|TAKE BACK RETURN|FOB|lyly regular pac| +135|109|10|1|47|47427.70|0.06|0.08|N|O|1996-02-18|1996-01-01|1996-02-25|COLLECT COD|RAIL|ctions wake slyly abo| +135|199|3|2|21|23082.99|0.00|0.07|N|O|1996-02-11|1996-01-12|1996-02-13|DELIVER IN PERSON|SHIP| deposits believe. furiously regular p| +135|158|10|3|33|34918.95|0.02|0.00|N|O|1996-01-03|1995-11-21|1996-02-01|TAKE BACK RETURN|MAIL|ptotes boost slowly care| +135|68|7|4|34|32914.04|0.02|0.03|N|O|1996-01-12|1996-01-19|1996-02-05|NONE|TRUCK|counts doze against the blithely ironi| +135|137|8|5|20|20742.60|0.01|0.04|N|O|1996-01-25|1995-11-20|1996-02-09|NONE|MAIL|theodolites. quickly p| +135|115|5|6|13|13196.43|0.04|0.02|N|O|1995-11-12|1995-12-22|1995-11-17|NONE|FOB|nal ideas. final instr| +160|15|2|1|36|32940.36|0.07|0.01|N|O|1997-03-11|1997-03-11|1997-03-20|COLLECT COD|MAIL|old, ironic deposits are quickly abov| +160|87|8|2|22|21715.76|0.00|0.04|N|O|1997-02-18|1997-03-05|1997-03-05|COLLECT COD|RAIL|ncies about the request| +160|21|10|3|34|31314.68|0.01|0.05|N|O|1997-01-31|1997-03-13|1997-02-14|NONE|FOB|st sleep even gifts. dependencies along| +161|103|10|1|19|19058.90|0.01|0.01|A|F|1994-12-13|1994-11-19|1994-12-26|DELIVER IN PERSON|TRUCK|, regular sheaves sleep along| +162|190|1|1|2|2180.38|0.02|0.01|N|O|1995-09-02|1995-06-17|1995-09-08|COLLECT COD|FOB|es! final somas integrate| +163|168|3|1|43|45930.88|0.01|0.00|N|O|1997-09-19|1997-11-19|1997-10-03|COLLECT COD|REG AIR|al, bold dependencies wake. iron| +163|121|2|2|13|13274.56|0.01|0.04|N|O|1997-11-11|1997-10-18|1997-12-07|DELIVER IN PERSON|TRUCK|inal requests. even pinto beans hag| +163|37|3|3|27|25299.81|0.04|0.08|N|O|1997-12-26|1997-11-28|1998-01-05|COLLECT COD|REG AIR|ously express dependen| +163|193|5|4|5|5465.95|0.02|0.00|N|O|1997-11-17|1997-10-09|1997-12-05|DELIVER IN PERSON|TRUCK| must belie| +163|127|2|5|12|12325.44|0.10|0.00|N|O|1997-12-18|1997-10-26|1997-12-22|COLLECT COD|TRUCK|ly blithe accounts cajole | +163|191|4|6|20|21823.80|0.00|0.07|N|O|1997-09-27|1997-11-15|1997-10-07|TAKE BACK RETURN|FOB|tructions integrate b| +164|92|4|1|26|25794.34|0.09|0.04|A|F|1993-01-04|1992-11-21|1993-01-07|NONE|RAIL|s. blithely special courts are blithel| +164|19|6|2|24|22056.24|0.05|0.05|R|F|1992-12-22|1992-11-27|1993-01-06|NONE|AIR|side of the slyly unusual theodolites. f| +164|126|9|3|38|38992.56|0.03|0.06|R|F|1992-12-04|1992-11-23|1993-01-02|TAKE BACK RETURN|AIR|counts cajole fluffily regular packages. b| +164|18|2|4|32|29376.32|0.05|0.01|R|F|1992-12-21|1992-12-23|1992-12-28|COLLECT COD|RAIL|ts wake again| +164|148|1|5|43|45070.02|0.06|0.01|R|F|1992-11-26|1993-01-03|1992-12-08|COLLECT COD|RAIL|y carefully regular dep| +164|109|10|6|27|27245.70|0.10|0.04|R|F|1992-12-23|1993-01-16|1993-01-10|DELIVER IN PERSON|AIR|ayers wake carefully a| +164|4|7|7|23|20792.00|0.09|0.04|A|F|1992-11-03|1992-12-02|1992-11-12|NONE|REG AIR|ress packages haggle ideas. blithely spec| +165|34|5|1|3|2802.09|0.01|0.08|R|F|1993-03-29|1993-03-06|1993-04-12|DELIVER IN PERSON|REG AIR|riously requests. depos| +165|162|7|2|43|45672.88|0.08|0.05|R|F|1993-02-27|1993-04-19|1993-03-03|DELIVER IN PERSON|TRUCK|jole slyly according | +165|59|1|3|15|14385.75|0.00|0.05|R|F|1993-04-10|1993-03-29|1993-05-01|COLLECT COD|SHIP| bold packages mainta| +165|140|1|4|49|50966.86|0.07|0.06|A|F|1993-02-20|1993-04-02|1993-03-10|COLLECT COD|REG AIR|uses sleep slyly ruthlessly regular a| +165|156|4|5|27|28516.05|0.01|0.04|R|F|1993-04-27|1993-03-04|1993-05-13|NONE|MAIL|around the ironic, even orb| +166|65|2|1|37|35707.22|0.09|0.03|N|O|1995-11-16|1995-10-17|1995-12-13|NONE|MAIL|lar frays wake blithely a| +166|167|8|2|13|13873.08|0.09|0.05|N|O|1995-11-09|1995-11-18|1995-11-14|COLLECT COD|SHIP|fully above the blithely fina| +166|100|2|3|41|41004.10|0.07|0.03|N|O|1995-11-13|1995-11-07|1995-12-08|COLLECT COD|FOB|hily along the blithely pending fo| +166|46|3|4|8|7568.32|0.05|0.02|N|O|1995-12-30|1995-11-29|1996-01-29|DELIVER IN PERSON|RAIL|e carefully bold | +167|102|3|1|28|28058.80|0.06|0.01|R|F|1993-02-19|1993-02-16|1993-03-03|DELIVER IN PERSON|TRUCK|sly during the u| +167|172|2|2|27|28948.59|0.09|0.00|R|F|1993-05-01|1993-03-31|1993-05-31|TAKE BACK RETURN|FOB|eans affix furiously-- packages| +192|98|1|1|23|22956.07|0.00|0.00|N|O|1998-02-05|1998-02-06|1998-03-07|TAKE BACK RETURN|AIR|ly pending theodolites haggle quickly fluf| +192|162|7|2|20|21243.20|0.07|0.01|N|O|1998-03-13|1998-02-02|1998-03-31|TAKE BACK RETURN|REG AIR|tes. carefu| +192|111|8|3|15|15166.65|0.09|0.01|N|O|1998-01-30|1998-02-10|1998-02-23|TAKE BACK RETURN|TRUCK|he ironic requests haggle about| +192|197|1|4|2|2194.38|0.06|0.02|N|O|1998-03-06|1998-02-03|1998-03-24|COLLECT COD|SHIP|s. dependencies nag furiously alongside| +192|83|4|5|25|24577.00|0.02|0.03|N|O|1998-02-15|1998-01-11|1998-03-17|COLLECT COD|TRUCK|. carefully regular| +192|142|9|6|45|46896.30|0.00|0.05|N|O|1998-03-11|1998-01-09|1998-04-03|NONE|MAIL|equests. ideas sleep idea| +193|93|5|1|9|8937.81|0.06|0.06|A|F|1993-09-17|1993-10-08|1993-09-30|COLLECT COD|TRUCK|against the fluffily regular d| +193|154|6|2|15|15812.25|0.02|0.07|R|F|1993-11-22|1993-10-09|1993-12-05|TAKE BACK RETURN|SHIP|ffily. regular packages d| +193|94|6|3|23|22864.07|0.06|0.05|A|F|1993-08-21|1993-10-11|1993-09-02|DELIVER IN PERSON|TRUCK|ly even accounts wake blithely bold| +194|3|6|1|17|15351.00|0.05|0.04|R|F|1992-05-24|1992-05-22|1992-05-30|COLLECT COD|AIR| regular deposi| +194|184|5|2|1|1084.18|0.04|0.06|R|F|1992-04-30|1992-05-18|1992-05-23|NONE|REG AIR| regular theodolites. regular, iron| +194|66|1|3|13|12558.78|0.08|0.08|A|F|1992-05-07|1992-06-18|1992-05-10|NONE|AIR|about the blit| +194|146|7|4|36|37661.04|0.00|0.05|R|F|1992-05-21|1992-05-18|1992-05-27|TAKE BACK RETURN|RAIL|pecial packages wake after the slyly r| +194|57|2|5|8|7656.40|0.04|0.00|R|F|1992-07-06|1992-06-25|1992-07-11|COLLECT COD|FOB|uriously unusual excuses| +194|149|6|6|16|16786.24|0.06|0.03|A|F|1992-05-14|1992-06-14|1992-05-21|TAKE BACK RETURN|TRUCK|y regular requests. furious| +194|168|7|7|21|22431.36|0.02|0.01|R|F|1992-05-06|1992-05-20|1992-05-07|COLLECT COD|REG AIR|accounts detect quickly dogged | +195|85|6|1|6|5910.48|0.04|0.02|A|F|1994-01-09|1994-03-27|1994-01-28|COLLECT COD|REG AIR|y, even deposits haggle carefully. bli| +195|94|8|2|41|40757.69|0.05|0.07|A|F|1994-02-24|1994-02-11|1994-03-20|NONE|TRUCK|rts detect in place of t| +195|86|7|3|34|33526.72|0.08|0.08|R|F|1994-01-31|1994-02-11|1994-02-12|NONE|TRUCK| cajole furiously bold i| +195|86|7|4|41|40429.28|0.06|0.04|R|F|1994-03-14|1994-03-13|1994-04-09|COLLECT COD|RAIL|ggle fluffily foxes. fluffily ironic ex| +196|136|7|1|19|19686.47|0.03|0.02|R|F|1993-04-17|1993-05-27|1993-04-30|NONE|SHIP|sts maintain foxes. furiously regular p| +196|10|3|2|15|13650.15|0.03|0.04|A|F|1993-07-05|1993-05-08|1993-07-06|TAKE BACK RETURN|SHIP|s accounts. furio| +197|99|1|1|39|38964.51|0.02|0.04|N|O|1995-07-21|1995-07-01|1995-08-14|TAKE BACK RETURN|AIR|press accounts. daringly sp| +197|178|8|2|8|8625.36|0.09|0.02|A|F|1995-04-17|1995-07-01|1995-04-27|DELIVER IN PERSON|SHIP|y blithely even deposits. blithely fina| +197|156|4|3|17|17954.55|0.06|0.02|N|O|1995-08-02|1995-06-23|1995-08-03|COLLECT COD|REG AIR|ts. careful| +197|18|5|4|25|22950.25|0.04|0.01|N|F|1995-06-13|1995-05-23|1995-06-24|TAKE BACK RETURN|FOB|s-- quickly final accounts| +197|42|9|5|14|13188.56|0.09|0.01|R|F|1995-05-08|1995-05-24|1995-05-12|TAKE BACK RETURN|RAIL|use slyly slyly silent depo| +197|106|1|6|1|1006.10|0.07|0.05|N|O|1995-07-15|1995-06-21|1995-08-11|COLLECT COD|RAIL| even, thin dependencies sno| +198|57|8|1|33|31582.65|0.07|0.02|N|O|1998-01-05|1998-03-20|1998-01-10|TAKE BACK RETURN|TRUCK|carefully caref| +198|16|10|2|20|18320.20|0.03|0.00|N|O|1998-01-15|1998-03-31|1998-01-25|DELIVER IN PERSON|FOB|carefully final escapades a| +198|149|2|3|15|15737.10|0.04|0.02|N|O|1998-04-12|1998-02-26|1998-04-15|COLLECT COD|MAIL|es. quickly pending deposits s| +198|11|5|4|35|31885.35|0.08|0.02|N|O|1998-02-27|1998-03-23|1998-03-14|TAKE BACK RETURN|RAIL|ests nod quickly furiously sly pinto be| +198|102|3|5|33|33069.30|0.02|0.01|N|O|1998-03-22|1998-03-12|1998-04-14|DELIVER IN PERSON|SHIP|ending foxes acr| +199|133|9|1|50|51656.50|0.02|0.00|N|O|1996-06-12|1996-06-03|1996-07-04|DELIVER IN PERSON|MAIL|essly regular ideas boost sly| +199|134|5|2|30|31023.90|0.08|0.05|N|O|1996-03-27|1996-05-29|1996-04-14|NONE|TRUCK|ilent packages doze quickly. thinly | +224|151|2|1|16|16818.40|0.04|0.00|A|F|1994-08-01|1994-07-30|1994-08-27|DELIVER IN PERSON|MAIL|y unusual foxes | +224|109|2|2|34|34309.40|0.04|0.08|R|F|1994-07-13|1994-08-25|1994-07-31|COLLECT COD|TRUCK| carefully. final platelets | +224|190|1|3|41|44697.79|0.07|0.04|A|F|1994-09-01|1994-09-15|1994-09-02|TAKE BACK RETURN|SHIP|after the furiou| +224|167|4|4|12|12805.92|0.08|0.06|R|F|1994-10-12|1994-08-29|1994-10-20|DELIVER IN PERSON|MAIL|uriously regular packages. slyly fina| +224|94|7|5|45|44734.05|0.07|0.07|R|F|1994-08-14|1994-09-02|1994-08-27|COLLECT COD|AIR|leep furiously regular requests. furiousl| +224|51|3|6|4|3804.20|0.02|0.00|R|F|1994-09-08|1994-08-24|1994-10-04|DELIVER IN PERSON|FOB|tructions | +225|172|3|1|4|4288.68|0.09|0.07|N|O|1995-08-05|1995-08-19|1995-09-03|TAKE BACK RETURN|SHIP|ng the ironic packages. asymptotes among | +225|131|7|2|3|3093.39|0.00|0.08|N|O|1995-07-25|1995-07-08|1995-08-17|DELIVER IN PERSON|REG AIR| fluffily about the carefully bold a| +225|199|2|3|45|49463.55|0.06|0.01|N|O|1995-08-17|1995-08-20|1995-08-30|TAKE BACK RETURN|FOB|the slyly even platelets use aro| +225|147|4|4|24|25131.36|0.00|0.06|N|O|1995-09-23|1995-08-05|1995-10-16|COLLECT COD|MAIL|ironic accounts are final account| +225|8|5|5|31|28148.00|0.04|0.06|N|O|1995-06-21|1995-07-24|1995-07-04|TAKE BACK RETURN|FOB|special platelets. quickly r| +225|132|8|6|12|12385.56|0.00|0.00|A|F|1995-06-04|1995-07-15|1995-06-08|COLLECT COD|MAIL| unusual requests. bus| +225|142|1|7|44|45854.16|0.10|0.06|N|O|1995-09-22|1995-08-16|1995-10-22|NONE|REG AIR|leep slyly | +226|97|9|1|4|3988.36|0.00|0.00|R|F|1993-03-31|1993-04-30|1993-04-10|NONE|TRUCK|c foxes integrate carefully against th| +226|138|4|2|46|47753.98|0.06|0.01|A|F|1993-07-06|1993-04-24|1993-07-13|COLLECT COD|FOB|s. carefully bold accounts cajol| +226|38|4|3|35|32831.05|0.09|0.03|A|F|1993-03-31|1993-05-18|1993-04-01|NONE|RAIL|osits cajole. final, even foxes a| +226|41|10|4|45|42346.80|0.10|0.02|R|F|1993-04-17|1993-05-27|1993-05-11|DELIVER IN PERSON|AIR| carefully pending pi| +226|118|8|5|2|2036.22|0.07|0.02|R|F|1993-03-26|1993-04-13|1993-04-20|TAKE BACK RETURN|SHIP|al platelets. express somas | +226|83|4|6|48|47187.84|0.02|0.00|A|F|1993-06-11|1993-05-15|1993-06-19|NONE|REG AIR|efully silent packages. final deposit| +226|118|8|7|14|14253.54|0.09|0.00|R|F|1993-05-20|1993-06-05|1993-05-27|COLLECT COD|MAIL|ep carefully regular accounts. ironic| +227|166|1|1|19|20257.04|0.05|0.06|N|O|1995-12-10|1996-01-30|1995-12-26|NONE|RAIL|s cajole furiously a| +227|175|3|2|24|25804.08|0.07|0.07|N|O|1996-02-03|1995-12-24|1996-02-12|COLLECT COD|SHIP|uses across the blithe dependencies cajol| +228|5|8|1|3|2715.00|0.10|0.08|A|F|1993-05-20|1993-04-08|1993-05-26|DELIVER IN PERSON|SHIP|ckages. sly| +229|84|5|1|20|19681.60|0.02|0.03|R|F|1994-01-11|1994-01-31|1994-01-26|DELIVER IN PERSON|REG AIR|le. instructions use across the quickly fin| +229|129|10|2|29|29844.48|0.07|0.00|A|F|1994-03-15|1994-03-02|1994-03-26|COLLECT COD|SHIP|s, final request| +229|79|10|3|28|27413.96|0.02|0.02|R|F|1994-02-10|1994-02-02|1994-03-10|DELIVER IN PERSON|FOB| final, regular requests. platel| +229|177|6|4|3|3231.51|0.02|0.08|R|F|1994-03-22|1994-03-24|1994-04-04|DELIVER IN PERSON|REG AIR|posits. furiously regular theodol| +229|156|1|5|33|34852.95|0.03|0.06|R|F|1994-03-25|1994-02-11|1994-04-13|NONE|FOB| deposits; bold, ruthless theodolites| +229|106|9|6|29|29176.90|0.04|0.00|R|F|1994-01-14|1994-02-16|1994-01-22|NONE|FOB|uriously pending | +230|186|7|1|46|49964.28|0.09|0.00|R|F|1994-02-03|1994-01-15|1994-02-23|TAKE BACK RETURN|SHIP|old packages ha| +230|195|7|2|6|6571.14|0.03|0.08|A|F|1994-01-26|1994-01-25|1994-02-13|NONE|REG AIR| sleep furiously about the p| +230|8|5|3|1|908.00|0.07|0.06|R|F|1994-01-22|1994-01-03|1994-02-05|TAKE BACK RETURN|RAIL|blithely unusual dolphins. bold, ex| +230|10|3|4|44|40040.44|0.08|0.06|R|F|1994-02-09|1994-01-18|1994-03-11|NONE|MAIL|deposits integrate slyly sile| +230|19|9|5|8|7352.08|0.09|0.06|R|F|1993-11-03|1994-01-20|1993-11-11|TAKE BACK RETURN|TRUCK|g the instructions. fluffil| +230|34|10|6|8|7472.24|0.00|0.05|R|F|1993-11-21|1994-01-05|1993-12-19|TAKE BACK RETURN|FOB|nal ideas. silent, reg| +231|159|10|1|16|16946.40|0.04|0.08|R|F|1994-11-20|1994-10-29|1994-12-17|TAKE BACK RETURN|AIR|e furiously ironic pinto beans.| +231|84|5|2|46|45267.68|0.04|0.05|R|F|1994-12-13|1994-12-02|1994-12-14|DELIVER IN PERSON|SHIP|affix blithely. bold requests among the f| +231|199|1|3|50|54959.50|0.09|0.01|A|F|1994-12-11|1994-12-14|1994-12-13|NONE|RAIL|onic packages haggle fluffily a| +231|57|8|4|31|29668.55|0.08|0.02|A|F|1994-11-05|1994-12-27|1994-11-30|TAKE BACK RETURN|SHIP|iously special decoys wake q| +256|89|10|1|22|21759.76|0.09|0.02|R|F|1994-01-12|1993-12-28|1994-01-26|COLLECT COD|FOB|ke quickly ironic, ironic deposits. reg| +256|119|6|2|40|40764.40|0.10|0.01|A|F|1993-11-30|1993-12-13|1993-12-02|NONE|FOB|nal theodolites. deposits cajole s| +256|130|9|3|45|46355.85|0.02|0.08|R|F|1994-01-14|1994-01-17|1994-02-10|COLLECT COD|SHIP| grouches. ideas wake quickly ar| +257|147|8|1|7|7329.98|0.05|0.02|N|O|1998-06-18|1998-05-15|1998-06-27|COLLECT COD|FOB|ackages sleep bold realms. f| +258|107|4|1|8|8056.80|0.00|0.07|R|F|1994-01-20|1994-03-21|1994-02-09|NONE|REG AIR|ully about the fluffily silent dependencies| +258|197|1|2|40|43887.60|0.10|0.01|A|F|1994-03-13|1994-02-23|1994-04-05|DELIVER IN PERSON|FOB|silent frets nod daringly busy, bold| +258|162|3|3|45|47797.20|0.07|0.07|R|F|1994-03-04|1994-02-13|1994-03-30|DELIVER IN PERSON|TRUCK|regular excuses-- fluffily ruthl| +258|133|9|4|31|32027.03|0.02|0.05|A|F|1994-04-20|1994-03-20|1994-04-28|COLLECT COD|REG AIR| slyly blithely special mul| +258|36|2|5|25|23400.75|0.08|0.02|A|F|1994-04-13|1994-02-26|1994-04-29|TAKE BACK RETURN|TRUCK|leep pending packages.| +258|147|4|6|36|37697.04|0.09|0.04|A|F|1994-01-11|1994-03-04|1994-01-18|DELIVER IN PERSON|AIR|nic asymptotes. slyly silent r| +259|99|10|1|14|13987.26|0.00|0.08|A|F|1993-12-17|1993-12-09|1993-12-31|COLLECT COD|SHIP|ons against the express acco| +259|162|1|2|14|14870.24|0.03|0.05|R|F|1993-11-10|1993-11-20|1993-11-17|DELIVER IN PERSON|FOB|ully even, regul| +259|24|5|3|42|38808.84|0.09|0.00|R|F|1993-10-20|1993-11-18|1993-11-12|NONE|TRUCK|the slyly ironic pinto beans. fi| +259|196|10|4|3|3288.57|0.08|0.06|R|F|1993-10-04|1993-11-07|1993-10-14|TAKE BACK RETURN|SHIP|ng slyly at the accounts.| +259|193|6|5|6|6559.14|0.00|0.05|R|F|1993-12-05|1993-12-22|1993-12-21|COLLECT COD|TRUCK| requests sleep| +260|156|7|1|50|52807.50|0.07|0.08|N|O|1997-03-24|1997-02-09|1997-04-20|TAKE BACK RETURN|REG AIR|c deposits | +260|183|4|2|26|28162.68|0.02|0.07|N|O|1996-12-12|1997-02-06|1996-12-15|NONE|TRUCK|ld theodolites boost fl| +260|42|1|3|27|25435.08|0.05|0.08|N|O|1997-03-23|1997-02-15|1997-04-22|TAKE BACK RETURN|RAIL|ions according to the| +260|6|1|4|29|26274.00|0.10|0.06|N|O|1997-03-15|1997-01-14|1997-04-13|NONE|MAIL|fluffily even asymptotes. express wa| +260|96|9|5|44|43827.96|0.01|0.05|N|O|1997-03-26|1997-02-03|1997-04-19|DELIVER IN PERSON|MAIL|above the blithely ironic instr| +261|2|7|1|34|30668.00|0.05|0.08|R|F|1993-08-18|1993-09-24|1993-08-20|COLLECT COD|REG AIR|c packages. asymptotes da| +261|66|7|2|20|19321.20|0.00|0.06|R|F|1993-10-21|1993-08-02|1993-11-04|DELIVER IN PERSON|RAIL|ites hinder | +261|174|3|3|28|30076.76|0.08|0.03|R|F|1993-07-24|1993-08-20|1993-08-05|COLLECT COD|AIR|ironic packages nag slyly. carefully fin| +261|119|3|4|49|49936.39|0.04|0.05|R|F|1993-09-12|1993-08-31|1993-10-07|COLLECT COD|SHIP|ions. bold accounts | +261|61|6|5|49|47091.94|0.01|0.08|A|F|1993-09-29|1993-09-08|1993-10-01|COLLECT COD|SHIP| pinto beans haggle slyly furiously pending| +261|97|9|6|20|19941.80|0.06|0.06|A|F|1993-10-15|1993-09-05|1993-11-07|NONE|AIR|ing to the special, ironic deposi| +262|192|3|1|39|42595.41|0.01|0.05|N|O|1996-01-15|1996-02-18|1996-01-28|COLLECT COD|RAIL|usual, regular requests| +262|61|6|2|33|31714.98|0.09|0.03|N|O|1996-03-10|1996-01-31|1996-03-27|TAKE BACK RETURN|AIR|atelets sleep furiously. requests cajole. b| +262|59|1|3|35|33566.75|0.05|0.08|N|O|1996-03-12|1996-02-14|1996-04-11|COLLECT COD|MAIL|lites cajole along the pending packag| +263|24|9|1|22|20328.44|0.06|0.08|R|F|1994-08-24|1994-06-20|1994-09-09|NONE|FOB|efully express fo| +263|85|6|2|9|8865.72|0.08|0.00|A|F|1994-07-21|1994-07-16|1994-08-08|TAKE BACK RETURN|TRUCK|lms wake bl| +263|143|2|3|50|52157.00|0.06|0.04|R|F|1994-08-18|1994-07-31|1994-08-22|NONE|TRUCK|re the packages. special| +288|51|3|1|31|29482.55|0.00|0.03|N|O|1997-03-17|1997-04-28|1997-04-06|TAKE BACK RETURN|AIR|instructions wa| +288|117|1|2|49|49838.39|0.08|0.05|N|O|1997-04-19|1997-05-19|1997-05-18|TAKE BACK RETURN|TRUCK|ic excuses sleep always spe| +288|99|10|3|36|35967.24|0.02|0.02|N|O|1997-02-22|1997-05-07|1997-03-07|TAKE BACK RETURN|TRUCK|yly pending excu| +288|79|10|4|19|18602.33|0.07|0.07|N|O|1997-03-14|1997-04-04|1997-03-26|NONE|MAIL|deposits. blithely quick courts ar| +288|162|9|5|31|32926.96|0.10|0.04|N|O|1997-05-29|1997-04-24|1997-06-20|TAKE BACK RETURN|RAIL|ns. fluffily| +289|174|2|1|25|26854.25|0.07|0.05|N|O|1997-03-18|1997-05-05|1997-04-15|DELIVER IN PERSON|FOB|out the quickly bold theodol| +289|112|2|2|6|6072.66|0.06|0.05|N|O|1997-02-18|1997-05-08|1997-03-19|DELIVER IN PERSON|SHIP|d packages use fluffily furiously| +289|17|4|3|44|40348.44|0.10|0.08|N|O|1997-06-05|1997-04-20|1997-07-02|COLLECT COD|MAIL|ly ironic foxes. asymptotes | +289|40|6|4|48|45121.92|0.01|0.08|N|O|1997-03-14|1997-03-30|1997-03-24|DELIVER IN PERSON|RAIL|sits cajole. bold pinto beans x-ray fl| +289|47|4|5|13|12311.52|0.10|0.03|N|O|1997-06-08|1997-04-06|1997-06-18|TAKE BACK RETURN|REG AIR|ts. quickly bold deposits alongside| +290|6|1|1|35|31710.00|0.01|0.02|R|F|1994-04-01|1994-02-05|1994-04-27|NONE|MAIL|ove the final foxes detect slyly fluffily| +290|129|4|2|2|2058.24|0.05|0.04|A|F|1994-01-30|1994-02-13|1994-02-21|TAKE BACK RETURN|TRUCK|. permanently furious reques| +290|2|5|3|5|4510.00|0.03|0.05|A|F|1994-01-19|1994-02-24|1994-01-27|NONE|MAIL|ans integrate. requests sleep. fur| +290|124|9|4|23|23554.76|0.05|0.08|R|F|1994-03-14|1994-02-21|1994-04-09|NONE|AIR|refully unusual packages. | +291|123|6|1|21|21485.52|0.05|0.07|A|F|1994-05-26|1994-05-10|1994-06-23|COLLECT COD|TRUCK|y quickly regular theodolites. final t| +291|138|9|2|19|19724.47|0.08|0.02|R|F|1994-06-14|1994-04-25|1994-06-19|NONE|REG AIR|e. ruthlessly final accounts after the| +291|61|8|3|30|28831.80|0.10|0.02|R|F|1994-03-22|1994-04-30|1994-03-24|DELIVER IN PERSON|FOB| fluffily regular deposits. quickl| +292|154|5|1|8|8433.20|0.10|0.03|R|F|1992-02-18|1992-03-30|1992-03-18|DELIVER IN PERSON|RAIL|sily bold deposits alongside of the ex| +292|100|1|2|24|24002.40|0.08|0.04|R|F|1992-03-24|1992-03-06|1992-04-20|COLLECT COD|TRUCK| bold, pending theodolites u| +293|9|6|1|14|12726.00|0.02|0.05|R|F|1992-10-19|1992-12-23|1992-11-10|DELIVER IN PERSON|SHIP|es. packages above the| +293|187|8|2|11|11958.98|0.10|0.04|R|F|1992-12-24|1992-12-01|1993-01-12|COLLECT COD|MAIL| affix carefully quickly special idea| +293|118|8|3|13|13235.43|0.04|0.02|A|F|1992-12-17|1992-12-26|1992-12-22|COLLECT COD|RAIL| wake after the quickly even deposits. bli| +294|60|2|1|31|29761.86|0.00|0.01|R|F|1993-08-06|1993-08-19|1993-08-13|TAKE BACK RETURN|AIR|le fluffily along the quick| +295|198|10|1|29|31847.51|0.02|0.07|A|F|1994-11-09|1994-12-08|1994-12-07|COLLECT COD|MAIL|inst the carefully ironic pinto beans. blit| +295|92|6|2|26|25794.34|0.04|0.03|R|F|1994-12-13|1994-11-30|1995-01-06|DELIVER IN PERSON|AIR|ts above the slyly regular requests x-ray q| +295|16|10|3|8|7328.08|0.10|0.07|R|F|1995-01-13|1994-11-17|1995-01-25|NONE|TRUCK| final instructions h| +295|61|10|4|26|24987.56|0.10|0.04|A|F|1995-01-12|1994-11-22|1995-01-22|DELIVER IN PERSON|MAIL| carefully iron| +320|5|2|1|30|27150.00|0.05|0.01|N|O|1997-12-04|1998-01-21|1997-12-13|NONE|RAIL| ironic, final accounts wake quick de| +320|193|5|2|13|14211.47|0.03|0.00|N|O|1997-12-16|1997-12-26|1997-12-17|TAKE BACK RETURN|AIR|he furiously regular pinto beans. car| +321|1|8|1|21|18921.00|0.01|0.08|A|F|1993-07-18|1993-04-24|1993-08-13|TAKE BACK RETURN|REG AIR|hockey players sleep slyly sl| +321|141|4|2|41|42686.74|0.08|0.07|R|F|1993-06-21|1993-06-07|1993-07-09|NONE|REG AIR|special packages shall have to doze blit| +322|153|8|1|12|12637.80|0.08|0.07|A|F|1992-06-29|1992-05-30|1992-07-11|NONE|AIR|ular theodolites promise qu| +322|44|5|2|48|45313.92|0.02|0.07|A|F|1992-06-11|1992-06-16|1992-06-26|COLLECT COD|RAIL|dolites detect qu| +322|13|3|3|20|18260.20|0.04|0.01|R|F|1992-04-26|1992-05-04|1992-05-22|DELIVER IN PERSON|MAIL|ckly toward | +322|184|5|4|10|10841.80|0.06|0.03|R|F|1992-04-12|1992-05-13|1992-04-14|DELIVER IN PERSON|AIR| deposits grow slyly according to th| +322|12|2|5|35|31920.35|0.07|0.06|A|F|1992-07-17|1992-05-03|1992-08-14|TAKE BACK RETURN|RAIL|egular accounts cajole carefully. even d| +322|34|5|6|3|2802.09|0.08|0.05|A|F|1992-07-03|1992-05-10|1992-07-28|NONE|AIR|ending, ironic deposits along the blith| +322|38|4|7|5|4690.15|0.01|0.02|A|F|1992-04-15|1992-05-12|1992-04-26|COLLECT COD|REG AIR| special grouches sleep quickly instructio| +323|164|9|1|50|53208.00|0.05|0.04|A|F|1994-04-20|1994-04-25|1994-05-12|DELIVER IN PERSON|REG AIR|cial requests | +323|96|8|2|18|17929.62|0.06|0.07|R|F|1994-04-13|1994-06-02|1994-05-10|DELIVER IN PERSON|TRUCK|posits cajole furiously pinto beans. | +323|143|4|3|9|9388.26|0.07|0.04|A|F|1994-06-26|1994-06-10|1994-07-13|COLLECT COD|TRUCK|nic accounts. regular, regular pack| +324|200|3|1|26|28605.20|0.07|0.01|R|F|1992-04-19|1992-05-28|1992-05-12|DELIVER IN PERSON|RAIL|ross the slyly regular s| +325|159|1|1|34|36011.10|0.09|0.04|A|F|1993-10-28|1993-12-13|1993-11-17|TAKE BACK RETURN|MAIL|ly bold deposits. always iron| +325|186|7|2|5|5430.90|0.07|0.08|A|F|1994-01-02|1994-01-05|1994-01-04|TAKE BACK RETURN|MAIL| theodolites. | +325|19|3|3|35|32165.35|0.07|0.07|A|F|1993-12-06|1994-01-03|1993-12-26|DELIVER IN PERSON|REG AIR|packages wa| +326|180|9|1|41|44287.38|0.06|0.03|N|O|1995-08-30|1995-07-09|1995-09-12|DELIVER IN PERSON|TRUCK|ily quickly bold ideas.| +326|20|4|2|38|34960.76|0.02|0.08|N|O|1995-09-12|1995-08-23|1995-09-14|COLLECT COD|RAIL|es sleep slyly. carefully regular inst| +326|184|5|3|25|27104.50|0.03|0.04|N|O|1995-08-03|1995-07-27|1995-08-16|NONE|AIR|ily furiously unusual accounts. | +326|85|6|4|5|4925.40|0.03|0.08|N|O|1995-07-29|1995-07-13|1995-08-12|NONE|REG AIR|deas sleep according to the sometimes spe| +326|35|6|5|31|28985.93|0.04|0.08|N|O|1995-09-27|1995-07-06|1995-10-22|NONE|TRUCK|cies sleep quick| +326|157|9|6|41|43343.15|0.02|0.00|N|O|1995-07-05|1995-07-23|1995-07-20|TAKE BACK RETURN|REG AIR|to beans wake before the furiously re| +326|43|10|7|47|44322.88|0.04|0.04|N|O|1995-09-16|1995-07-04|1995-10-04|NONE|REG AIR| special accounts sleep | +327|144|3|1|16|16706.24|0.03|0.01|N|O|1995-07-05|1995-06-07|1995-07-09|TAKE BACK RETURN|TRUCK|cial ideas sleep af| +327|42|9|2|9|8478.36|0.09|0.05|A|F|1995-05-24|1995-07-11|1995-06-05|NONE|AIR| asymptotes are fu| +352|64|5|1|17|16389.02|0.07|0.05|R|F|1994-06-02|1994-05-31|1994-06-29|NONE|FOB|pending deposits sleep furiously | +353|120|7|1|41|41824.92|0.00|0.06|A|F|1994-03-25|1994-03-31|1994-03-30|DELIVER IN PERSON|AIR|refully final theodoli| +353|148|9|2|29|30396.06|0.09|0.00|A|F|1994-01-11|1994-03-19|1994-02-09|COLLECT COD|FOB|ctions impr| +353|135|1|3|12|12421.56|0.06|0.01|R|F|1994-01-02|1994-03-26|1994-01-19|DELIVER IN PERSON|RAIL|g deposits cajole | +353|78|7|4|46|44991.22|0.00|0.04|A|F|1994-04-14|1994-01-31|1994-05-05|DELIVER IN PERSON|FOB| ironic dolphins | +353|117|4|5|9|9153.99|0.02|0.02|A|F|1994-03-15|1994-03-20|1994-03-18|TAKE BACK RETURN|RAIL|ual accounts! carefu| +353|103|4|6|39|39120.90|0.02|0.05|A|F|1994-01-15|1994-03-30|1994-02-01|NONE|MAIL|losely quickly even accounts. c| +354|50|7|1|14|13300.70|0.08|0.04|N|O|1996-04-12|1996-06-03|1996-05-08|NONE|SHIP|quickly regular grouches will eat. careful| +354|194|8|2|24|26260.56|0.01|0.01|N|O|1996-05-08|1996-05-17|1996-06-07|DELIVER IN PERSON|AIR|y silent requests. regular, even accounts| +354|59|10|3|50|47952.50|0.08|0.05|N|O|1996-03-21|1996-05-20|1996-04-04|COLLECT COD|TRUCK|to beans s| +354|107|4|4|7|7049.70|0.06|0.01|N|O|1996-05-07|1996-04-18|1996-05-24|NONE|MAIL|ously idly ironic accounts-- quickl| +354|31|2|5|18|16758.54|0.04|0.08|N|O|1996-03-31|1996-05-13|1996-04-27|DELIVER IN PERSON|RAIL| about the carefully unusual | +354|62|1|6|36|34634.16|0.03|0.02|N|O|1996-03-19|1996-05-29|1996-03-30|NONE|AIR|onic requests thrash bold g| +354|5|10|7|14|12670.00|0.01|0.07|N|O|1996-07-06|1996-06-08|1996-07-10|TAKE BACK RETURN|MAIL|t thinly above the ironic, | +355|114|1|1|31|31437.41|0.09|0.07|A|F|1994-07-13|1994-08-18|1994-07-18|DELIVER IN PERSON|FOB|y unusual, ironic| +355|97|1|2|41|40880.69|0.05|0.00|A|F|1994-08-15|1994-07-19|1994-09-06|DELIVER IN PERSON|TRUCK| deposits. carefully r| +356|46|7|1|4|3784.16|0.10|0.01|A|F|1994-07-28|1994-08-01|1994-08-04|DELIVER IN PERSON|REG AIR| the dependencies nod unusual, final ac| +356|108|3|2|48|48388.80|0.02|0.03|R|F|1994-08-12|1994-07-31|1994-08-26|NONE|FOB|unusual packages. furiously | +356|119|3|3|35|35668.85|0.08|0.07|R|F|1994-10-14|1994-07-31|1994-10-23|COLLECT COD|TRUCK|s. unusual, final| +356|56|1|4|41|39198.05|0.07|0.05|A|F|1994-09-28|1994-09-20|1994-10-07|COLLECT COD|SHIP| according to the express foxes will| +356|125|8|5|37|37929.44|0.05|0.03|A|F|1994-07-15|1994-08-24|1994-08-09|DELIVER IN PERSON|FOB|ndencies are since the packag| +357|114|5|1|26|26366.86|0.06|0.03|N|O|1996-12-28|1996-11-26|1997-01-13|NONE|FOB| carefully pending accounts use a| +357|186|7|2|36|39102.48|0.07|0.06|N|O|1996-12-28|1996-11-13|1997-01-24|DELIVER IN PERSON|AIR|d the carefully even requests. | +357|165|2|3|32|34085.12|0.05|0.07|N|O|1997-01-28|1996-12-29|1997-02-14|NONE|MAIL|y above the carefully final accounts| +358|191|3|1|41|44738.79|0.06|0.01|A|F|1993-11-18|1993-11-14|1993-11-28|NONE|TRUCK|ely frets. furious deposits sleep | +358|190|1|2|32|34886.08|0.05|0.08|A|F|1993-10-18|1993-12-12|1993-10-31|NONE|TRUCK|y final foxes sleep blithely sl| +358|169|6|3|40|42766.40|0.09|0.01|A|F|1993-12-05|1993-11-04|1994-01-01|COLLECT COD|MAIL|ng the ironic theo| +358|97|10|4|15|14956.35|0.08|0.08|A|F|1993-10-04|1993-12-17|1993-10-23|TAKE BACK RETURN|MAIL|out the blithely ironic deposits slee| +358|29|2|5|18|16722.36|0.01|0.02|R|F|1993-10-07|1993-11-01|1993-10-26|COLLECT COD|SHIP|olphins haggle ironic accounts. f| +358|162|3|6|32|33989.12|0.03|0.05|R|F|1993-12-21|1993-11-06|1994-01-17|DELIVER IN PERSON|RAIL|lyly express deposits | +358|83|4|7|45|44238.60|0.05|0.02|A|F|1993-12-08|1993-10-29|1993-12-30|NONE|REG AIR|to beans. regular, unusual deposits sl| +359|166|7|1|30|31984.80|0.00|0.08|A|F|1995-01-06|1995-02-20|1995-01-20|TAKE BACK RETURN|AIR|uses detect spec| +359|12|9|2|18|16416.18|0.00|0.03|A|F|1995-01-27|1995-03-18|1995-01-31|DELIVER IN PERSON|RAIL|unusual warthogs. ironically sp| +359|132|8|3|17|17546.21|0.07|0.06|A|F|1995-01-31|1995-03-18|1995-02-10|COLLECT COD|SHIP|sts according to the blithely| +359|90|1|4|38|37623.42|0.10|0.08|R|F|1995-03-30|1995-01-20|1995-04-25|DELIVER IN PERSON|RAIL|g furiously. regular, sile| +359|168|5|5|11|11749.76|0.01|0.03|A|F|1995-02-15|1995-01-27|1995-02-18|NONE|FOB|rets wake blithely. slyly final dep| +359|183|4|6|23|24913.14|0.04|0.07|R|F|1995-01-31|1995-03-11|1995-02-16|DELIVER IN PERSON|REG AIR|ic courts snooze quickly furiously final fo| +384|179|8|1|38|41008.46|0.07|0.01|R|F|1992-06-02|1992-04-18|1992-06-10|DELIVER IN PERSON|TRUCK|totes cajole blithely against the even| +384|64|3|2|49|47238.94|0.09|0.07|A|F|1992-04-01|1992-04-25|1992-04-18|COLLECT COD|AIR|refully carefully ironic instructions. bl| +384|182|3|3|11|11903.98|0.02|0.08|A|F|1992-04-02|1992-04-21|1992-04-15|COLLECT COD|MAIL|ash carefully| +384|93|6|4|11|10923.99|0.00|0.06|R|F|1992-06-24|1992-05-29|1992-07-22|COLLECT COD|TRUCK|nic excuses are furiously above the blith| +384|132|8|5|14|14449.82|0.08|0.06|R|F|1992-06-14|1992-05-29|1992-07-05|DELIVER IN PERSON|TRUCK|ckages are slyly after the slyly specia| +385|167|6|1|7|7470.12|0.05|0.06|N|O|1996-05-23|1996-05-09|1996-06-06|DELIVER IN PERSON|REG AIR| special asymptote| +385|54|9|2|46|43886.30|0.08|0.07|N|O|1996-03-29|1996-05-17|1996-04-18|NONE|REG AIR|lthily ironic f| +386|153|5|1|39|41072.85|0.10|0.07|A|F|1995-05-10|1995-02-28|1995-05-25|NONE|SHIP|hely. carefully regular accounts hag| +386|69|4|2|16|15504.96|0.06|0.01|A|F|1995-04-12|1995-04-18|1995-05-11|DELIVER IN PERSON|MAIL|lithely fluffi| +386|131|2|3|37|38151.81|0.09|0.04|A|F|1995-05-23|1995-03-01|1995-05-25|TAKE BACK RETURN|MAIL|ending pearls breach fluffily. slyly pen| +387|137|8|1|1|1037.13|0.08|0.03|N|O|1997-05-06|1997-04-23|1997-05-10|NONE|SHIP| pinto beans wake furiously carefu| +387|153|4|2|42|44232.30|0.07|0.05|N|O|1997-05-25|1997-02-25|1997-05-29|DELIVER IN PERSON|RAIL|lithely final theodolites.| +387|97|10|3|40|39883.60|0.09|0.02|N|O|1997-03-08|1997-04-18|1997-03-31|COLLECT COD|TRUCK| quickly ironic platelets are slyly. fluff| +387|56|7|4|19|18164.95|0.08|0.00|N|O|1997-03-14|1997-04-21|1997-04-04|NONE|REG AIR|gular dependencies| +387|149|6|5|32|33572.48|0.08|0.06|N|O|1997-05-02|1997-04-11|1997-05-11|DELIVER IN PERSON|TRUCK|gle. silent, fur| +388|33|9|1|42|39187.26|0.05|0.06|R|F|1993-02-21|1993-02-26|1993-03-15|COLLECT COD|FOB|accounts sleep furiously| +388|128|9|2|46|47293.52|0.07|0.01|A|F|1993-03-22|1993-01-26|1993-03-24|COLLECT COD|FOB|to beans nag about the careful reque| +388|65|2|3|40|38602.40|0.06|0.01|A|F|1992-12-24|1993-01-28|1993-01-19|TAKE BACK RETURN|REG AIR|quests against the carefully unusual epi| +389|190|1|1|2|2180.38|0.09|0.00|R|F|1994-04-13|1994-04-10|1994-04-25|TAKE BACK RETURN|RAIL|fts. courts eat blithely even dependenc| +390|107|10|1|10|10071.00|0.02|0.05|N|O|1998-05-26|1998-07-06|1998-06-23|TAKE BACK RETURN|SHIP| requests. final accounts x-ray beside the| +390|124|7|2|17|17410.04|0.09|0.06|N|O|1998-06-07|1998-06-14|1998-07-07|COLLECT COD|SHIP|ending, pending pinto beans wake slyl| +390|184|5|3|46|49872.28|0.07|0.04|N|O|1998-06-06|1998-05-20|1998-06-14|DELIVER IN PERSON|SHIP|cial excuses. bold, pending packages| +390|142|3|4|42|43769.88|0.01|0.05|N|O|1998-06-06|1998-06-22|1998-07-05|COLLECT COD|SHIP|counts nag across the sly, sil| +390|128|3|5|13|13365.56|0.02|0.06|N|O|1998-07-08|1998-05-10|1998-07-18|DELIVER IN PERSON|SHIP|sleep carefully idle packages. blithely | +390|125|4|6|11|11276.32|0.09|0.06|N|O|1998-05-05|1998-05-15|1998-06-01|DELIVER IN PERSON|SHIP|according to the foxes are furiously | +390|85|6|7|24|23641.92|0.05|0.02|N|O|1998-04-18|1998-05-19|1998-04-28|TAKE BACK RETURN|AIR|y. enticingly final depos| +391|122|1|1|14|14309.68|0.09|0.02|R|F|1995-02-11|1995-02-03|1995-02-13|TAKE BACK RETURN|TRUCK| escapades sleep furiously about | +416|94|6|1|25|24852.25|0.00|0.05|A|F|1993-10-11|1993-11-26|1993-10-21|DELIVER IN PERSON|TRUCK|y final theodolites about| +416|111|1|2|22|22244.42|0.10|0.00|R|F|1993-12-27|1993-12-17|1994-01-09|COLLECT COD|RAIL|rint blithely above the pending sentim| +416|175|5|3|25|26879.25|0.07|0.01|R|F|1993-10-16|1993-12-03|1993-10-29|NONE|AIR|ses boost after the bold requests.| +417|40|1|1|39|36661.56|0.01|0.02|A|F|1994-05-31|1994-05-02|1994-06-06|NONE|SHIP|y regular requests wake along | +417|70|7|2|18|17461.26|0.00|0.01|R|F|1994-03-29|1994-04-10|1994-04-26|TAKE BACK RETURN|FOB|- final requests sle| +417|45|2|3|41|38746.64|0.10|0.01|R|F|1994-04-11|1994-03-08|1994-05-06|COLLECT COD|RAIL|tes. regular requests across the | +417|132|3|4|2|2064.26|0.01|0.03|R|F|1994-02-13|1994-04-19|1994-03-15|DELIVER IN PERSON|SHIP|uriously bol| +418|19|3|1|31|28489.31|0.00|0.03|N|F|1995-06-05|1995-06-18|1995-06-26|COLLECT COD|FOB|final theodolites. fluffil| +418|2|5|2|1|902.00|0.04|0.07|N|O|1995-06-23|1995-06-16|1995-07-23|DELIVER IN PERSON|AIR|regular, silent pinto| +418|35|1|3|3|2805.09|0.04|0.06|N|O|1995-06-29|1995-07-12|1995-07-01|COLLECT COD|AIR|ly furiously regular w| +419|153|8|1|33|34753.95|0.05|0.02|N|O|1996-11-06|1996-12-25|1996-11-20|TAKE BACK RETURN|TRUCK|y above the bli| +419|65|2|2|32|30881.92|0.01|0.06|N|O|1996-12-04|1996-12-04|1996-12-24|COLLECT COD|SHIP|blithely regular requests. special pinto| +419|71|1|3|15|14566.05|0.07|0.04|N|O|1996-12-17|1996-11-28|1996-12-19|TAKE BACK RETURN|REG AIR| sleep final, regular theodolites. fluffi| +419|9|6|4|15|13635.00|0.01|0.02|N|O|1997-01-09|1996-12-22|1997-01-25|COLLECT COD|FOB|of the careful, thin theodolites. quickly s| +419|149|2|5|17|17835.38|0.01|0.00|N|O|1997-01-13|1996-12-20|1997-02-01|COLLECT COD|REG AIR|lar dependencies: carefully regu| +420|101|6|1|5|5005.50|0.04|0.03|N|O|1995-11-04|1996-01-02|1995-11-30|NONE|REG AIR|cajole blit| +420|162|7|2|22|23367.52|0.05|0.04|N|O|1996-01-25|1995-12-16|1996-02-03|TAKE BACK RETURN|AIR|ly against the blithely re| +420|48|1|3|45|42661.80|0.09|0.08|N|O|1996-01-14|1996-01-01|1996-01-26|COLLECT COD|FOB| final accounts. furiously express forges| +420|75|6|4|12|11700.84|0.08|0.08|N|O|1996-02-05|1996-01-03|1996-02-12|TAKE BACK RETURN|REG AIR|c instructions are | +420|73|2|5|37|36003.59|0.02|0.00|N|O|1995-11-16|1995-12-13|1995-11-19|DELIVER IN PERSON|SHIP|rbits. bold requests along the quickl| +420|124|7|6|40|40964.80|0.01|0.05|N|O|1995-11-26|1995-12-26|1995-12-20|TAKE BACK RETURN|FOB| after the special| +420|16|7|7|39|35724.39|0.00|0.08|N|O|1995-12-09|1995-12-16|1995-12-31|DELIVER IN PERSON|REG AIR|s. ironic waters about the car| +421|134|5|1|1|1034.13|0.02|0.07|R|F|1992-05-29|1992-04-27|1992-06-09|NONE|TRUCK|oldly busy deposit| +422|152|10|1|25|26303.75|0.10|0.07|N|O|1997-07-01|1997-08-17|1997-07-09|DELIVER IN PERSON|SHIP|carefully bold theodolit| +422|171|1|2|10|10711.70|0.02|0.03|N|O|1997-06-15|1997-08-04|1997-07-08|TAKE BACK RETURN|AIR|he furiously ironic theodolite| +422|176|4|3|46|49503.82|0.09|0.00|N|O|1997-06-21|1997-07-14|1997-06-27|DELIVER IN PERSON|RAIL| ideas. qu| +422|162|7|4|25|26554.00|0.10|0.04|N|O|1997-08-24|1997-07-09|1997-09-22|NONE|FOB|ep along the furiousl| +423|132|3|1|27|27867.51|0.06|0.03|N|O|1996-08-20|1996-08-01|1996-08-23|TAKE BACK RETURN|SHIP|ccounts. blithely regular pack| +448|126|7|1|4|4104.48|0.00|0.04|N|O|1995-11-25|1995-10-20|1995-11-26|TAKE BACK RETURN|MAIL|nts thrash quickly among the b| +448|173|1|2|46|49365.82|0.05|0.00|N|O|1995-08-31|1995-09-30|1995-09-09|COLLECT COD|SHIP| to the fluffily ironic packages.| +448|27|6|3|35|32445.70|0.10|0.08|N|O|1995-09-27|1995-11-19|1995-10-20|COLLECT COD|REG AIR|ses nag quickly quickly ir| +448|170|1|4|8|8561.36|0.10|0.00|N|O|1995-11-02|1995-10-16|1995-11-15|COLLECT COD|TRUCK|ounts wake blithely. furiously pending| +448|138|9|5|23|23876.99|0.02|0.05|N|O|1995-09-26|1995-11-02|1995-10-17|NONE|SHIP|ious, final gifts| +449|152|7|1|12|12625.80|0.02|0.08|N|O|1995-11-06|1995-08-25|1995-11-18|TAKE BACK RETURN|SHIP|ly. blithely ironic | +449|109|6|2|4|4036.40|0.10|0.06|N|O|1995-10-27|1995-09-14|1995-11-21|DELIVER IN PERSON|FOB|are fluffily. requests are furiously| +449|10|1|3|3|2730.03|0.07|0.08|N|O|1995-07-28|1995-09-11|1995-08-01|NONE|RAIL| bold deposits. express theodolites haggle| +449|158|3|4|22|23279.30|0.07|0.00|N|O|1995-08-17|1995-09-04|1995-09-10|COLLECT COD|FOB|furiously final theodolites eat careful| +450|162|7|1|42|44610.72|0.03|0.00|N|F|1995-06-07|1995-05-29|1995-06-23|TAKE BACK RETURN|SHIP|y asymptotes. regular depen| +450|107|8|2|5|5035.50|0.03|0.02|A|F|1995-04-02|1995-05-06|1995-04-13|TAKE BACK RETURN|TRUCK|the pinto bea| +450|143|6|3|32|33380.48|0.06|0.03|N|O|1995-07-02|1995-04-25|1995-07-30|TAKE BACK RETURN|SHIP| accounts nod fluffily even, pending| +450|57|9|4|40|38282.00|0.05|0.03|R|F|1995-03-20|1995-05-25|1995-04-14|NONE|RAIL|ve. asymptote| +450|79|10|5|2|1958.14|0.09|0.00|A|F|1995-03-11|1995-05-21|1995-03-16|COLLECT COD|AIR|y even pinto beans; qui| +450|153|1|6|33|34753.95|0.08|0.05|R|F|1995-05-18|1995-05-22|1995-05-23|TAKE BACK RETURN|REG AIR|ily carefully final depo| +451|130|9|1|36|37084.68|0.02|0.06|N|O|1998-06-18|1998-08-14|1998-06-20|TAKE BACK RETURN|AIR|rges can haggle carefully ironic, dogged | +451|33|4|2|42|39187.26|0.05|0.01|N|O|1998-08-01|1998-08-05|1998-08-30|DELIVER IN PERSON|TRUCK|express excuses. blithely ironic pin| +451|87|8|3|1|987.08|0.07|0.05|N|O|1998-07-13|1998-07-03|1998-08-04|DELIVER IN PERSON|AIR| carefully ironic packages solve furiously | +451|77|5|4|28|27357.96|0.04|0.05|N|O|1998-06-16|1998-07-09|1998-06-17|DELIVER IN PERSON|SHIP| theodolites. even cou| +452|115|6|1|2|2030.22|0.04|0.03|N|O|1997-12-26|1998-01-03|1998-01-12|COLLECT COD|FOB|y express instru| +453|198|1|1|45|49418.55|0.01|0.00|N|O|1997-06-30|1997-08-20|1997-07-19|COLLECT COD|REG AIR|ifts wake carefully.| +453|176|4|2|38|40894.46|0.08|0.04|N|O|1997-06-30|1997-07-08|1997-07-16|DELIVER IN PERSON|REG AIR| furiously f| +453|14|1|3|38|34732.38|0.10|0.01|N|O|1997-08-10|1997-07-24|1997-09-07|NONE|SHIP|sts cajole. furiously un| +453|96|7|4|45|44824.05|0.10|0.01|N|O|1997-09-18|1997-06-29|1997-10-14|TAKE BACK RETURN|AIR|ironic foxes. slyly pending depos| +453|26|1|5|32|29632.64|0.04|0.01|N|O|1997-07-15|1997-06-27|1997-07-18|NONE|REG AIR|s. fluffily bold packages cajole. unu| +453|95|7|6|28|27862.52|0.07|0.07|N|O|1997-08-16|1997-08-12|1997-08-27|NONE|MAIL|final dependencies. slyly special pl| +454|118|8|1|24|24434.64|0.06|0.01|N|O|1996-04-26|1996-03-23|1996-05-20|NONE|TRUCK|le. deposits after the ideas nag unusual pa| +455|157|9|1|42|44400.30|0.10|0.02|N|O|1997-01-26|1997-01-10|1997-02-22|DELIVER IN PERSON|REG AIR|around the quickly blit| +455|28|9|2|44|40832.88|0.05|0.08|N|O|1997-01-17|1997-02-22|1997-02-12|TAKE BACK RETURN|TRUCK| accounts sleep slyly ironic asymptote| +455|49|2|3|45|42706.80|0.04|0.06|N|O|1996-12-20|1997-01-31|1997-01-07|TAKE BACK RETURN|SHIP|thrash ironically regular packages. qui| +455|171|9|4|11|11782.87|0.01|0.02|N|O|1997-03-15|1997-02-14|1997-03-26|DELIVER IN PERSON|MAIL|g deposits against the slyly idle foxes u| +480|53|4|1|22|20967.10|0.04|0.02|A|F|1993-06-16|1993-07-28|1993-07-09|NONE|MAIL|into beans cajole furiously. accounts s| +481|19|9|1|17|15623.17|0.07|0.05|A|F|1992-10-21|1992-12-09|1992-11-19|DELIVER IN PERSON|MAIL|. quickly final accounts among the | +481|21|2|2|19|17499.38|0.08|0.01|R|F|1993-01-09|1992-11-27|1993-01-14|TAKE BACK RETURN|AIR|p blithely after t| +481|186|7|3|42|45619.56|0.08|0.08|A|F|1992-11-27|1992-11-11|1992-12-08|COLLECT COD|RAIL|mptotes are furiously among the iron| +481|82|3|4|11|10802.88|0.05|0.06|A|F|1993-01-12|1992-11-17|1993-02-05|NONE|FOB|eful attai| +481|112|9|5|31|31375.41|0.05|0.01|A|F|1993-01-15|1992-12-31|1993-01-21|DELIVER IN PERSON|AIR|usly final packages believe. quick| +482|138|9|1|32|33220.16|0.00|0.02|N|O|1996-05-22|1996-05-14|1996-05-29|NONE|SHIP|usual deposits affix against | +482|122|5|2|1|1022.12|0.05|0.08|N|O|1996-05-29|1996-05-20|1996-05-31|COLLECT COD|AIR|es. quickly ironic escapades sleep furious| +482|62|9|3|31|29823.86|0.04|0.03|N|O|1996-06-01|1996-05-06|1996-06-17|NONE|MAIL| blithe pin| +482|196|7|4|8|8769.52|0.02|0.05|N|O|1996-04-19|1996-05-05|1996-04-21|NONE|TRUCK|tructions near the final, regular ideas de| +482|39|10|5|46|43195.38|0.01|0.06|N|O|1996-07-19|1996-06-05|1996-08-10|NONE|MAIL|furiously thin realms. final, fina| +482|79|10|6|19|18602.33|0.04|0.00|N|O|1996-03-27|1996-04-25|1996-04-15|NONE|FOB|ts hinder carefully silent requests| +483|33|9|1|8|7464.24|0.00|0.08|N|O|1995-08-22|1995-08-23|1995-09-18|COLLECT COD|RAIL|osits. carefully fin| +483|80|1|2|23|22541.84|0.04|0.06|N|O|1995-07-20|1995-08-11|1995-08-04|DELIVER IN PERSON|MAIL|requests was quickly against th| +483|88|9|3|9|8892.72|0.04|0.03|N|O|1995-09-10|1995-09-02|1995-09-13|NONE|AIR| carefully express ins| +484|31|2|1|49|45620.47|0.10|0.02|N|O|1997-03-06|1997-02-28|1997-03-23|COLLECT COD|TRUCK|ven accounts| +484|32|8|2|45|41941.35|0.06|0.07|N|O|1997-04-09|1997-03-20|1997-04-19|DELIVER IN PERSON|TRUCK|usly final excuses boost slyly blithe| +484|184|5|3|50|54209.00|0.06|0.05|N|O|1997-01-24|1997-03-27|1997-02-22|DELIVER IN PERSON|MAIL|uctions wake. final, silent requests haggle| +484|165|6|4|22|23433.52|0.07|0.03|N|O|1997-04-29|1997-03-26|1997-05-17|TAKE BACK RETURN|SHIP|es are pending instructions. furiously unu| +484|77|6|5|48|46899.36|0.00|0.05|N|O|1997-03-05|1997-02-08|1997-03-22|TAKE BACK RETURN|MAIL|l, bold packages? even mult| +484|97|9|6|10|9970.90|0.01|0.08|N|O|1997-04-06|1997-02-14|1997-04-16|COLLECT COD|FOB|x fluffily carefully regular| +485|150|1|1|50|52507.50|0.01|0.00|N|O|1997-03-28|1997-05-26|1997-04-18|TAKE BACK RETURN|MAIL|iously quick excuses. carefully final f| +485|28|7|2|40|37120.80|0.08|0.01|N|O|1997-04-29|1997-05-08|1997-04-30|TAKE BACK RETURN|TRUCK|al escapades| +485|137|3|3|22|22816.86|0.00|0.05|N|O|1997-04-06|1997-04-27|1997-05-01|DELIVER IN PERSON|TRUCK|refully final notornis haggle according | +486|76|7|1|36|35138.52|0.00|0.01|N|O|1996-06-25|1996-05-06|1996-07-07|COLLECT COD|AIR|deposits around the quickly regular packa| +486|68|9|2|40|38722.40|0.03|0.08|N|O|1996-05-21|1996-06-06|1996-06-07|COLLECT COD|SHIP|ts nag quickly among the slyl| +486|136|2|3|26|26939.38|0.04|0.03|N|O|1996-03-16|1996-05-25|1996-03-31|NONE|RAIL|forges along the | +486|72|1|4|38|36938.66|0.08|0.05|N|O|1996-05-07|1996-04-26|1996-05-26|TAKE BACK RETURN|TRUCK| blithely final pinto | +486|29|2|5|3|2787.06|0.07|0.05|N|O|1996-07-07|1996-04-20|1996-07-23|DELIVER IN PERSON|RAIL|ccounts ha| +486|47|4|6|46|43563.84|0.00|0.03|N|O|1996-04-18|1996-05-02|1996-04-20|COLLECT COD|AIR|theodolites eat carefully furious| +487|92|3|1|47|46628.23|0.06|0.06|R|F|1992-09-30|1992-10-08|1992-10-24|NONE|TRUCK|tions. blithely reg| +487|83|4|2|2|1966.16|0.02|0.06|R|F|1992-10-19|1992-11-04|1992-11-11|COLLECT COD|TRUCK|oss the unusual pinto beans. reg| +512|189|10|1|19|20694.42|0.08|0.05|N|O|1995-07-12|1995-07-11|1995-08-04|COLLECT COD|MAIL| sleep. requests alongside of the fluff| +512|23|2|2|37|34151.74|0.01|0.04|N|O|1995-06-20|1995-07-05|1995-07-16|NONE|RAIL|nic depths cajole? blithely b| +512|180|1|3|40|43207.20|0.05|0.02|N|O|1995-07-06|1995-07-08|1995-07-08|COLLECT COD|TRUCK|quests are da| +512|83|4|4|10|9830.80|0.09|0.02|N|O|1995-09-16|1995-07-29|1995-10-07|NONE|AIR|xes. pinto beans cajole carefully; | +512|65|6|5|6|5790.36|0.03|0.05|R|F|1995-06-10|1995-06-21|1995-06-16|DELIVER IN PERSON|FOB|en ideas haggle | +512|33|9|6|12|11196.36|0.04|0.00|R|F|1995-05-21|1995-08-03|1995-06-09|COLLECT COD|FOB|old furiously express deposits. specia| +512|51|9|7|2|1902.10|0.09|0.08|N|O|1995-06-19|1995-08-13|1995-06-24|NONE|TRUCK|e slyly silent accounts serve with| +513|62|7|1|20|19241.20|0.09|0.07|N|O|1995-07-12|1995-05-31|1995-07-31|NONE|AIR|efully ironic ideas doze slyl| +513|122|5|2|44|44973.28|0.01|0.01|N|O|1995-07-14|1995-07-14|1995-08-12|NONE|MAIL|kages sleep boldly ironic theodolites. acco| +514|79|9|1|21|20560.47|0.06|0.02|N|O|1996-06-09|1996-05-15|1996-07-07|DELIVER IN PERSON|RAIL|s sleep quickly blithely| +514|118|2|2|34|34615.74|0.08|0.02|N|O|1996-04-14|1996-06-03|1996-04-23|COLLECT COD|REG AIR|ily even patterns. bold, silent instruc| +514|13|7|3|6|5478.06|0.06|0.01|N|O|1996-05-30|1996-06-04|1996-06-28|COLLECT COD|SHIP|as haggle blithely; quickly s| +514|116|7|4|43|43692.73|0.00|0.08|N|O|1996-06-07|1996-05-14|1996-07-01|TAKE BACK RETURN|FOB|thely regular | +515|105|8|1|10|10051.00|0.03|0.02|A|F|1993-10-04|1993-11-03|1993-10-08|NONE|FOB|ar deposits th| +515|148|1|2|38|39829.32|0.10|0.07|A|F|1993-09-19|1993-11-12|1993-10-03|DELIVER IN PERSON|SHIP|ays. furiously express requests haggle furi| +515|183|4|3|11|11914.98|0.00|0.02|R|F|1993-09-04|1993-10-02|1993-09-05|DELIVER IN PERSON|FOB|ly pending accounts haggle blithel| +515|109|10|4|34|34309.40|0.09|0.03|R|F|1993-10-03|1993-10-26|1993-10-15|DELIVER IN PERSON|REG AIR|ic dependencie| +515|131|7|5|32|32996.16|0.01|0.07|R|F|1993-10-10|1993-10-08|1993-11-02|TAKE BACK RETURN|FOB|r sauternes boost. final theodolites wake a| +515|109|4|6|25|25227.50|0.04|0.08|R|F|1993-11-14|1993-11-07|1993-12-03|DELIVER IN PERSON|MAIL|e packages engag| +516|25|4|1|11|10175.22|0.01|0.06|N|O|1998-05-02|1998-05-23|1998-05-12|DELIVER IN PERSON|FOB|ongside of the blithely final reque| +517|45|6|1|28|26461.12|0.03|0.02|N|O|1997-04-30|1997-05-18|1997-05-17|COLLECT COD|MAIL| requests. special, fi| +517|156|4|2|15|15842.25|0.02|0.00|N|O|1997-04-09|1997-06-26|1997-05-01|NONE|TRUCK| slyly. express requests ar| +517|41|8|3|9|8469.36|0.04|0.00|N|O|1997-05-03|1997-06-16|1997-05-24|COLLECT COD|SHIP| slyly stealthily express instructions. | +517|133|4|4|11|11364.43|0.06|0.02|N|O|1997-06-20|1997-06-01|1997-06-27|NONE|REG AIR|ly throughout the fu| +517|24|3|5|23|21252.46|0.00|0.01|N|O|1997-04-19|1997-05-07|1997-05-12|COLLECT COD|RAIL| kindle. furiously bold requests mus| +518|165|6|1|30|31954.80|0.07|0.05|N|O|1998-02-18|1998-03-27|1998-03-16|COLLECT COD|TRUCK|slyly by the packages. carefull| +518|84|5|2|23|22633.84|0.05|0.07|N|O|1998-02-20|1998-05-05|1998-03-11|COLLECT COD|TRUCK| special requests. fluffily ironic re| +518|134|5|3|12|12409.56|0.01|0.06|N|O|1998-03-08|1998-03-31|1998-04-06|NONE|AIR| packages thrash slyly| +518|122|3|4|46|47017.52|0.07|0.02|N|O|1998-04-07|1998-04-17|1998-04-29|NONE|MAIL|. blithely even ideas cajole furiously. b| +518|71|2|5|16|15537.12|0.01|0.01|N|O|1998-03-15|1998-03-24|1998-04-08|NONE|MAIL|use quickly expre| +518|197|10|6|39|42790.41|0.09|0.08|N|O|1998-02-26|1998-03-17|1998-03-21|DELIVER IN PERSON|FOB| the bold, special deposits are carefully | +518|186|7|7|48|52136.64|0.03|0.07|N|O|1998-03-06|1998-04-22|1998-03-14|NONE|FOB| slyly final platelets; quickly even deposi| +519|159|4|1|1|1059.15|0.07|0.07|N|O|1997-12-01|1998-01-26|1997-12-23|COLLECT COD|REG AIR|bold requests believe furiou| +519|3|4|2|38|34314.00|0.05|0.08|N|O|1998-02-19|1997-12-15|1998-03-19|DELIVER IN PERSON|FOB|gular excuses detect quickly furiously | +519|106|1|3|19|19115.90|0.00|0.02|N|O|1998-01-09|1998-01-03|1998-02-06|COLLECT COD|AIR|asymptotes. p| +519|47|6|4|27|25570.08|0.08|0.06|N|O|1997-11-20|1997-12-06|1997-12-16|DELIVER IN PERSON|REG AIR|le. even, final dependencies| +519|10|5|5|13|11830.13|0.06|0.08|N|O|1998-02-06|1997-12-02|1998-03-03|TAKE BACK RETURN|TRUCK|c accounts wake along the ironic so| +519|151|6|6|3|3153.45|0.04|0.00|N|O|1998-02-01|1998-01-25|1998-02-27|TAKE BACK RETURN|FOB|erve blithely blithely ironic asymp| +544|139|10|1|47|48839.11|0.08|0.06|R|F|1993-03-14|1993-03-27|1993-03-27|COLLECT COD|SHIP|ecial pains. deposits grow foxes. | +545|170|1|1|4|4280.68|0.02|0.00|N|O|1996-02-23|1995-12-16|1996-03-21|DELIVER IN PERSON|FOB|, ironic grouches cajole over| +545|171|10|2|18|19281.06|0.00|0.00|N|O|1996-02-21|1996-01-17|1996-02-26|NONE|RAIL|al, final packages affix. even a| +546|85|6|1|16|15761.28|0.08|0.02|N|O|1997-02-04|1996-12-30|1997-02-25|DELIVER IN PERSON|TRUCK|de of the orbits. sometimes regula| +547|71|10|1|44|42727.08|0.08|0.08|N|O|1996-10-18|1996-08-17|1996-10-27|TAKE BACK RETURN|FOB|thely express dependencies. qu| +547|137|8|2|48|49782.24|0.01|0.04|N|O|1996-10-21|1996-08-04|1996-11-20|COLLECT COD|SHIP|thely specia| +547|182|3|3|3|3246.54|0.05|0.02|N|O|1996-09-04|1996-08-01|1996-09-21|COLLECT COD|SHIP|pinto beans. ironi| +548|197|8|1|2|2194.38|0.06|0.05|A|F|1994-11-26|1994-11-06|1994-12-06|COLLECT COD|MAIL|ests haggle quickly eve| +548|5|6|2|6|5430.00|0.00|0.08|A|F|1995-01-18|1994-12-08|1995-02-10|NONE|TRUCK|sits wake furiously regular| +548|1|8|3|21|18921.00|0.03|0.08|A|F|1995-01-13|1994-12-18|1995-01-25|NONE|AIR|ideas. special accounts above the furiou| +548|57|9|4|21|20098.05|0.08|0.03|A|F|1994-10-27|1994-12-04|1994-11-21|DELIVER IN PERSON|AIR| engage quickly. regular theo| +548|93|7|5|19|18868.71|0.00|0.02|A|F|1994-09-24|1994-11-24|1994-10-01|DELIVER IN PERSON|MAIL|courts boost care| +548|153|8|6|32|33700.80|0.06|0.04|A|F|1994-12-16|1994-11-20|1994-12-29|NONE|REG AIR|c instruction| +549|196|9|1|18|19731.42|0.07|0.04|R|F|1992-10-19|1992-08-12|1992-11-13|COLLECT COD|REG AIR|furiously according to the ironic, regular | +549|189|10|2|38|41388.84|0.07|0.05|A|F|1992-08-17|1992-08-28|1992-09-05|COLLECT COD|RAIL|the regular, furious excuses. carefu| +549|66|7|3|36|34778.16|0.08|0.04|R|F|1992-09-11|1992-10-11|1992-09-12|DELIVER IN PERSON|AIR|ts against the ironic, even theodolites eng| +549|21|4|4|18|16578.36|0.09|0.01|A|F|1992-07-31|1992-09-11|1992-08-08|NONE|RAIL|ely regular accounts above the | +549|24|7|5|38|35112.76|0.06|0.02|R|F|1992-08-23|1992-08-12|1992-08-25|COLLECT COD|REG AIR|eposits. carefully regular depos| +550|191|3|1|31|33826.89|0.04|0.02|N|O|1995-10-24|1995-09-27|1995-11-04|COLLECT COD|AIR|thely silent packages. unusual| +551|24|9|1|8|7392.16|0.08|0.02|N|O|1995-07-29|1995-07-18|1995-08-02|NONE|REG AIR| wake quickly slyly pending platel| +551|159|4|2|20|21183.00|0.00|0.07|N|O|1995-09-18|1995-08-25|1995-10-11|COLLECT COD|TRUCK|r ideas. final, even ideas hinder alongside| +551|162|9|3|16|16994.56|0.07|0.06|N|O|1995-07-29|1995-08-19|1995-08-10|COLLECT COD|MAIL|y along the carefully ex| +576|87|8|1|2|1974.16|0.07|0.01|N|O|1997-05-15|1997-06-30|1997-05-28|NONE|RAIL|ccounts along the ac| +576|34|5|2|6|5604.18|0.06|0.05|N|O|1997-05-15|1997-07-26|1997-06-03|DELIVER IN PERSON|TRUCK|al deposits. slyly even sauternes a| +576|37|3|3|6|5622.18|0.08|0.07|N|O|1997-08-28|1997-06-16|1997-09-25|DELIVER IN PERSON|FOB|ts. ironic multipliers | +576|138|9|4|5|5190.65|0.03|0.07|N|O|1997-06-11|1997-06-17|1997-07-05|NONE|REG AIR|l foxes boost slyly. accounts af| +577|26|5|1|25|23150.50|0.06|0.01|A|F|1995-04-09|1995-02-20|1995-05-09|TAKE BACK RETURN|AIR|ve slyly of the frets. careful| +577|64|1|2|14|13496.84|0.08|0.03|R|F|1995-03-19|1995-02-25|1995-04-09|DELIVER IN PERSON|RAIL|l accounts wake deposits. ironic packa| +578|156|7|1|40|42246.00|0.02|0.08|N|O|1997-02-10|1997-03-18|1997-02-11|NONE|SHIP|usly even platel| +578|188|9|2|23|25028.14|0.05|0.08|N|O|1997-03-06|1997-03-03|1997-03-20|TAKE BACK RETURN|FOB|nstructions. ironic deposits| +579|151|6|1|9|9460.35|0.00|0.05|N|O|1998-06-20|1998-04-28|1998-07-19|DELIVER IN PERSON|RAIL|e ironic, express deposits are furiously| +579|33|4|2|39|36388.17|0.02|0.01|N|O|1998-06-21|1998-06-03|1998-06-26|COLLECT COD|REG AIR|ncies. furiously final r| +579|60|5|3|6|5760.36|0.03|0.00|N|O|1998-04-24|1998-05-03|1998-05-08|TAKE BACK RETURN|TRUCK|ickly final requests-- bold accou| +579|7|10|4|41|37187.00|0.04|0.05|N|O|1998-05-28|1998-05-01|1998-06-04|COLLECT COD|REG AIR|bold, express requests sublate slyly. blith| +579|13|7|5|28|25564.28|0.00|0.03|N|O|1998-07-10|1998-05-24|1998-07-19|NONE|RAIL|ic ideas until th| +579|167|6|6|5|5335.80|0.05|0.08|N|O|1998-05-02|1998-04-25|1998-05-05|COLLECT COD|REG AIR|refully silent ideas cajole furious| +580|85|6|1|33|32507.64|0.03|0.05|N|O|1997-10-11|1997-09-19|1997-10-16|TAKE BACK RETURN|FOB|y express theodolites cajole carefully | +580|174|5|2|31|33299.27|0.04|0.08|N|O|1997-10-04|1997-09-08|1997-10-15|COLLECT COD|FOB|ose alongside of the sl| +580|185|6|3|19|20618.42|0.04|0.04|N|O|1997-07-23|1997-09-21|1997-08-15|NONE|FOB|mong the special packag| +581|64|1|1|41|39526.46|0.09|0.07|N|O|1997-05-26|1997-04-06|1997-06-10|TAKE BACK RETURN|MAIL|nts. quickly| +581|93|5|2|14|13903.26|0.06|0.08|N|O|1997-05-17|1997-04-14|1997-06-08|NONE|MAIL|. deposits s| +581|101|6|3|49|49053.90|0.10|0.02|N|O|1997-02-27|1997-04-24|1997-03-10|TAKE BACK RETURN|MAIL|. slyly regular pinto beans acr| +581|75|4|4|30|29252.10|0.10|0.08|N|O|1997-06-19|1997-05-21|1997-06-22|TAKE BACK RETURN|TRUCK| regular ideas grow furio| +582|57|9|1|7|6699.35|0.07|0.00|N|O|1997-11-16|1997-11-29|1997-12-10|TAKE BACK RETURN|FOB|ithely unusual t| +582|51|2|2|49|46601.45|0.05|0.03|N|O|1997-12-17|1998-01-12|1997-12-31|COLLECT COD|REG AIR|nts according to the furiously regular pin| +582|141|4|3|42|43727.88|0.07|0.00|N|O|1997-11-15|1997-12-21|1997-12-03|COLLECT COD|SHIP|iously beside the silent de| +582|168|9|4|36|38453.76|0.06|0.01|N|O|1997-12-09|1997-11-27|1997-12-26|TAKE BACK RETURN|SHIP|lar requests. quickly | +583|145|6|1|1|1045.14|0.07|0.07|N|O|1997-06-17|1997-04-29|1997-06-28|NONE|TRUCK| regular, regular ideas. even, bra| +583|120|4|2|47|47945.64|0.10|0.06|N|O|1997-07-14|1997-05-12|1997-08-11|DELIVER IN PERSON|AIR|nts are fluffily. furiously even re| +583|130|5|3|34|35024.42|0.01|0.02|N|O|1997-05-11|1997-04-24|1997-06-03|DELIVER IN PERSON|MAIL|express req| +583|142|1|4|33|34390.62|0.10|0.01|N|O|1997-05-28|1997-04-25|1997-06-24|NONE|AIR|kages cajole slyly across the| +583|189|10|5|13|14159.34|0.04|0.06|N|O|1997-06-23|1997-05-29|1997-07-08|COLLECT COD|TRUCK|y sly theodolites. ironi| +608|154|6|1|19|20028.85|0.08|0.06|N|O|1996-04-19|1996-05-02|1996-05-03|DELIVER IN PERSON|RAIL|ideas. the| +608|198|1|2|40|43927.60|0.03|0.01|N|O|1996-05-21|1996-04-11|1996-06-02|NONE|AIR| alongside of the regular tithes. sly| +609|66|5|1|21|20287.26|0.01|0.05|R|F|1994-08-24|1994-08-23|1994-08-27|DELIVER IN PERSON|FOB|de of the special warthogs. excu| +610|111|8|1|49|49544.39|0.10|0.07|N|O|1995-08-29|1995-10-26|1995-09-12|TAKE BACK RETURN|SHIP|ular instruc| +610|68|3|2|11|10648.66|0.07|0.08|N|O|1995-10-31|1995-10-25|1995-11-18|NONE|MAIL|blithely final | +610|118|9|3|26|26470.86|0.09|0.04|N|O|1995-11-22|1995-09-09|1995-12-04|TAKE BACK RETURN|AIR|cross the furiously even theodolites sl| +610|186|7|4|17|18465.06|0.03|0.03|N|O|1995-11-01|1995-10-30|1995-11-04|COLLECT COD|FOB|p quickly instead of the slyly pending foxe| +610|146|7|5|39|40799.46|0.08|0.05|N|O|1995-10-30|1995-10-21|1995-11-11|TAKE BACK RETURN|REG AIR|counts. ironic warhorses are | +610|95|7|6|5|4975.45|0.00|0.07|N|O|1995-08-11|1995-10-22|1995-08-26|TAKE BACK RETURN|FOB|n pinto beans. iro| +610|190|1|7|27|29435.13|0.06|0.03|N|O|1995-09-02|1995-09-19|1995-09-15|NONE|REG AIR| ironic pinto beans haggle. blithe| +611|17|7|1|39|35763.39|0.05|0.06|R|F|1993-05-06|1993-04-09|1993-05-22|TAKE BACK RETURN|SHIP|nto beans | +611|81|2|2|1|981.08|0.08|0.07|R|F|1993-05-17|1993-02-26|1993-06-15|DELIVER IN PERSON|MAIL|ts. pending platelets aff| +611|120|4|3|39|39784.68|0.09|0.02|A|F|1993-03-10|1993-03-10|1993-03-17|TAKE BACK RETURN|TRUCK|the evenly bold requests. furious| +612|185|6|1|5|5425.90|0.07|0.00|R|F|1992-11-08|1992-11-20|1992-12-03|TAKE BACK RETURN|RAIL|structions. q| +612|195|7|2|28|30665.32|0.07|0.06|R|F|1993-01-02|1992-12-11|1993-01-30|DELIVER IN PERSON|TRUCK|regular instructions affix bl| +612|67|4|3|49|47385.94|0.00|0.08|A|F|1993-01-08|1992-11-25|1993-01-17|TAKE BACK RETURN|REG AIR|theodolite| +612|39|5|4|28|26292.84|0.05|0.00|A|F|1992-11-12|1992-12-05|1992-12-02|TAKE BACK RETURN|REG AIR|lyly regular asym| +612|88|9|5|1|988.08|0.08|0.04|R|F|1992-12-18|1992-12-13|1992-12-20|TAKE BACK RETURN|FOB| requests.| +612|189|10|6|33|35942.94|0.10|0.03|R|F|1992-11-30|1992-12-01|1992-12-12|COLLECT COD|MAIL|bove the blithely even ideas. careful| +613|91|5|1|17|16848.53|0.06|0.06|N|O|1995-09-23|1995-08-04|1995-10-15|NONE|SHIP|ar dependencie| +613|79|7|2|6|5874.42|0.05|0.05|N|O|1995-08-05|1995-08-09|1995-08-08|TAKE BACK RETURN|MAIL|y ironic deposits eat | +613|186|7|3|3|3258.54|0.03|0.01|N|O|1995-09-27|1995-09-11|1995-10-05|NONE|TRUCK|ccounts cajole. | +613|159|10|4|7|7414.05|0.02|0.04|N|O|1995-09-07|1995-08-02|1995-09-16|DELIVER IN PERSON|MAIL|ously blithely final pinto beans. regula| +614|195|8|1|21|22998.99|0.00|0.03|R|F|1993-03-29|1993-01-06|1993-04-16|TAKE BACK RETURN|TRUCK|arefully. slyly express packag| +614|187|8|2|48|52184.64|0.07|0.07|A|F|1993-03-09|1993-01-19|1993-03-19|DELIVER IN PERSON|SHIP|riously special excuses haggle along the| +614|167|2|3|43|45887.88|0.05|0.00|A|F|1993-03-07|1993-02-22|1993-03-18|DELIVER IN PERSON|SHIP| express accounts wake. slyly ironic ins| +614|147|6|4|14|14659.96|0.04|0.06|A|F|1992-12-03|1993-02-14|1992-12-27|DELIVER IN PERSON|SHIP|ular packages haggle about the pack| +614|196|8|5|30|32885.70|0.08|0.07|R|F|1993-01-16|1993-02-08|1993-02-12|TAKE BACK RETURN|FOB|tructions are f| +614|137|8|6|48|49782.24|0.04|0.08|A|F|1992-12-14|1993-01-22|1993-01-11|NONE|TRUCK| regular platelets cajole quickly eve| +615|105|6|1|36|36183.60|0.10|0.01|A|F|1992-06-01|1992-07-14|1992-06-27|NONE|FOB| packages. carefully final pinto bea| +640|93|7|1|49|48661.41|0.09|0.02|R|F|1993-03-27|1993-04-17|1993-04-15|NONE|RAIL|s haggle slyly| +640|1|4|2|40|36040.00|0.09|0.05|A|F|1993-05-11|1993-04-11|1993-05-15|COLLECT COD|TRUCK|oach according to the bol| +640|180|8|3|22|23763.96|0.05|0.07|A|F|1993-05-07|1993-04-14|1993-05-21|TAKE BACK RETURN|TRUCK|osits across the slyly regular theodo| +640|32|3|4|45|41941.35|0.07|0.07|R|F|1993-04-15|1993-04-23|1993-04-21|DELIVER IN PERSON|REG AIR|ong the qui| +641|126|9|1|18|18470.16|0.01|0.08|R|F|1993-10-17|1993-10-11|1993-10-29|DELIVER IN PERSON|AIR|p blithely bold packages. quick| +641|100|2|2|1|1000.10|0.09|0.02|R|F|1993-12-03|1993-10-28|1993-12-26|TAKE BACK RETURN|RAIL| nag across the regular foxes.| +641|95|7|3|40|39803.60|0.05|0.06|R|F|1993-11-22|1993-10-20|1993-12-11|DELIVER IN PERSON|REG AIR|lets. furiously regular requests cajo| +641|71|10|4|25|24276.75|0.03|0.02|A|F|1993-12-04|1993-11-18|1993-12-18|TAKE BACK RETURN|FOB|d, regular d| +641|4|9|5|41|37064.00|0.07|0.04|R|F|1993-11-29|1993-10-27|1993-12-04|TAKE BACK RETURN|FOB| asymptotes are quickly. bol| +642|54|5|1|26|24805.30|0.10|0.03|A|F|1994-04-16|1994-02-01|1994-04-27|COLLECT COD|REG AIR|quests according to the unu| +643|13|3|1|28|25564.28|0.00|0.08|A|F|1995-04-13|1995-05-12|1995-04-14|TAKE BACK RETURN|TRUCK|ly regular requests nag sly| +643|51|2|2|48|45650.40|0.01|0.02|N|O|1995-07-10|1995-06-07|1995-08-01|NONE|FOB|ly ironic accounts| +643|163|2|3|23|24452.68|0.05|0.03|N|O|1995-07-09|1995-05-18|1995-07-31|COLLECT COD|RAIL|sits are carefully according to the e| +643|45|4|4|39|36856.56|0.08|0.04|A|F|1995-06-08|1995-06-16|1995-06-13|COLLECT COD|RAIL| the pains. carefully s| +643|190|1|5|47|51238.93|0.10|0.03|R|F|1995-04-05|1995-06-14|1995-04-26|DELIVER IN PERSON|RAIL|y against | +644|134|10|1|46|47569.98|0.02|0.01|A|F|1992-05-20|1992-06-14|1992-06-14|DELIVER IN PERSON|RAIL| special requests was sometimes expre| +644|130|3|2|11|11331.43|0.05|0.02|A|F|1992-08-20|1992-07-21|1992-09-11|TAKE BACK RETURN|TRUCK|ealthy pinto beans use carefu| +644|101|6|3|44|44048.40|0.04|0.04|R|F|1992-08-17|1992-07-26|1992-08-20|COLLECT COD|REG AIR|iously ironic pinto beans. bold packa| +644|80|8|4|7|6860.56|0.01|0.02|A|F|1992-05-18|1992-07-01|1992-06-07|COLLECT COD|RAIL| regular requests are blithely. slyly| +644|50|1|5|23|21851.15|0.02|0.04|R|F|1992-07-31|1992-07-28|1992-08-13|DELIVER IN PERSON|TRUCK|uctions nag quickly alongside of t| +644|85|6|6|33|32507.64|0.00|0.07|R|F|1992-08-26|1992-07-27|1992-08-28|NONE|AIR|ages sleep. bold, bo| +644|51|9|7|38|36139.90|0.08|0.06|R|F|1992-05-17|1992-07-10|1992-06-06|TAKE BACK RETURN|MAIL| packages. blithely slow accounts nag quic| +645|160|8|1|33|34985.28|0.01|0.02|A|F|1994-12-09|1995-02-21|1995-01-03|NONE|TRUCK|heodolites b| +645|170|1|2|47|50297.99|0.07|0.05|R|F|1995-02-16|1995-02-15|1995-02-25|COLLECT COD|TRUCK|hely regular instructions alon| +645|70|7|3|46|44623.22|0.10|0.01|A|F|1995-01-04|1995-02-21|1995-01-21|COLLECT COD|REG AIR| regular dependencies across the speci| +645|96|9|4|49|48808.41|0.05|0.03|R|F|1995-01-24|1995-01-06|1995-02-17|NONE|TRUCK|y. slyly iron| +645|5|8|5|43|38915.00|0.06|0.02|A|F|1995-02-12|1995-02-27|1995-03-06|TAKE BACK RETURN|REG AIR| furiously accounts. slyly| +645|34|5|6|18|16812.54|0.10|0.08|A|F|1995-03-02|1995-02-08|1995-03-03|COLLECT COD|RAIL|ep. slyly even | +645|28|9|7|9|8352.18|0.03|0.03|A|F|1994-12-25|1995-01-04|1995-01-15|COLLECT COD|REG AIR|special deposits. regular, final th| +646|109|6|1|31|31282.10|0.00|0.05|R|F|1994-12-17|1995-02-16|1995-01-04|COLLECT COD|MAIL|ag furiousl| +646|127|8|2|1|1027.12|0.07|0.01|A|F|1994-12-05|1995-01-07|1994-12-31|TAKE BACK RETURN|MAIL|t blithely regular deposits. quic| +646|30|9|3|24|22320.72|0.06|0.02|A|F|1995-02-20|1994-12-30|1995-03-16|TAKE BACK RETURN|TRUCK|regular accounts haggle dog| +646|99|2|4|34|33969.06|0.01|0.00|R|F|1994-12-28|1994-12-27|1994-12-31|COLLECT COD|SHIP|slow accounts. fluffily idle instructions| +646|90|1|5|17|16831.53|0.04|0.01|A|F|1994-12-31|1994-12-26|1995-01-01|DELIVER IN PERSON|REG AIR|inal packages haggle carefully | +646|115|2|6|40|40604.40|0.10|0.01|R|F|1995-01-01|1995-01-13|1995-01-11|COLLECT COD|TRUCK|ronic packages sleep across th| +647|17|1|1|41|37597.41|0.08|0.08|N|O|1997-11-19|1997-09-24|1997-12-15|COLLECT COD|REG AIR|r instructions. quickly unusu| +647|113|10|2|5|5065.55|0.10|0.00|N|O|1997-09-25|1997-09-22|1997-10-25|TAKE BACK RETURN|AIR|ly express packages haggle caref| +647|153|8|3|15|15797.25|0.08|0.00|N|O|1997-09-23|1997-10-09|1997-10-21|NONE|MAIL|ve the even, bold foxes sleep | +672|173|4|1|41|43999.97|0.06|0.06|R|F|1994-06-20|1994-07-03|1994-06-22|COLLECT COD|REG AIR| dependencies in| +672|190|1|2|9|9811.71|0.03|0.04|R|F|1994-06-25|1994-06-06|1994-07-19|TAKE BACK RETURN|TRUCK|haggle carefully carefully reg| +672|143|2|3|35|36509.90|0.02|0.01|R|F|1994-07-13|1994-06-04|1994-07-14|COLLECT COD|RAIL| dependencies haggle quickly. theo| +673|71|10|1|22|21363.54|0.03|0.02|R|F|1994-03-15|1994-04-27|1994-03-29|TAKE BACK RETURN|TRUCK| the regular, even requests. carefully fin| +674|102|5|1|23|23048.30|0.06|0.07|A|F|1992-10-25|1992-10-15|1992-11-03|COLLECT COD|SHIP|ve the quickly even deposits. blithe| +674|59|4|2|4|3836.20|0.02|0.07|R|F|1992-10-05|1992-11-22|1992-10-22|NONE|RAIL|ly express pinto beans sleep car| +675|157|9|1|1|1057.15|0.04|0.08|N|O|1997-11-27|1997-09-30|1997-12-12|DELIVER IN PERSON|REG AIR|ide of the slyly regular packages. unus| +675|137|3|2|35|36299.55|0.08|0.07|N|O|1997-08-19|1997-10-16|1997-09-17|DELIVER IN PERSON|REG AIR|s. furiously expre| +675|176|6|3|34|36589.78|0.10|0.04|N|O|1997-11-17|1997-10-07|1997-11-27|NONE|FOB|y final accounts unwind around the | +675|100|4|4|15|15001.50|0.09|0.05|N|O|1997-10-18|1997-09-28|1997-11-13|COLLECT COD|TRUCK|posits after the furio| +675|5|8|5|46|41630.00|0.09|0.05|N|O|1997-09-18|1997-10-14|1997-10-01|DELIVER IN PERSON|AIR| deposits along the express foxes | +676|51|3|1|9|8559.45|0.09|0.02|N|O|1997-04-03|1997-02-02|1997-04-08|COLLECT COD|REG AIR|aintain sl| +676|78|6|2|20|19561.40|0.07|0.07|N|O|1997-02-02|1997-02-01|1997-02-11|NONE|REG AIR|riously around the blithely | +676|163|4|3|35|37210.60|0.05|0.01|N|O|1996-12-30|1997-01-13|1997-01-19|DELIVER IN PERSON|RAIL|into beans. blithe| +676|73|1|4|24|23353.68|0.01|0.06|N|O|1997-02-05|1997-01-16|1997-03-07|TAKE BACK RETURN|TRUCK|ress, regular dep| +676|166|1|5|31|33050.96|0.01|0.06|N|O|1997-02-06|1997-02-28|1997-03-08|COLLECT COD|TRUCK|ial deposits cajo| +676|76|7|6|33|32210.31|0.09|0.05|N|O|1997-03-02|1997-02-22|1997-03-19|TAKE BACK RETURN|TRUCK|as wake slyly furiously close pinto b| +676|143|6|7|11|11474.54|0.07|0.02|N|O|1997-03-09|1997-03-06|1997-03-31|TAKE BACK RETURN|MAIL|he final acco| +677|59|7|1|32|30689.60|0.04|0.08|R|F|1994-01-06|1994-01-31|1994-02-02|NONE|RAIL|slyly final| +677|168|9|2|39|41658.24|0.00|0.07|R|F|1993-12-19|1994-02-11|1994-01-05|TAKE BACK RETURN|SHIP|ges. furiously regular packages use | +677|24|5|3|46|42504.92|0.01|0.02|R|F|1993-12-02|1994-02-12|1993-12-06|COLLECT COD|RAIL|ng theodolites. furiously unusual theodo| +677|148|7|4|1|1048.14|0.06|0.05|R|F|1993-12-01|1994-01-14|1993-12-26|DELIVER IN PERSON|MAIL|ly. regular | +677|150|9|5|25|26253.75|0.00|0.05|A|F|1994-03-12|1994-02-02|1994-03-28|DELIVER IN PERSON|AIR| packages integrate blithely| +678|146|7|1|20|20922.80|0.05|0.08|R|F|1993-06-21|1993-04-07|1993-07-10|TAKE BACK RETURN|MAIL|furiously express excuses. foxes eat fu| +678|37|3|2|22|20614.66|0.01|0.02|A|F|1993-05-10|1993-04-29|1993-06-08|NONE|REG AIR|de of the carefully even requests. bl| +678|143|10|3|16|16690.24|0.06|0.02|R|F|1993-03-20|1993-04-13|1993-04-16|DELIVER IN PERSON|REG AIR|equests cajole around the carefully regular| +678|199|10|4|48|52761.12|0.08|0.08|R|F|1993-02-28|1993-04-04|1993-03-24|NONE|REG AIR|ithely. slyly express foxes| +678|98|9|5|16|15969.44|0.06|0.04|R|F|1993-03-09|1993-04-18|1993-04-07|NONE|AIR| about the | +678|43|4|6|11|10373.44|0.09|0.00|R|F|1993-04-28|1993-05-16|1993-05-11|COLLECT COD|TRUCK|ess deposits dazzle f| +679|192|3|1|9|9829.71|0.09|0.00|N|O|1995-12-20|1996-01-27|1996-01-07|COLLECT COD|REG AIR|leep slyly. entici| +704|190|1|1|40|43607.60|0.05|0.05|N|O|1997-01-30|1997-01-10|1997-02-20|COLLECT COD|AIR|ggle quickly. r| +704|4|5|2|14|12656.00|0.07|0.08|N|O|1997-02-02|1996-12-26|1997-02-19|DELIVER IN PERSON|REG AIR|ve the quickly final forges. furiously p| +705|189|10|1|46|50102.28|0.05|0.06|N|O|1997-04-18|1997-05-06|1997-05-05|DELIVER IN PERSON|SHIP|ss deposits. ironic packa| +705|117|7|2|35|35598.85|0.10|0.04|N|O|1997-03-25|1997-03-20|1997-04-23|TAKE BACK RETURN|FOB|carefully ironic accounts| +706|197|9|1|23|25235.37|0.05|0.00|N|O|1995-12-06|1995-12-02|1995-12-16|COLLECT COD|SHIP|ckey players. requests above the| +707|155|6|1|34|35875.10|0.01|0.02|R|F|1994-12-08|1995-01-15|1995-01-02|NONE|RAIL| dependencies| +707|43|10|2|22|20746.88|0.00|0.06|A|F|1995-01-12|1994-12-28|1995-01-16|DELIVER IN PERSON|REG AIR| kindle ironically| +708|124|7|1|3|3072.36|0.05|0.02|N|O|1998-10-09|1998-09-22|1998-11-07|COLLECT COD|FOB|e slyly pending foxes. | +708|180|1|2|19|20523.42|0.06|0.00|N|O|1998-10-28|1998-09-23|1998-11-25|COLLECT COD|SHIP| requests. even, thin ideas| +708|122|5|3|33|33729.96|0.09|0.06|N|O|1998-09-10|1998-09-20|1998-09-22|COLLECT COD|RAIL|s boost carefully ruthless theodolites. f| +708|56|7|4|5|4780.25|0.07|0.07|N|O|1998-07-22|1998-08-15|1998-07-28|TAKE BACK RETURN|REG AIR|c pinto beans nag after the account| +708|143|2|5|36|37553.04|0.08|0.01|N|O|1998-07-16|1998-09-04|1998-08-11|NONE|SHIP|ests. even, regular hockey p| +708|23|6|6|7|6461.14|0.10|0.03|N|O|1998-08-16|1998-08-15|1998-09-10|COLLECT COD|REG AIR|lly express ac| +709|87|8|1|7|6909.56|0.00|0.00|N|O|1998-06-14|1998-06-08|1998-06-18|TAKE BACK RETURN|RAIL| special orbits cajole | +709|198|10|2|15|16472.85|0.08|0.00|N|O|1998-07-10|1998-06-26|1998-08-09|NONE|RAIL|ily regular deposits. sauternes was accor| +709|169|8|3|10|10691.60|0.01|0.02|N|O|1998-06-04|1998-06-30|1998-06-11|NONE|REG AIR|ts cajole boldly | +709|108|9|4|40|40324.00|0.10|0.08|N|O|1998-08-12|1998-06-20|1998-08-20|DELIVER IN PERSON|RAIL|ggle fluffily carefully ironic| +710|163|8|1|47|49968.52|0.06|0.08|A|F|1993-01-18|1993-03-24|1993-01-24|TAKE BACK RETURN|MAIL|usual ideas into th| +710|193|4|2|38|41541.22|0.07|0.02|R|F|1993-04-18|1993-03-12|1993-05-15|COLLECT COD|FOB|sts boost fluffily aft| +710|139|5|3|7|7273.91|0.04|0.06|R|F|1993-01-20|1993-03-28|1993-02-15|TAKE BACK RETURN|REG AIR|xpress, special ideas. bl| +710|90|1|4|25|24752.25|0.00|0.05|R|F|1993-03-31|1993-02-05|1993-04-22|COLLECT COD|FOB|eas detect do| +710|186|7|5|12|13034.16|0.01|0.02|A|F|1993-02-18|1993-02-27|1993-03-07|DELIVER IN PERSON|MAIL|ions. slyly express theodolites al| +710|114|4|6|21|21296.31|0.04|0.06|R|F|1993-03-22|1993-03-05|1993-03-27|DELIVER IN PERSON|SHIP|es. furiously p| +710|160|2|7|46|48767.36|0.03|0.07|R|F|1993-04-16|1993-03-27|1993-05-05|COLLECT COD|MAIL|ges use; blithely pending excuses inte| +711|146|3|1|2|2092.28|0.10|0.04|R|F|1993-12-01|1993-12-09|1993-12-16|DELIVER IN PERSON|REG AIR|ely across t| +711|103|8|2|27|27083.70|0.00|0.08|A|F|1993-10-02|1993-10-26|1993-10-08|DELIVER IN PERSON|MAIL|slyly. ironic asy| +711|128|7|3|46|47293.52|0.10|0.00|R|F|1993-12-26|1993-11-19|1994-01-21|TAKE BACK RETURN|MAIL|deposits. permanen| +711|128|9|4|20|20562.40|0.09|0.00|A|F|1994-01-17|1993-11-10|1994-01-31|DELIVER IN PERSON|TRUCK|kly regular acco| +736|158|9|1|46|48674.90|0.05|0.01|N|O|1998-07-16|1998-09-01|1998-08-09|NONE|AIR|uctions cajole| +736|80|1|2|23|22541.84|0.02|0.05|N|O|1998-10-08|1998-08-27|1998-10-19|TAKE BACK RETURN|AIR|k accounts are carefully| +736|57|9|3|13|12441.65|0.00|0.03|N|O|1998-08-16|1998-07-26|1998-08-19|DELIVER IN PERSON|FOB|st furiously among the | +736|98|2|4|14|13973.26|0.06|0.04|N|O|1998-10-04|1998-08-14|1998-10-16|COLLECT COD|REG AIR|nstructions.| +736|169|6|5|32|34213.12|0.04|0.03|N|O|1998-07-30|1998-08-22|1998-08-12|DELIVER IN PERSON|RAIL|iously final accoun| +737|182|3|1|12|12986.16|0.01|0.01|R|F|1992-04-28|1992-06-30|1992-05-08|COLLECT COD|RAIL|posits after the slyly bold du| +738|198|1|1|34|37338.46|0.00|0.06|R|F|1993-06-09|1993-04-15|1993-07-09|TAKE BACK RETURN|TRUCK|s against the ironic exc| +738|188|9|2|4|4352.72|0.00|0.03|A|F|1993-06-20|1993-04-08|1993-07-09|NONE|AIR|ar packages. fluffily bo| +738|170|1|3|23|24613.91|0.04|0.08|A|F|1993-03-17|1993-04-02|1993-04-05|TAKE BACK RETURN|SHIP|nic, final excuses promise quickly regula| +738|141|10|4|12|12493.68|0.04|0.08|A|F|1993-06-16|1993-05-05|1993-06-22|NONE|SHIP|ove the slyly regular p| +738|175|4|5|30|32255.10|0.02|0.00|A|F|1993-06-12|1993-05-29|1993-06-25|NONE|AIR|ecial instructions haggle blithely regula| +739|85|6|1|28|27582.24|0.00|0.03|N|O|1998-06-03|1998-08-04|1998-06-29|TAKE BACK RETURN|RAIL|elets about the pe| +739|4|7|2|50|45200.00|0.07|0.06|N|O|1998-08-26|1998-07-16|1998-09-02|COLLECT COD|MAIL|ndencies. blith| +739|49|2|3|12|11388.48|0.05|0.00|N|O|1998-08-20|1998-07-24|1998-08-22|NONE|MAIL|le slyly along the close i| +739|44|3|4|47|44369.88|0.09|0.07|N|O|1998-08-12|1998-07-09|1998-08-28|NONE|REG AIR|deas according to the theodolites sn| +739|188|9|5|30|32645.40|0.07|0.06|N|O|1998-06-19|1998-08-26|1998-07-02|DELIVER IN PERSON|REG AIR|above the even deposits. ironic requests| +740|2|9|1|22|19844.00|0.10|0.02|N|O|1995-07-24|1995-09-11|1995-08-11|TAKE BACK RETURN|FOB|odolites cajole ironic, pending instruc| +740|66|1|2|35|33812.10|0.00|0.00|N|O|1995-09-06|1995-08-22|1995-10-02|NONE|TRUCK|p quickly. fu| +740|199|10|3|29|31876.51|0.06|0.05|N|O|1995-10-26|1995-09-17|1995-10-29|DELIVER IN PERSON|FOB|ntly bold pinto beans sleep quickl| +741|187|8|1|25|27179.50|0.03|0.06|N|O|1998-07-15|1998-08-27|1998-08-12|DELIVER IN PERSON|MAIL|accounts. blithely bold pa| +741|91|4|2|22|21803.98|0.09|0.01|N|O|1998-09-07|1998-09-28|1998-09-12|COLLECT COD|AIR|ven deposits about the regular, ironi| +742|102|3|1|46|46096.60|0.04|0.08|A|F|1995-03-12|1995-03-20|1995-03-16|TAKE BACK RETURN|SHIP|e slyly bold deposits cajole according to| +742|96|8|2|15|14941.35|0.08|0.05|A|F|1995-02-26|1995-03-20|1995-03-03|NONE|SHIP|blithely unusual pinto| +742|102|5|3|24|24050.40|0.08|0.08|A|F|1995-02-12|1995-03-12|1995-02-14|DELIVER IN PERSON|SHIP|affix slyly. furiously i| +742|192|4|4|16|17475.04|0.01|0.05|A|F|1995-01-15|1995-02-25|1995-01-24|COLLECT COD|AIR|eodolites haggle carefully regul| +742|101|4|5|48|48052.80|0.09|0.08|R|F|1995-03-24|1995-01-23|1995-04-08|TAKE BACK RETURN|TRUCK| platelets | +742|192|6|6|49|53517.31|0.02|0.07|A|F|1995-01-13|1995-02-13|1995-01-26|TAKE BACK RETURN|RAIL| carefully bold foxes sle| +743|192|5|1|21|22935.99|0.01|0.04|N|O|1996-10-26|1996-11-05|1996-11-11|COLLECT COD|MAIL|d requests. packages afte| +768|196|7|1|39|42751.41|0.06|0.08|N|O|1996-09-25|1996-10-27|1996-10-20|NONE|SHIP|out the ironic| +768|18|9|2|2|1836.02|0.00|0.04|N|O|1996-11-13|1996-10-03|1996-11-25|DELIVER IN PERSON|SHIP|ular courts. slyly dogged accou| +768|6|1|3|30|27180.00|0.06|0.05|N|O|1996-09-22|1996-11-03|1996-10-13|NONE|MAIL| furiously fluffy pinto beans haggle along| +768|25|8|4|37|34225.74|0.10|0.00|N|O|1996-10-02|1996-09-23|1996-10-14|TAKE BACK RETURN|REG AIR|ending requests across the quickly| +768|47|10|5|47|44510.88|0.06|0.05|N|O|1996-11-28|1996-10-30|1996-12-12|NONE|TRUCK|foxes. slyly ironic deposits a| +768|112|9|6|43|43520.73|0.10|0.06|N|O|1996-09-22|1996-11-03|1996-10-22|TAKE BACK RETURN|AIR|sual ideas wake quickly| +768|49|10|7|33|31318.32|0.01|0.04|N|O|1996-09-06|1996-09-29|1996-10-01|COLLECT COD|RAIL|sly ironic instructions. excuses can hagg| +769|176|6|1|36|38742.12|0.02|0.02|A|F|1993-10-01|1993-08-07|1993-10-15|NONE|AIR|es. furiously iro| +769|160|8|2|4|4240.64|0.01|0.04|R|F|1993-06-25|1993-08-12|1993-07-15|DELIVER IN PERSON|FOB| ideas. even| +770|181|2|1|39|42166.02|0.09|0.06|N|O|1998-07-19|1998-08-09|1998-08-04|NONE|REG AIR|osits. foxes cajole | +770|54|2|2|25|23851.25|0.03|0.02|N|O|1998-05-26|1998-07-23|1998-06-04|TAKE BACK RETURN|AIR| deposits dazzle fluffily alongside of | +771|7|4|1|12|10884.00|0.10|0.08|N|O|1995-07-18|1995-08-02|1995-08-07|COLLECT COD|TRUCK|carefully. pending in| +771|161|10|2|38|40324.08|0.03|0.08|N|O|1995-07-22|1995-09-10|1995-07-29|TAKE BACK RETURN|REG AIR| quickly final requests are final packages.| +771|7|8|3|14|12698.00|0.02|0.05|N|O|1995-07-31|1995-08-13|1995-08-07|DELIVER IN PERSON|AIR|r, final packages are slyly iro| +771|42|3|4|7|6594.28|0.06|0.02|N|O|1995-06-18|1995-08-31|1995-06-20|NONE|REG AIR|theodolites after the fluffily express | +771|78|6|5|13|12714.91|0.09|0.01|N|O|1995-08-10|1995-08-21|1995-08-30|NONE|FOB|packages affix slyly about the quickly | +771|82|3|6|23|22587.84|0.08|0.03|N|O|1995-06-19|1995-09-07|1995-07-09|COLLECT COD|FOB|cajole besides the quickly ironic pin| +772|53|5|1|35|33356.75|0.10|0.06|R|F|1993-07-05|1993-06-05|1993-08-02|NONE|SHIP|kly thin packages wake slowly| +772|84|5|2|10|9840.80|0.05|0.01|R|F|1993-05-20|1993-05-19|1993-06-15|DELIVER IN PERSON|MAIL| deposits cajole carefully instructions. t| +772|86|7|3|35|34512.80|0.03|0.04|R|F|1993-04-18|1993-06-13|1993-05-01|COLLECT COD|MAIL|ng ideas. special packages haggle alon| +772|180|8|4|10|10801.80|0.08|0.02|A|F|1993-05-17|1993-06-09|1993-05-29|COLLECT COD|AIR|o the furiously final deposits. furi| +772|54|5|5|42|40070.10|0.02|0.07|A|F|1993-06-09|1993-07-16|1993-06-12|DELIVER IN PERSON|MAIL| express foxes abo| +773|100|1|1|5|5000.50|0.06|0.04|A|F|1993-11-21|1993-12-19|1993-12-21|COLLECT COD|MAIL|ar requests. regular, thin packages u| +773|11|5|2|31|28241.31|0.02|0.06|A|F|1993-12-30|1993-11-02|1994-01-01|TAKE BACK RETURN|MAIL|e slyly unusual deposit| +773|151|3|3|39|40994.85|0.06|0.05|A|F|1994-01-04|1993-12-23|1994-01-26|DELIVER IN PERSON|FOB|quickly eve| +773|29|8|4|28|26012.56|0.10|0.06|R|F|1994-01-19|1993-11-05|1994-01-23|NONE|TRUCK|he furiously slow deposits.| +773|134|5|5|9|9307.17|0.09|0.02|R|F|1993-10-09|1993-12-25|1993-11-04|TAKE BACK RETURN|FOB|ent orbits haggle fluffily after the | +773|40|1|6|43|40421.72|0.07|0.03|A|F|1993-11-06|1993-11-20|1993-11-08|TAKE BACK RETURN|SHIP|furiously bold dependencies. blithel| +774|183|4|1|49|53075.82|0.08|0.03|N|O|1995-12-06|1996-01-07|1995-12-14|DELIVER IN PERSON|SHIP|ess accounts are carefully | +774|17|4|2|3|2751.03|0.10|0.06|N|O|1996-02-13|1996-01-14|1996-03-04|COLLECT COD|FOB| slyly even courts nag blith| +774|148|7|3|34|35636.76|0.02|0.07|N|O|1996-03-16|1996-01-03|1996-03-22|COLLECT COD|FOB|lar excuses are furiously final instr| +774|15|6|4|8|7320.08|0.00|0.02|N|O|1996-01-24|1996-01-15|1996-02-13|COLLECT COD|RAIL|ully ironic requests c| +774|177|5|5|44|47395.48|0.09|0.07|N|O|1996-02-29|1996-01-16|1996-03-06|NONE|REG AIR|s according to the deposits unwind ca| +774|120|1|6|2|2040.24|0.07|0.03|N|O|1995-12-11|1996-02-10|1995-12-14|TAKE BACK RETURN|SHIP|accounts; slyly regular| +775|32|3|1|16|14912.48|0.10|0.06|N|F|1995-05-23|1995-05-07|1995-06-19|NONE|TRUCK|un quickly slyly| +775|174|2|2|21|22557.57|0.01|0.06|R|F|1995-05-01|1995-06-02|1995-05-13|DELIVER IN PERSON|FOB| quickly sile| +775|108|5|3|20|20162.00|0.01|0.08|N|F|1995-06-17|1995-05-22|1995-07-13|COLLECT COD|AIR|en dependencies nag slowly | +800|72|1|1|38|36938.66|0.00|0.05|N|O|1998-07-21|1998-09-25|1998-08-07|TAKE BACK RETURN|TRUCK|according to the bold, final dependencies | +800|85|6|2|21|20686.68|0.04|0.05|N|O|1998-07-23|1998-10-01|1998-08-20|TAKE BACK RETURN|RAIL|ckly even requests after the carefully r| +800|176|5|3|26|27980.42|0.01|0.02|N|O|1998-07-23|1998-10-08|1998-07-25|DELIVER IN PERSON|FOB|bove the pending requests.| +801|6|3|1|13|11778.00|0.10|0.02|R|F|1992-04-25|1992-04-24|1992-05-16|COLLECT COD|RAIL|s are fluffily stealthily expres| +801|95|8|2|21|20896.89|0.05|0.02|A|F|1992-03-14|1992-04-01|1992-04-05|COLLECT COD|AIR|wake silently furiously idle deposits. | +801|3|4|3|21|18963.00|0.05|0.03|A|F|1992-04-25|1992-03-20|1992-05-04|COLLECT COD|REG AIR|cial, special packages.| +801|164|9|4|12|12769.92|0.08|0.04|A|F|1992-06-06|1992-04-14|1992-06-12|TAKE BACK RETURN|RAIL|s. ironic pinto b| +801|74|2|5|45|43833.15|0.01|0.06|R|F|1992-03-22|1992-03-22|1992-03-25|COLLECT COD|REG AIR| even asymptotes| +801|122|7|6|10|10221.20|0.08|0.01|A|F|1992-06-05|1992-05-15|1992-06-21|DELIVER IN PERSON|MAIL|al accounts. carefully regular foxes wake| +801|26|5|7|11|10186.22|0.01|0.03|A|F|1992-05-09|1992-04-19|1992-05-15|DELIVER IN PERSON|REG AIR|y special pinto beans cajole | +802|143|6|1|40|41725.60|0.08|0.08|A|F|1995-01-07|1995-04-03|1995-01-23|DELIVER IN PERSON|RAIL|y bold accou| +802|133|4|2|34|35126.42|0.08|0.06|A|F|1995-03-01|1995-03-15|1995-03-12|COLLECT COD|AIR|instructions cajole carefully. quietl| +802|131|2|3|44|45369.72|0.07|0.04|R|F|1995-01-09|1995-02-04|1995-01-18|TAKE BACK RETURN|SHIP|rmanently idly special requ| +802|157|2|4|18|19028.70|0.09|0.02|R|F|1995-03-06|1995-02-07|1995-03-19|TAKE BACK RETURN|RAIL|y regular requests engage furiously final d| +802|132|3|5|19|19610.47|0.08|0.06|A|F|1995-04-01|1995-02-20|1995-04-23|DELIVER IN PERSON|REG AIR|old, furious| +803|54|9|1|8|7632.40|0.07|0.01|N|O|1997-08-04|1997-06-19|1997-08-12|NONE|SHIP|ronic theodo| +803|99|10|2|21|20980.89|0.08|0.06|N|O|1997-08-25|1997-06-30|1997-09-10|TAKE BACK RETURN|AIR|ironic packages cajole slyly. un| +804|126|7|1|30|30783.60|0.08|0.04|A|F|1993-03-29|1993-05-07|1993-04-14|COLLECT COD|REG AIR|ehind the quietly regular pac| +804|199|3|2|2|2198.38|0.02|0.00|A|F|1993-06-23|1993-04-30|1993-06-25|NONE|TRUCK|slyly silent | +804|76|5|3|44|42947.08|0.04|0.05|R|F|1993-07-06|1993-04-13|1993-07-28|DELIVER IN PERSON|TRUCK|ly final deposits? special | +804|38|9|4|21|19698.63|0.01|0.00|A|F|1993-04-12|1993-06-06|1993-04-20|DELIVER IN PERSON|RAIL|ular, ironic foxes. quickly even accounts| +805|198|10|1|25|27454.75|0.07|0.06|N|O|1995-08-05|1995-09-30|1995-08-06|NONE|AIR|ide of the pending, sly requests. quickly f| +805|57|5|2|29|27754.45|0.07|0.01|N|O|1995-08-24|1995-08-15|1995-09-16|TAKE BACK RETURN|AIR|dolites according to the slyly f| +805|47|8|3|12|11364.48|0.01|0.06|N|O|1995-07-13|1995-09-27|1995-08-02|TAKE BACK RETURN|REG AIR| regular foxes. furio| +805|76|6|4|26|25377.82|0.08|0.07|N|O|1995-08-28|1995-09-24|1995-09-11|TAKE BACK RETURN|RAIL|. ironic deposits sleep across | +806|105|2|1|1|1005.10|0.04|0.07|N|O|1996-07-14|1996-09-12|1996-07-25|COLLECT COD|RAIL|ar accounts? pending, pending foxes a| +806|160|5|2|22|23323.52|0.08|0.02|N|O|1996-10-03|1996-08-11|1996-10-20|DELIVER IN PERSON|REG AIR|fily pending | +806|91|3|3|4|3964.36|0.04|0.03|N|O|1996-08-09|1996-09-18|1996-08-13|COLLECT COD|TRUCK|eans. quickly ironic ideas | +807|117|7|1|49|49838.39|0.00|0.00|R|F|1993-12-05|1994-01-13|1993-12-25|COLLECT COD|REG AIR| furiously according to the un| +807|155|10|2|49|51702.35|0.01|0.06|A|F|1994-01-17|1994-01-24|1994-01-22|COLLECT COD|TRUCK|y regular requests haggle.| +807|181|2|3|48|51896.64|0.07|0.07|A|F|1994-01-08|1994-02-02|1994-01-15|DELIVER IN PERSON|SHIP|kly across the f| +807|80|1|4|10|9800.80|0.09|0.00|R|F|1994-01-19|1994-02-12|1994-01-28|NONE|TRUCK|furiously final depths sleep a| +807|143|6|5|30|31294.20|0.02|0.01|R|F|1994-01-19|1994-01-09|1994-01-27|NONE|RAIL|cial accoun| +807|12|2|6|11|10032.11|0.02|0.04|R|F|1994-03-25|1994-01-26|1994-04-14|NONE|FOB|unts above the slyly final ex| +807|1|6|7|19|17119.00|0.08|0.05|A|F|1994-02-10|1994-02-20|1994-03-06|NONE|SHIP|ns haggle quickly across the furi| +832|103|6|1|45|45139.50|0.01|0.02|A|F|1992-05-08|1992-06-06|1992-06-04|COLLECT COD|MAIL|foxes engage slyly alon| +832|48|1|2|24|22752.96|0.05|0.06|A|F|1992-06-15|1992-07-14|1992-06-17|NONE|TRUCK|ully. carefully speci| +833|54|5|1|1|954.05|0.04|0.04|R|F|1994-04-26|1994-04-05|1994-04-29|COLLECT COD|MAIL|ffily ironic theodolites| +833|112|6|2|38|38460.18|0.05|0.05|A|F|1994-04-05|1994-04-21|1994-05-01|COLLECT COD|TRUCK| platelets promise furiously. | +833|162|7|3|9|9559.44|0.05|0.07|A|F|1994-02-28|1994-04-26|1994-03-20|TAKE BACK RETURN|FOB|ecial, even requests. even, bold instructi| +834|145|2|1|36|37625.04|0.06|0.04|R|F|1994-06-28|1994-07-25|1994-07-07|TAKE BACK RETURN|SHIP|ccounts haggle after the furiously | +834|7|2|2|11|9977.00|0.03|0.00|A|F|1994-09-18|1994-08-03|1994-10-02|DELIVER IN PERSON|TRUCK|inst the regular packa| +835|107|2|1|33|33234.30|0.09|0.06|N|O|1995-11-01|1995-12-02|1995-11-24|DELIVER IN PERSON|RAIL|onic instructions among the carefully iro| +835|185|6|2|28|30385.04|0.02|0.02|N|O|1995-12-27|1995-12-11|1996-01-21|NONE|SHIP| fluffily furious pinto beans| +836|188|9|1|6|6529.08|0.09|0.03|N|O|1996-12-09|1997-01-31|1996-12-29|COLLECT COD|TRUCK|fully bold theodolites are daringly across| +836|84|5|2|18|17713.44|0.03|0.05|N|O|1997-02-27|1997-02-11|1997-03-22|NONE|REG AIR|y pending packages use alon| +836|141|8|3|46|47892.44|0.05|0.07|N|O|1997-03-21|1997-02-06|1997-04-05|NONE|REG AIR|boldly final pinto beans haggle furiously| +837|57|5|1|39|37324.95|0.03|0.08|A|F|1994-07-22|1994-08-10|1994-08-11|NONE|RAIL|ecial pinto bea| +837|88|9|2|24|23713.92|0.08|0.00|R|F|1994-06-27|1994-09-02|1994-07-27|DELIVER IN PERSON|FOB|p carefully. theodolites use. bold courts a| +838|134|10|1|20|20682.60|0.10|0.07|N|O|1998-04-11|1998-03-25|1998-04-19|COLLECT COD|TRUCK| furiously final ideas. slow, bold | +838|29|10|2|27|25083.54|0.05|0.07|N|O|1998-02-15|1998-04-03|1998-02-20|DELIVER IN PERSON|SHIP| pending pinto beans haggle about t| +838|95|7|3|23|22887.07|0.10|0.07|N|O|1998-03-26|1998-04-17|1998-04-02|COLLECT COD|AIR|ets haggle furiously furiously regular r| +838|44|5|4|18|16992.72|0.09|0.00|N|O|1998-03-28|1998-04-06|1998-03-31|TAKE BACK RETURN|AIR|hely unusual foxes. furio| +839|158|10|1|23|24337.45|0.07|0.02|N|O|1995-10-17|1995-11-03|1995-11-04|COLLECT COD|AIR|ng ideas haggle accord| +839|189|10|2|47|51191.46|0.08|0.00|N|O|1995-10-17|1995-11-06|1995-11-10|NONE|AIR|refully final excuses about | +864|130|5|1|34|35024.42|0.03|0.04|N|O|1997-12-16|1997-10-23|1998-01-12|TAKE BACK RETURN|SHIP|gside of the furiously special| +864|98|1|2|7|6986.63|0.01|0.02|N|O|1997-11-13|1997-10-07|1997-12-13|TAKE BACK RETURN|MAIL|ven requests should sleep along | +864|80|10|3|34|33322.72|0.03|0.00|N|O|1997-09-14|1997-11-04|1997-09-21|TAKE BACK RETURN|REG AIR|to the furiously ironic platelets! | +865|198|10|1|16|17571.04|0.07|0.03|R|F|1993-08-24|1993-06-26|1993-08-28|TAKE BACK RETURN|TRUCK|y even accounts. quickly bold decoys| +865|20|7|2|3|2760.06|0.02|0.05|A|F|1993-07-17|1993-07-14|1993-08-01|NONE|MAIL|fully regular the| +865|87|8|3|15|14806.20|0.00|0.06|R|F|1993-07-05|1993-06-25|1993-07-26|NONE|SHIP| deposits sleep quickl| +865|169|4|4|34|36351.44|0.09|0.06|A|F|1993-05-09|1993-07-28|1993-05-18|DELIVER IN PERSON|REG AIR|furiously fluffily unusual account| +866|136|7|1|5|5180.65|0.08|0.00|R|F|1993-01-22|1993-01-14|1993-02-07|TAKE BACK RETURN|AIR|tegrate fluffily. carefully f| +867|139|10|1|7|7273.91|0.04|0.07|A|F|1994-02-19|1993-12-25|1994-02-25|DELIVER IN PERSON|TRUCK|pendencies-- slyly unusual packages hagg| +868|168|9|1|8|8545.28|0.06|0.03|R|F|1992-10-07|1992-08-01|1992-10-16|NONE|MAIL|l deposits. blithely regular pint| +868|29|8|2|13|12077.26|0.05|0.07|R|F|1992-07-25|1992-08-26|1992-08-04|NONE|AIR|gged instructi| +868|68|5|3|19|18393.14|0.09|0.06|R|F|1992-06-22|1992-08-27|1992-07-04|COLLECT COD|SHIP|lyly ironic platelets wake. rut| +868|122|1|4|43|43951.16|0.02|0.04|A|F|1992-07-02|1992-07-22|1992-07-21|COLLECT COD|SHIP|kly silent deposits wake dar| +868|25|8|5|27|24975.54|0.04|0.01|R|F|1992-08-01|1992-08-25|1992-08-12|TAKE BACK RETURN|RAIL|oss the fluffily unusual pinto | +868|125|6|6|19|19477.28|0.02|0.05|R|F|1992-09-20|1992-07-18|1992-10-04|NONE|FOB|ely even deposits lose blithe| +869|63|2|1|27|26002.62|0.07|0.07|N|O|1997-01-30|1997-02-17|1997-02-26|TAKE BACK RETURN|TRUCK|uffily even excuses? slyly even deposits | +869|47|4|2|36|34093.44|0.04|0.01|N|O|1997-05-03|1997-03-17|1997-05-24|NONE|RAIL|ong the furiously bold instructi| +870|50|9|1|36|34201.80|0.04|0.07|A|F|1993-10-18|1993-09-16|1993-11-15|COLLECT COD|MAIL|fily. furiously final accounts are | +870|186|7|2|5|5430.90|0.06|0.05|A|F|1993-08-13|1993-09-11|1993-08-24|COLLECT COD|FOB|e slyly excuses. ironi| +871|97|8|1|48|47860.32|0.10|0.03|N|O|1996-02-25|1996-02-09|1996-03-18|NONE|AIR|coys dazzle slyly slow notornis. f| +871|55|10|2|47|44887.35|0.07|0.03|N|O|1995-12-25|1996-02-01|1996-01-24|TAKE BACK RETURN|RAIL|ss, final dep| +871|108|5|3|13|13105.30|0.09|0.01|N|O|1996-01-25|1996-01-24|1996-02-03|NONE|REG AIR| haggle furiou| +871|190|1|4|29|31615.51|0.06|0.07|N|O|1995-11-16|1996-01-27|1995-12-16|DELIVER IN PERSON|RAIL|ests are carefu| +871|128|7|5|8|8224.96|0.00|0.01|N|O|1995-11-25|1996-01-12|1995-12-12|DELIVER IN PERSON|AIR|lar ideas-- slyly even accou| +871|143|2|6|26|27121.64|0.00|0.06|N|O|1996-02-07|1996-01-05|1996-02-25|COLLECT COD|AIR|symptotes use quickly near the | +871|174|3|7|4|4296.68|0.00|0.07|N|O|1996-03-09|1996-01-20|1996-03-26|COLLECT COD|FOB|l, regular dependencies w| +896|39|10|1|47|44134.41|0.07|0.08|R|F|1993-05-28|1993-05-15|1993-06-15|DELIVER IN PERSON|TRUCK|ly even pinto beans integrate. b| +896|198|2|2|10|10981.90|0.03|0.07|A|F|1993-07-07|1993-06-03|1993-07-24|COLLECT COD|SHIP| quickly even theodolites. carefully regu| +896|2|9|3|7|6314.00|0.09|0.02|A|F|1993-05-02|1993-05-24|1993-05-31|DELIVER IN PERSON|MAIL| requests | +896|152|3|4|11|11573.65|0.08|0.04|A|F|1993-05-19|1993-05-22|1993-06-08|COLLECT COD|MAIL|the multipliers sleep| +896|188|9|5|34|36998.12|0.00|0.05|R|F|1993-05-21|1993-06-01|1993-05-23|NONE|TRUCK|ular, close requests cajo| +896|177|6|6|44|47395.48|0.09|0.08|R|F|1993-05-19|1993-04-14|1993-06-02|DELIVER IN PERSON|FOB|lar, pending packages. deposits are q| +896|109|2|7|11|11100.10|0.01|0.07|A|F|1993-05-01|1993-04-09|1993-05-06|TAKE BACK RETURN|FOB|rding to the pinto beans wa| +897|91|4|1|15|14866.35|0.07|0.04|R|F|1995-05-25|1995-05-09|1995-06-07|COLLECT COD|REG AIR|r ideas. slyly spec| +897|184|5|2|26|28188.68|0.05|0.08|N|O|1995-07-01|1995-06-10|1995-07-14|COLLECT COD|MAIL|tions sleep according to the special| +897|126|1|3|13|13339.56|0.07|0.00|A|F|1995-03-30|1995-05-17|1995-04-21|TAKE BACK RETURN|MAIL|bold accounts mold carefully! braids| +897|102|7|4|2|2004.20|0.08|0.08|R|F|1995-05-22|1995-05-07|1995-06-16|COLLECT COD|RAIL|into beans. slyly special fox| +898|161|2|1|9|9550.44|0.07|0.08|A|F|1993-07-04|1993-07-09|1993-07-25|NONE|AIR|e slyly across the blithe| +898|179|7|2|37|39929.29|0.03|0.05|A|F|1993-08-17|1993-08-04|1993-09-01|DELIVER IN PERSON|REG AIR|packages sleep furiously| +898|49|8|3|11|10439.44|0.01|0.00|A|F|1993-09-13|1993-08-31|1993-09-25|TAKE BACK RETURN|MAIL|etly bold accounts | +898|193|6|4|36|39354.84|0.04|0.07|R|F|1993-08-04|1993-07-25|1993-08-23|DELIVER IN PERSON|REG AIR| after the carefully | +899|61|10|1|18|17299.08|0.04|0.05|N|O|1998-08-06|1998-05-09|1998-09-05|DELIVER IN PERSON|AIR|re daring, pending deposits. blit| +899|47|4|2|25|23676.00|0.00|0.07|N|O|1998-07-21|1998-05-12|1998-08-16|NONE|REG AIR|rly final sentiments. bold pinto beans | +899|85|6|3|4|3940.32|0.09|0.05|N|O|1998-06-02|1998-06-28|1998-06-14|TAKE BACK RETURN|REG AIR|ter the carefully regular deposits are agai| +899|180|9|4|14|15122.52|0.05|0.03|N|O|1998-05-21|1998-05-28|1998-06-03|TAKE BACK RETURN|FOB|ades impress carefully| +899|71|10|5|4|3884.28|0.06|0.02|N|O|1998-04-11|1998-05-14|1998-04-27|NONE|TRUCK|ges. blithe, ironic waters cajole care| +899|120|4|6|47|47945.64|0.00|0.04|N|O|1998-04-14|1998-05-30|1998-05-13|DELIVER IN PERSON|TRUCK|furiously final foxes after the s| +899|14|1|7|11|10054.11|0.02|0.08|N|O|1998-06-03|1998-06-15|1998-06-20|COLLECT COD|REG AIR|t the ironic| +900|199|1|1|44|48364.36|0.01|0.06|R|F|1994-12-15|1994-12-03|1994-12-27|COLLECT COD|MAIL| detect quick| +900|115|6|2|48|48725.28|0.08|0.04|A|F|1994-12-22|1994-11-08|1995-01-19|COLLECT COD|TRUCK|cial pinto beans nag | +900|75|6|3|24|23401.68|0.03|0.00|R|F|1994-10-21|1994-12-25|1994-10-22|TAKE BACK RETURN|SHIP|-ray furiously un| +901|22|7|1|36|33192.72|0.01|0.01|N|O|1998-08-11|1998-10-09|1998-08-27|DELIVER IN PERSON|REG AIR|. accounts are care| +901|46|7|2|2|1892.08|0.09|0.02|N|O|1998-10-25|1998-09-27|1998-11-01|DELIVER IN PERSON|AIR|d foxes use slyly| +901|43|10|3|37|34892.48|0.04|0.08|N|O|1998-11-01|1998-09-13|1998-11-05|NONE|AIR|ickly final deposits | +901|18|9|4|11|10098.11|0.00|0.06|N|O|1998-11-13|1998-10-19|1998-11-14|TAKE BACK RETURN|TRUCK|ourts among the quickly expre| +902|111|2|1|3|3033.33|0.06|0.00|R|F|1994-10-01|1994-10-25|1994-10-28|COLLECT COD|MAIL|into beans thrash blithely about the flu| +902|118|2|2|8|8144.88|0.06|0.07|R|F|1994-10-25|1994-09-20|1994-11-07|COLLECT COD|RAIL| orbits al| +902|165|2|3|24|25563.84|0.02|0.05|R|F|1994-11-08|1994-10-12|1994-11-26|NONE|FOB|. blithely even accounts poach furiously i| +903|65|10|1|27|26056.62|0.04|0.03|N|O|1995-09-18|1995-09-20|1995-10-02|TAKE BACK RETURN|SHIP|lly pending foxes. furiously| +903|9|2|2|35|31815.00|0.06|0.05|N|O|1995-09-18|1995-08-21|1995-10-12|TAKE BACK RETURN|TRUCK|rets wake fin| +903|9|2|3|33|29997.00|0.02|0.03|N|O|1995-09-24|1995-09-01|1995-10-12|COLLECT COD|MAIL|ely ironic packages wake blithely| +903|56|1|4|9|8604.45|0.09|0.00|N|O|1995-10-06|1995-09-14|1995-10-24|NONE|TRUCK|he slyly ev| +903|42|3|5|1|942.04|0.04|0.00|N|O|1995-10-22|1995-09-13|1995-11-03|NONE|AIR|y final platelets sublate among the | +903|168|9|6|13|13886.08|0.07|0.02|N|O|1995-09-11|1995-10-04|1995-10-03|COLLECT COD|SHIP|sleep along the final| +928|169|10|1|29|31005.64|0.07|0.02|R|F|1995-05-17|1995-05-12|1995-05-21|NONE|REG AIR|ly alongside of the s| +928|48|7|2|24|22752.96|0.05|0.08|A|F|1995-04-06|1995-05-08|1995-04-24|DELIVER IN PERSON|AIR|s the furiously regular warthogs im| +928|152|10|3|46|48398.90|0.08|0.00|A|F|1995-05-09|1995-04-09|1995-06-01|DELIVER IN PERSON|REG AIR| beans sleep against the carefully ir| +928|52|4|4|43|40938.15|0.10|0.05|A|F|1995-04-14|1995-04-21|1995-05-09|NONE|REG AIR|blithely. express, silent requests doze at| +928|12|3|5|38|34656.38|0.02|0.08|N|F|1995-06-08|1995-04-15|1995-06-30|TAKE BACK RETURN|SHIP|xpress grouc| +928|55|6|6|50|47752.50|0.05|0.00|N|F|1995-06-07|1995-04-15|1995-07-01|DELIVER IN PERSON|TRUCK| slyly slyly special request| +928|11|5|7|11|10021.11|0.00|0.01|A|F|1995-04-29|1995-04-16|1995-04-30|NONE|AIR|longside of| +929|129|8|1|45|46310.40|0.09|0.01|R|F|1993-01-24|1992-12-06|1993-02-16|DELIVER IN PERSON|REG AIR|ges haggle careful| +929|175|5|2|44|47307.48|0.02|0.00|A|F|1992-10-09|1992-11-20|1992-10-22|DELIVER IN PERSON|SHIP|s. excuses cajole. carefully regu| +929|74|5|3|14|13636.98|0.06|0.07|A|F|1992-10-21|1992-11-17|1992-11-15|NONE|FOB|gainst the| +929|102|5|4|7|7014.70|0.06|0.01|A|F|1992-12-24|1992-12-19|1993-01-08|TAKE BACK RETURN|TRUCK|ithely. slyly c| +930|45|4|1|36|34021.44|0.10|0.04|R|F|1994-12-21|1995-02-20|1994-12-24|COLLECT COD|RAIL|quickly regular pinto beans sle| +930|18|8|2|47|43146.47|0.08|0.00|A|F|1995-03-20|1995-02-04|1995-04-04|DELIVER IN PERSON|AIR|ackages. fluffily e| +930|65|10|3|10|9650.60|0.07|0.08|A|F|1994-12-18|1995-01-27|1995-01-16|COLLECT COD|AIR|ckly regular requests: regular instructions| +930|100|2|4|21|21002.10|0.06|0.02|A|F|1995-02-16|1995-03-03|1995-03-13|DELIVER IN PERSON|SHIP|foxes. regular deposits integrate carefu| +930|164|9|5|50|53208.00|0.03|0.06|A|F|1995-04-03|1995-01-29|1995-04-22|COLLECT COD|MAIL| excuses among the furiously express ideas | +930|145|4|6|10|10451.40|0.00|0.04|A|F|1995-02-09|1995-02-17|1995-02-16|NONE|SHIP|blithely bold i| +930|167|4|7|30|32014.80|0.07|0.08|R|F|1995-01-20|1995-02-28|1995-02-04|TAKE BACK RETURN|RAIL|g accounts sleep along the platelets.| +931|40|1|1|18|16920.72|0.00|0.05|A|F|1993-04-04|1993-01-11|1993-04-13|NONE|RAIL|slyly ironic re| +931|17|7|2|10|9170.10|0.05|0.07|A|F|1993-03-01|1993-01-09|1993-03-07|TAKE BACK RETURN|SHIP|ajole quickly. slyly sil| +931|147|6|3|48|50262.72|0.01|0.08|A|F|1993-02-03|1993-03-02|1993-02-09|TAKE BACK RETURN|FOB|ep alongside of the fluffy | +931|82|3|4|38|37319.04|0.08|0.08|A|F|1993-03-06|1993-02-24|1993-03-27|DELIVER IN PERSON|RAIL|usly final packages integrate carefully| +932|44|1|1|41|38705.64|0.01|0.05|N|O|1997-06-05|1997-07-22|1997-06-26|COLLECT COD|RAIL|foxes. ironic pl| +933|49|8|1|23|21827.92|0.02|0.04|R|F|1992-08-13|1992-09-18|1992-08-25|DELIVER IN PERSON|MAIL| the furiously bold dinos. sly| +933|13|4|2|27|24651.27|0.02|0.01|R|F|1992-10-03|1992-10-02|1992-10-26|DELIVER IN PERSON|RAIL|ests. express| +933|100|2|3|26|26002.60|0.05|0.00|A|F|1992-11-09|1992-11-03|1992-11-16|DELIVER IN PERSON|AIR| the deposits affix slyly after t| +934|118|5|1|18|18325.98|0.07|0.01|N|O|1996-09-10|1996-09-20|1996-09-25|COLLECT COD|RAIL|y unusual requests dazzle above t| +935|28|3|1|23|21344.46|0.05|0.00|N|O|1997-11-11|1997-11-22|1997-11-29|COLLECT COD|REG AIR|ular accounts about| +935|65|10|2|23|22196.38|0.02|0.01|N|O|1998-01-11|1997-11-25|1998-02-05|COLLECT COD|TRUCK|hes haggle furiously dolphins. qu| +935|135|1|3|36|37264.68|0.06|0.00|N|O|1997-11-05|1997-12-05|1997-11-25|TAKE BACK RETURN|AIR|leep about the exp| +935|58|3|4|13|12454.65|0.08|0.04|N|O|1998-01-13|1997-11-30|1998-02-08|DELIVER IN PERSON|TRUCK|ld platelet| +935|13|7|5|8|7304.08|0.02|0.05|N|O|1998-01-12|1997-11-02|1998-02-05|NONE|TRUCK|cept the quickly regular p| +935|59|1|6|1|959.05|0.01|0.08|N|O|1997-12-14|1997-11-22|1998-01-08|DELIVER IN PERSON|TRUCK| instructions. ironic acc| +960|107|10|1|1|1007.10|0.07|0.00|A|F|1994-12-24|1994-10-26|1995-01-20|DELIVER IN PERSON|AIR|y ironic packages. quickly even | +960|117|7|2|25|25427.75|0.06|0.08|R|F|1994-12-01|1994-10-29|1994-12-27|DELIVER IN PERSON|RAIL|ts. fluffily regular requests | +960|175|3|3|32|34405.44|0.01|0.08|R|F|1995-01-19|1994-12-17|1995-02-04|DELIVER IN PERSON|FOB|around the blithe, even pl| +961|118|5|1|7|7126.77|0.10|0.00|N|O|1995-07-23|1995-07-20|1995-08-11|TAKE BACK RETURN|RAIL|usual dolphins. ironic pearls sleep blit| +961|91|2|2|18|17839.62|0.09|0.05|N|O|1995-07-01|1995-08-14|1995-07-04|DELIVER IN PERSON|AIR|rmanent foxes haggle speci| +961|97|8|3|42|41877.78|0.06|0.01|N|O|1995-08-24|1995-08-21|1995-09-10|TAKE BACK RETURN|SHIP|ests do cajole blithely. furiously bo| +961|34|10|4|29|27086.87|0.00|0.07|N|F|1995-06-10|1995-08-20|1995-06-26|TAKE BACK RETURN|TRUCK|l accounts use blithely against the| +961|26|7|5|38|35188.76|0.03|0.05|N|O|1995-08-21|1995-07-19|1995-08-27|NONE|RAIL|he blithely special requests. furiousl| +961|197|8|6|30|32915.70|0.09|0.03|N|O|1995-07-06|1995-07-20|1995-07-26|DELIVER IN PERSON|MAIL|warhorses slee| +962|57|8|1|36|34453.80|0.01|0.03|R|F|1994-08-09|1994-07-10|1994-09-02|COLLECT COD|TRUCK|al foxes. iron| +962|36|2|2|27|25272.81|0.09|0.02|A|F|1994-05-11|1994-07-10|1994-06-03|TAKE BACK RETURN|SHIP|y slyly express deposits. final i| +962|80|1|3|3|2940.24|0.07|0.08|A|F|1994-05-08|1994-07-06|1994-06-02|DELIVER IN PERSON|FOB|ag furiously. even pa| +962|57|5|4|20|19141.00|0.04|0.02|R|F|1994-08-26|1994-06-27|1994-09-11|DELIVER IN PERSON|SHIP| deposits use fluffily according to | +962|152|7|5|12|12625.80|0.02|0.00|A|F|1994-06-09|1994-06-07|1994-06-11|COLLECT COD|TRUCK|across the furiously regular escapades daz| +962|188|9|6|5|5440.90|0.02|0.05|A|F|1994-08-29|1994-07-15|1994-09-19|COLLECT COD|TRUCK|efully bold packages run slyly caref| +963|194|8|1|7|7659.33|0.01|0.00|R|F|1994-09-12|1994-07-18|1994-09-17|DELIVER IN PERSON|REG AIR|s. slyly regular depe| +963|98|10|2|48|47908.32|0.10|0.06|R|F|1994-08-25|1994-08-12|1994-09-21|DELIVER IN PERSON|RAIL|ages. quickly express deposits cajole pe| +964|199|10|1|39|42868.41|0.04|0.01|N|O|1995-06-21|1995-07-24|1995-06-24|NONE|AIR|se furiously regular instructions. blith| +964|113|4|2|1|1013.11|0.02|0.05|N|O|1995-08-20|1995-07-29|1995-09-10|DELIVER IN PERSON|REG AIR|unts. quickly even platelets s| +964|57|5|3|49|46895.45|0.01|0.03|N|O|1995-09-06|1995-08-10|1995-10-05|NONE|MAIL|ounts. blithely regular packag| +964|55|3|4|44|42022.20|0.05|0.02|N|O|1995-09-18|1995-08-02|1995-10-17|TAKE BACK RETURN|TRUCK|ronic deposit| +965|108|1|1|20|20162.00|0.04|0.05|N|F|1995-06-16|1995-07-20|1995-07-06|COLLECT COD|MAIL|kly. carefully pending requ| +965|18|5|2|23|21114.23|0.06|0.08|N|O|1995-07-12|1995-07-08|1995-08-11|COLLECT COD|MAIL|ld kindle carefully across th| +966|180|8|1|19|20523.42|0.07|0.01|N|O|1998-05-26|1998-07-15|1998-05-29|COLLECT COD|FOB|efully final pinto beans. quickly | +966|117|4|2|42|42718.62|0.02|0.06|N|O|1998-06-28|1998-06-20|1998-07-05|NONE|TRUCK|tions boost furiously car| +966|22|1|3|42|38724.84|0.06|0.08|N|O|1998-06-15|1998-06-08|1998-07-05|NONE|RAIL|sly ironic asymptotes hagg| +966|5|2|4|20|18100.00|0.04|0.01|N|O|1998-07-19|1998-07-15|1998-07-27|NONE|TRUCK|pecial ins| +967|59|4|1|41|39321.05|0.05|0.05|R|F|1992-09-21|1992-08-15|1992-10-21|NONE|MAIL|ld foxes wake closely special| +967|85|6|2|4|3940.32|0.01|0.02|A|F|1992-07-15|1992-07-27|1992-07-18|DELIVER IN PERSON|REG AIR|platelets hang carefully along | +967|132|8|3|10|10321.30|0.00|0.02|A|F|1992-09-18|1992-08-06|1992-09-19|DELIVER IN PERSON|MAIL|old pinto beans alongside of the exp| +967|148|7|4|49|51358.86|0.01|0.04|A|F|1992-09-28|1992-09-15|1992-10-14|NONE|SHIP|the slyly even ideas. carefully even| +967|17|1|5|41|37597.41|0.08|0.04|A|F|1992-07-23|1992-08-07|1992-08-13|TAKE BACK RETURN|FOB|efully special ide| +967|106|9|6|17|17103.70|0.05|0.06|A|F|1992-10-02|1992-08-19|1992-10-25|NONE|MAIL|y ironic foxes caj| +967|161|8|7|18|19100.88|0.00|0.02|A|F|1992-10-06|1992-08-05|1992-10-15|DELIVER IN PERSON|RAIL|ngage blith| +992|60|2|1|14|13440.84|0.10|0.03|N|O|1998-01-29|1997-12-29|1998-02-18|TAKE BACK RETURN|MAIL|the unusual, even dependencies affix fluff| +992|38|9|2|34|31893.02|0.02|0.00|N|O|1997-11-29|1998-01-21|1997-11-30|NONE|RAIL|s use silently. blithely regular ideas b| +992|105|6|3|30|30153.00|0.10|0.00|N|O|1997-12-15|1998-02-02|1998-01-12|NONE|SHIP|nic instructions n| +992|48|5|4|21|19908.84|0.06|0.06|N|O|1997-11-13|1997-12-28|1997-12-10|NONE|TRUCK|fily. quickly special deposit| +992|92|4|5|7|6944.63|0.09|0.05|N|O|1997-11-30|1997-12-24|1997-12-16|DELIVER IN PERSON|TRUCK|ideas haggle. special theodolit| +992|75|3|6|41|39977.87|0.10|0.01|N|O|1997-11-14|1998-02-04|1997-11-23|TAKE BACK RETURN|AIR|eodolites cajole across the accounts.| +993|175|5|1|33|35480.61|0.01|0.05|N|O|1996-01-03|1995-11-28|1996-01-23|DELIVER IN PERSON|AIR| the deposits affix agains| +993|3|6|2|28|25284.00|0.06|0.08|N|O|1995-10-24|1995-11-20|1995-11-06|DELIVER IN PERSON|RAIL|lites. even theodolite| +993|40|1|3|10|9400.40|0.03|0.08|N|O|1995-12-17|1995-11-13|1995-12-20|NONE|RAIL|encies wake fur| +993|191|4|4|40|43647.60|0.01|0.01|N|O|1995-11-16|1995-11-01|1995-12-05|TAKE BACK RETURN|RAIL|gle above the furiously | +993|146|7|5|33|34522.62|0.09|0.08|N|O|1995-09-28|1995-10-24|1995-10-03|COLLECT COD|RAIL|fluffily. quiet excuses sleep furiously sly| +993|137|3|6|35|36299.55|0.04|0.02|N|O|1995-10-26|1995-10-20|1995-11-05|DELIVER IN PERSON|FOB|es. ironic, ironic requests| +993|5|2|7|15|13575.00|0.09|0.03|N|O|1995-09-27|1995-10-21|1995-10-17|DELIVER IN PERSON|MAIL|sits. pending pinto beans haggle? ca| +994|65|6|1|4|3860.24|0.07|0.03|R|F|1994-07-05|1994-05-21|1994-07-20|COLLECT COD|SHIP|aggle carefully acc| +994|10|3|2|11|10010.11|0.01|0.00|R|F|1994-05-03|1994-06-10|1994-05-22|NONE|AIR|ular accounts sleep | +994|31|7|3|5|4655.15|0.08|0.08|A|F|1994-06-24|1994-06-14|1994-06-26|NONE|MAIL|ainst the pending requests. packages sl| +994|131|7|4|25|25778.25|0.10|0.00|A|F|1994-06-03|1994-06-02|1994-06-06|COLLECT COD|RAIL|usual pinto beans.| +995|173|1|1|15|16097.55|0.08|0.05|N|O|1995-06-30|1995-08-04|1995-07-27|NONE|REG AIR|uses. fluffily fina| +995|129|4|2|28|28815.36|0.08|0.03|N|F|1995-06-12|1995-07-20|1995-06-19|DELIVER IN PERSON|SHIP|pades. quick, final frays use flu| +995|166|3|3|45|47977.20|0.00|0.05|N|O|1995-08-02|1995-07-21|1995-08-03|DELIVER IN PERSON|SHIP|lar packages detect blithely above t| +995|66|3|4|25|24151.50|0.01|0.08|N|O|1995-09-08|1995-08-05|1995-09-28|NONE|TRUCK|lyly even | +995|24|5|5|18|16632.36|0.06|0.03|N|O|1995-07-03|1995-07-29|1995-07-22|TAKE BACK RETURN|AIR| even accounts unwind c| +996|173|2|1|43|46146.31|0.03|0.06|N|O|1998-03-27|1998-03-25|1998-04-06|COLLECT COD|SHIP| the blithely ironic foxes. slyly silent d| +997|163|4|1|11|11694.76|0.00|0.02|N|O|1997-06-16|1997-07-21|1997-07-14|DELIVER IN PERSON|TRUCK|p furiously according to t| +997|48|9|2|17|16116.68|0.03|0.00|N|O|1997-07-28|1997-07-26|1997-08-20|DELIVER IN PERSON|SHIP|aggle quickly furiously| +998|10|7|1|22|20020.22|0.04|0.05|A|F|1994-12-03|1995-02-17|1994-12-19|TAKE BACK RETURN|RAIL|lites. qui| +998|181|2|2|7|7568.26|0.10|0.05|R|F|1995-03-24|1995-01-18|1995-04-03|NONE|MAIL|nic deposits. even asym| +998|142|9|3|30|31264.20|0.05|0.07|A|F|1994-12-02|1995-01-23|1994-12-23|NONE|SHIP|lyly idle Tir| +998|11|8|4|6|5466.06|0.09|0.05|R|F|1995-03-20|1994-12-27|1995-04-13|DELIVER IN PERSON|MAIL|refully accounts. carefully express ac| +998|73|2|5|1|973.07|0.04|0.00|R|F|1995-01-05|1995-01-06|1995-01-13|NONE|SHIP|es sleep. regular dependencies use bl| +999|61|6|1|34|32676.04|0.00|0.08|R|F|1993-10-30|1993-10-17|1993-10-31|TAKE BACK RETURN|SHIP|its. daringly final instruc| +999|199|1|2|41|45066.79|0.08|0.01|A|F|1993-10-16|1993-12-04|1993-11-03|DELIVER IN PERSON|REG AIR|us depths. carefully ironic instruc| +999|118|5|3|15|15271.65|0.07|0.06|A|F|1993-12-12|1993-10-18|1994-01-08|COLLECT COD|REG AIR|y ironic requests. carefully regu| +999|3|4|4|10|9030.00|0.05|0.02|A|F|1993-11-23|1993-12-02|1993-11-29|NONE|MAIL|efully pending| +999|19|10|5|3|2757.03|0.03|0.00|R|F|1993-09-17|1993-10-22|1993-10-13|NONE|FOB|nic, pending ideas. bl| +999|181|2|6|37|40003.66|0.00|0.04|R|F|1994-01-03|1993-10-28|1994-01-12|DELIVER IN PERSON|TRUCK|ckly slyly unusual packages: packages hagg| +1024|199|2|1|49|53860.31|0.03|0.05|N|O|1998-03-06|1998-01-26|1998-03-29|TAKE BACK RETURN|FOB|ts. asymptotes nag fur| +1024|126|5|2|34|34888.08|0.00|0.01|N|O|1998-01-06|1998-02-05|1998-01-26|COLLECT COD|SHIP|des the slyly even| +1024|44|3|3|28|26433.12|0.04|0.01|N|O|1998-03-04|1998-03-12|1998-03-15|TAKE BACK RETURN|TRUCK|e blithely regular pi| +1024|184|5|4|13|14094.34|0.02|0.04|N|O|1998-04-11|1998-02-26|1998-04-18|NONE|FOB|e slyly around the slyly special instructi| +1024|21|4|5|49|45129.98|0.02|0.04|N|O|1998-02-27|1998-03-10|1998-03-27|COLLECT COD|FOB| carefully bold | +1025|150|1|1|36|37805.40|0.03|0.04|A|F|1995-05-15|1995-07-05|1995-06-10|COLLECT COD|FOB|e unusual, regular instr| +1025|69|10|2|23|22288.38|0.08|0.03|N|F|1995-06-02|1995-07-29|1995-06-23|COLLECT COD|RAIL| regular platelets nag carefu| +1025|23|2|3|25|23075.50|0.06|0.05|R|F|1995-05-29|1995-06-21|1995-06-13|DELIVER IN PERSON|REG AIR|xpress foxes. furiousl| +1026|38|4|1|36|33769.08|0.10|0.02|N|O|1997-06-14|1997-07-20|1997-06-23|NONE|SHIP|st the ide| +1026|37|8|2|6|5622.18|0.10|0.08|N|O|1997-07-07|1997-08-16|1997-07-14|TAKE BACK RETURN|TRUCK|to beans. special, regular packages hagg| +1027|156|1|1|43|45414.45|0.07|0.08|R|F|1992-06-17|1992-08-28|1992-07-10|DELIVER IN PERSON|MAIL|oxes. carefully regular deposits| +1027|113|10|2|20|20262.20|0.01|0.02|A|F|1992-06-08|1992-08-29|1992-06-14|NONE|TRUCK|ar excuses eat f| +1027|126|9|3|2|2052.24|0.01|0.02|R|F|1992-08-28|1992-07-09|1992-09-10|NONE|FOB|s. quickly unusual waters inside | +1027|100|4|4|13|13001.30|0.08|0.01|R|F|1992-08-22|1992-07-10|1992-09-12|DELIVER IN PERSON|RAIL|ily ironic ideas use| +1027|136|2|5|22|22794.86|0.02|0.00|A|F|1992-09-03|1992-08-14|1992-10-01|DELIVER IN PERSON|FOB|the furiously express ex| +1027|105|8|6|10|10051.00|0.06|0.08|R|F|1992-08-28|1992-08-06|1992-09-03|COLLECT COD|REG AIR|ilent, express foxes near the blithely sp| +1028|128|3|1|2|2056.24|0.09|0.03|A|F|1994-01-10|1994-03-22|1994-01-26|COLLECT COD|FOB|s alongside of the regular asymptotes sleep| +1028|112|9|2|39|39472.29|0.06|0.05|R|F|1994-02-18|1994-03-22|1994-03-06|TAKE BACK RETURN|MAIL| final dependencies affix a| +1028|100|3|3|8|8000.80|0.03|0.07|A|F|1994-02-14|1994-03-28|1994-02-22|NONE|AIR|e carefully final packages. furiously fi| +1028|32|8|4|26|24232.78|0.07|0.02|A|F|1994-03-18|1994-02-08|1994-03-19|TAKE BACK RETURN|RAIL|ronic platelets. carefully f| +1028|29|2|5|27|25083.54|0.00|0.04|A|F|1994-04-03|1994-02-07|1994-04-26|NONE|REG AIR|ial accounts nag. slyly| +1028|26|1|6|39|36114.78|0.03|0.02|A|F|1994-02-27|1994-02-16|1994-03-02|DELIVER IN PERSON|AIR|c theodoli| +1028|31|2|7|22|20482.66|0.03|0.00|R|F|1994-04-24|1994-02-27|1994-05-08|NONE|REG AIR| Tiresias alongside of the carefully spec| +1029|137|3|1|45|46670.85|0.05|0.07|R|F|1994-07-21|1994-08-30|1994-07-29|TAKE BACK RETURN|FOB|sits boost blithely| +1030|65|10|1|17|16406.02|0.06|0.06|R|F|1994-10-13|1994-08-01|1994-11-10|DELIVER IN PERSON|RAIL|ly. carefully even packages dazz| +1031|46|7|1|15|14190.60|0.10|0.08|A|F|1994-11-07|1994-10-29|1994-11-09|TAKE BACK RETURN|FOB|about the carefully bold a| +1031|165|4|2|28|29824.48|0.05|0.01|A|F|1994-12-10|1994-10-29|1994-12-18|COLLECT COD|FOB|ly ironic accounts across the q| +1031|187|8|3|27|29353.86|0.07|0.02|R|F|1994-09-20|1994-10-18|1994-10-10|DELIVER IN PERSON|SHIP|gular deposits cajole. blithely unus| +1031|88|9|4|7|6916.56|0.03|0.03|R|F|1994-12-07|1994-11-11|1994-12-30|COLLECT COD|FOB|r instructions. car| +1031|191|5|5|44|48012.36|0.01|0.07|R|F|1994-11-20|1994-11-24|1994-12-11|NONE|AIR|re slyly above the furio| +1056|121|6|1|37|37781.44|0.04|0.06|R|F|1995-02-18|1995-04-01|1995-03-20|NONE|TRUCK| special packages. qui| +1057|193|5|1|29|31702.51|0.10|0.01|A|F|1992-05-05|1992-05-05|1992-06-03|TAKE BACK RETURN|SHIP|es wake according to the q| +1057|169|8|2|11|11760.76|0.00|0.02|R|F|1992-03-31|1992-04-18|1992-04-18|COLLECT COD|AIR|yly final theodolites. furi| +1057|85|6|3|21|20686.68|0.03|0.04|A|F|1992-02-28|1992-05-01|1992-03-10|NONE|REG AIR|ar orbits boost bli| +1057|182|3|4|20|21643.60|0.06|0.03|R|F|1992-03-02|1992-05-19|1992-03-13|DELIVER IN PERSON|TRUCK|s wake bol| +1057|97|1|5|7|6979.63|0.06|0.05|R|F|1992-06-05|1992-04-30|1992-06-20|NONE|TRUCK|y slyly express theodolites. slyly bo| +1057|52|7|6|19|18088.95|0.04|0.07|A|F|1992-05-31|1992-05-09|1992-06-02|DELIVER IN PERSON|FOB|r-- packages haggle alon| +1058|140|6|1|24|24963.36|0.08|0.04|A|F|1993-07-09|1993-05-28|1993-07-22|DELIVER IN PERSON|TRUCK|fully ironic accounts. express accou| +1058|89|10|2|5|4945.40|0.04|0.07|R|F|1993-05-11|1993-05-29|1993-05-27|COLLECT COD|TRUCK|refully even requests boost along| +1058|90|1|3|44|43563.96|0.10|0.01|R|F|1993-06-26|1993-06-21|1993-07-20|COLLECT COD|TRUCK|uriously f| +1058|5|2|4|25|22625.00|0.09|0.01|A|F|1993-05-27|1993-06-10|1993-06-20|TAKE BACK RETURN|MAIL| the final requests believe carefully | +1059|178|9|1|16|17250.72|0.07|0.02|A|F|1994-04-24|1994-03-31|1994-04-28|DELIVER IN PERSON|SHIP|y ironic pinto | +1059|29|2|2|7|6503.14|0.07|0.06|R|F|1994-03-30|1994-04-01|1994-04-24|DELIVER IN PERSON|MAIL|the furiously silent excuses are e| +1059|88|9|3|45|44463.60|0.00|0.02|R|F|1994-06-10|1994-05-08|1994-06-21|COLLECT COD|RAIL|riously even theodolites. slyly regula| +1059|110|7|4|26|26262.86|0.09|0.01|A|F|1994-03-17|1994-04-18|1994-03-26|DELIVER IN PERSON|TRUCK|ar pinto beans at the furiously | +1059|139|5|5|37|38447.81|0.09|0.04|R|F|1994-03-31|1994-05-08|1994-04-06|COLLECT COD|RAIL| packages lose in place of the slyly unusu| +1059|190|1|6|50|54509.50|0.00|0.03|A|F|1994-06-15|1994-05-11|1994-06-29|NONE|MAIL|s impress furiously about| +1059|123|4|7|13|13300.56|0.01|0.03|R|F|1994-06-12|1994-05-11|1994-07-02|COLLECT COD|TRUCK|usly regular theodo| +1060|196|10|1|8|8769.52|0.07|0.04|R|F|1993-05-21|1993-05-06|1993-06-10|DELIVER IN PERSON|FOB|iously. furiously regular in| +1060|8|5|2|26|23608.00|0.06|0.08|R|F|1993-04-12|1993-04-01|1993-04-20|DELIVER IN PERSON|TRUCK|counts; even deposits are carefull| +1060|164|3|3|11|11705.76|0.01|0.07|A|F|1993-05-13|1993-05-08|1993-05-17|TAKE BACK RETURN|MAIL|e regular deposits: re| +1060|110|7|4|16|16161.76|0.03|0.06|A|F|1993-06-15|1993-04-18|1993-07-05|COLLECT COD|SHIP|ccounts. foxes maintain care| +1060|53|8|5|1|953.05|0.04|0.06|A|F|1993-06-19|1993-05-10|1993-06-21|COLLECT COD|RAIL|posits detect carefully abo| +1060|72|2|6|26|25273.82|0.01|0.03|A|F|1993-02-28|1993-04-01|1993-03-09|TAKE BACK RETURN|FOB|quickly abo| +1060|121|10|7|36|36760.32|0.09|0.01|R|F|1993-03-14|1993-03-24|1993-04-02|TAKE BACK RETURN|FOB|r the quickly| +1061|151|6|1|7|7358.05|0.04|0.04|N|O|1998-08-09|1998-08-12|1998-08-16|COLLECT COD|FOB|es are slyly expr| +1061|119|3|2|2|2038.22|0.06|0.02|N|O|1998-08-15|1998-08-05|1998-08-24|COLLECT COD|MAIL|. regular accounts impre| +1061|111|8|3|26|26288.86|0.08|0.02|N|O|1998-06-18|1998-07-25|1998-06-24|TAKE BACK RETURN|AIR|ave to slee| +1061|136|7|4|41|42481.33|0.00|0.05|N|O|1998-06-29|1998-07-02|1998-07-27|NONE|MAIL|s are. ironic theodolites cajole. dep| +1061|131|2|5|50|51556.50|0.04|0.08|N|O|1998-05-25|1998-07-22|1998-06-22|COLLECT COD|AIR|nding excuses are around the e| +1061|144|1|6|35|36544.90|0.09|0.05|N|O|1998-07-05|1998-07-07|1998-07-30|TAKE BACK RETURN|MAIL|ending requests nag careful| +1062|137|8|1|38|39410.94|0.00|0.01|N|O|1997-01-27|1997-03-07|1997-02-16|DELIVER IN PERSON|TRUCK|deas. pending acc| +1063|96|9|1|42|41835.78|0.03|0.02|A|F|1994-07-10|1994-05-25|1994-07-26|NONE|RAIL|tructions about the blithely ex| +1088|107|8|1|30|30213.00|0.07|0.03|A|F|1992-05-22|1992-06-25|1992-06-11|TAKE BACK RETURN|SHIP|long the packages snooze careful| +1088|37|3|2|11|10307.33|0.06|0.00|A|F|1992-08-30|1992-07-25|1992-09-10|TAKE BACK RETURN|AIR|inal requests. fluffily express theod| +1088|181|2|3|5|5405.90|0.03|0.07|A|F|1992-07-01|1992-07-25|1992-07-02|NONE|AIR|refully ironic packages. r| +1088|124|5|4|3|3072.36|0.09|0.03|A|F|1992-06-15|1992-08-02|1992-06-18|DELIVER IN PERSON|MAIL|pecial theodolites | +1089|151|3|1|47|49404.05|0.05|0.06|N|O|1996-06-26|1996-06-25|1996-07-11|NONE|TRUCK|aggle furiously among the bravely eve| +1089|50|7|2|35|33251.75|0.03|0.00|N|O|1996-08-14|1996-07-10|1996-08-26|NONE|TRUCK|ly express deposits haggle| +1089|26|7|3|23|21298.46|0.10|0.05|N|O|1996-06-24|1996-07-25|1996-07-20|DELIVER IN PERSON|AIR|g dolphins. deposits integrate. s| +1089|141|10|4|1|1041.14|0.01|0.03|N|O|1996-07-08|1996-07-07|1996-07-17|COLLECT COD|RAIL|n courts among the caref| +1090|22|3|1|5|4610.10|0.02|0.05|N|O|1998-02-19|1997-12-25|1998-02-24|DELIVER IN PERSON|AIR|s above the | +1090|113|10|2|28|28367.08|0.08|0.08|N|O|1998-02-20|1998-01-03|1998-03-19|NONE|FOB|s cajole above the regular| +1091|38|9|1|40|37521.20|0.10|0.06|N|O|1996-12-17|1996-10-14|1996-12-24|TAKE BACK RETURN|REG AIR|platelets. regular packag| +1092|184|5|1|48|52040.64|0.04|0.04|N|O|1995-06-25|1995-04-06|1995-07-18|DELIVER IN PERSON|AIR|unusual accounts. fluffi| +1092|153|5|2|1|1053.15|0.01|0.06|A|F|1995-03-10|1995-04-21|1995-04-06|COLLECT COD|RAIL|lent, pending requests-- requests nag accor| +1092|161|8|3|28|29712.48|0.05|0.08|R|F|1995-04-08|1995-05-01|1995-05-02|DELIVER IN PERSON|FOB|affix carefully. u| +1092|86|7|4|2|1972.16|0.05|0.07|R|F|1995-04-09|1995-05-12|1995-05-03|TAKE BACK RETURN|TRUCK|ans. slyly eve| +1093|87|8|1|7|6909.56|0.04|0.02|N|O|1997-11-24|1997-09-23|1997-11-25|TAKE BACK RETURN|SHIP|bold deposits. blithely ironic depos| +1093|177|5|2|37|39855.29|0.08|0.04|N|O|1997-11-06|1997-10-08|1997-11-22|COLLECT COD|FOB|le furiously across the carefully sp| +1093|61|2|3|34|32676.04|0.01|0.06|N|O|1997-11-07|1997-09-06|1997-11-28|TAKE BACK RETURN|REG AIR|sits. express accounts play carefully. bol| +1094|115|6|1|9|9135.99|0.07|0.06|N|O|1997-12-28|1998-03-16|1998-01-18|DELIVER IN PERSON|AIR|as. slyly pe| +1095|137|3|1|33|34225.29|0.01|0.02|N|O|1995-10-03|1995-09-22|1995-10-13|NONE|MAIL|slyly around the iron| +1095|136|2|2|24|24867.12|0.04|0.06|N|O|1995-08-24|1995-10-20|1995-09-09|COLLECT COD|TRUCK|packages nod furiously above the carefully | +1095|156|4|3|13|13729.95|0.06|0.01|N|O|1995-08-24|1995-10-19|1995-09-02|TAKE BACK RETURN|REG AIR|ously even accounts. slyly bold a| +1095|135|1|4|28|28983.64|0.08|0.03|N|O|1995-09-20|1995-11-18|1995-10-02|DELIVER IN PERSON|SHIP| regular pac| +1095|112|2|5|40|40484.40|0.09|0.03|N|O|1995-10-18|1995-11-14|1995-11-09|NONE|MAIL| bold accounts haggle slyly furiously even| +1095|181|2|6|37|40003.66|0.07|0.08|N|O|1995-10-04|1995-11-13|1995-10-12|NONE|SHIP|. quickly even dolphins sle| +1120|178|8|1|10|10781.70|0.08|0.05|N|O|1997-12-17|1998-01-21|1997-12-23|DELIVER IN PERSON|MAIL|dependencies. blithel| +1120|20|1|2|49|45080.98|0.01|0.07|N|O|1998-01-03|1998-02-02|1998-01-09|TAKE BACK RETURN|RAIL|heodolites. quick re| +1120|76|6|3|21|20497.47|0.06|0.01|N|O|1998-01-11|1998-02-04|1998-01-19|COLLECT COD|REG AIR|s: fluffily even packages c| +1120|46|9|4|22|20812.88|0.09|0.08|N|O|1997-11-15|1998-01-25|1997-12-07|TAKE BACK RETURN|REG AIR|ons. slyly silent requests sleep silent| +1120|83|4|5|10|9830.80|0.07|0.08|N|O|1997-11-10|1998-02-01|1997-11-28|TAKE BACK RETURN|AIR|ages haggle furiously | +1121|168|3|1|42|44862.72|0.04|0.05|N|O|1997-03-05|1997-03-18|1997-03-14|DELIVER IN PERSON|SHIP|nts are slyly special packages. f| +1121|161|10|2|27|28651.32|0.08|0.00|N|O|1997-05-08|1997-03-28|1997-05-14|NONE|MAIL|ly ironic accounts cajole slyly abou| +1121|157|5|3|10|10571.50|0.00|0.04|N|O|1997-04-17|1997-03-18|1997-05-02|TAKE BACK RETURN|RAIL|dencies. quickly regular theodolites n| +1121|166|1|4|29|30918.64|0.02|0.01|N|O|1997-03-07|1997-04-02|1997-04-01|DELIVER IN PERSON|REG AIR| use furiously. quickly silent package| +1121|30|9|5|47|43711.41|0.09|0.03|N|O|1997-04-27|1997-03-28|1997-05-14|COLLECT COD|FOB|ly idle, i| +1121|200|1|6|50|55010.00|0.06|0.03|N|O|1997-04-21|1997-02-16|1997-04-25|NONE|TRUCK|odolites. slyly even accounts| +1121|80|8|7|37|36262.96|0.06|0.01|N|O|1997-02-27|1997-03-04|1997-03-02|COLLECT COD|RAIL|special packages. fluffily final requests s| +1122|92|6|1|8|7936.72|0.10|0.06|N|O|1997-02-02|1997-04-03|1997-02-22|TAKE BACK RETURN|RAIL|c foxes are along the slyly r| +1122|182|3|2|29|31383.22|0.05|0.04|N|O|1997-05-07|1997-04-07|1997-05-15|COLLECT COD|SHIP|ptotes. quickl| +1122|147|6|3|25|26178.50|0.09|0.01|N|O|1997-03-21|1997-03-03|1997-04-07|TAKE BACK RETURN|RAIL|d furiously. pinto | +1122|106|9|4|40|40244.00|0.08|0.08|N|O|1997-02-07|1997-03-25|1997-02-25|NONE|REG AIR|packages sleep after the asym| +1122|151|2|5|15|15767.25|0.05|0.03|N|O|1997-04-15|1997-03-15|1997-05-07|COLLECT COD|SHIP|olve blithely regular, | +1122|162|7|6|24|25491.84|0.04|0.01|N|O|1997-03-08|1997-02-20|1997-04-05|NONE|RAIL|blithely requests. slyly pending r| +1122|1|6|7|38|34238.00|0.00|0.08|N|O|1997-01-23|1997-04-02|1997-02-16|NONE|TRUCK|t theodolites sleep. even, ironic| +1123|12|2|1|10|9120.10|0.05|0.08|N|O|1996-11-12|1996-10-04|1996-11-30|NONE|MAIL|ckages are above the depths. slyly ir| +1123|178|8|2|39|42048.63|0.03|0.08|N|O|1996-08-25|1996-10-21|1996-09-04|DELIVER IN PERSON|REG AIR|rding to the furiously ironic requests: r| +1123|101|4|3|38|38041.80|0.03|0.08|N|O|1996-09-23|1996-10-04|1996-09-27|DELIVER IN PERSON|FOB| blithely carefully unusual reques| +1124|198|2|1|1|1098.19|0.09|0.08|N|O|1998-10-06|1998-10-02|1998-10-30|NONE|REG AIR| instructions cajole qu| +1124|6|1|2|13|11778.00|0.05|0.04|N|O|1998-09-05|1998-10-03|1998-09-30|DELIVER IN PERSON|SHIP|t the slyly | +1124|93|5|3|35|34758.15|0.10|0.05|N|O|1998-11-25|1998-10-08|1998-12-25|TAKE BACK RETURN|AIR|ut the slyly bold pinto beans; fi| +1124|50|1|4|25|23751.25|0.08|0.05|N|O|1998-08-05|1998-10-14|1998-08-11|NONE|MAIL|ggle slyly according| +1124|75|5|5|33|32177.31|0.05|0.04|N|O|1998-10-19|1998-09-17|1998-10-26|TAKE BACK RETURN|SHIP|eposits sleep slyly. stealthily f| +1124|27|6|6|43|39861.86|0.01|0.03|N|O|1998-09-19|1998-10-28|1998-10-10|COLLECT COD|MAIL|across the | +1124|95|6|7|1|995.09|0.09|0.01|N|O|1998-10-07|1998-08-31|1998-10-12|NONE|TRUCK|ly bold accou| +1125|133|4|1|4|4132.52|0.08|0.02|A|F|1994-12-10|1994-12-28|1994-12-30|NONE|MAIL| quickly express packages a| +1125|138|9|2|24|24915.12|0.10|0.03|R|F|1995-01-31|1994-12-02|1995-02-20|COLLECT COD|AIR|es about the slyly s| +1125|122|7|3|26|26575.12|0.05|0.04|A|F|1995-02-24|1995-01-18|1995-03-05|COLLECT COD|TRUCK|l instruction| +1125|98|1|4|29|28944.61|0.06|0.00|A|F|1994-11-29|1994-12-20|1994-12-10|DELIVER IN PERSON|RAIL| platelets wake against the carefully i| +1126|36|2|1|44|41185.32|0.08|0.03|N|O|1998-05-07|1998-04-02|1998-05-29|NONE|TRUCK|es. carefully special| +1126|58|3|2|7|6706.35|0.06|0.01|N|O|1998-05-02|1998-03-22|1998-05-21|COLLECT COD|MAIL|ons. final, unusual| +1126|147|10|3|14|14659.96|0.07|0.07|N|O|1998-04-17|1998-04-15|1998-05-12|DELIVER IN PERSON|TRUCK|nstructions. blithe| +1127|43|10|1|35|33006.40|0.02|0.03|N|O|1995-11-25|1995-11-03|1995-12-17|NONE|TRUCK|l instructions boost blithely according | +1127|110|5|2|38|38384.18|0.09|0.05|N|O|1995-11-07|1995-11-11|1995-11-26|DELIVER IN PERSON|RAIL|. never final packages boost acro| +1127|20|1|3|29|26680.58|0.09|0.07|N|O|1995-09-20|1995-11-21|1995-10-11|DELIVER IN PERSON|REG AIR|y. blithely r| +1127|175|6|4|7|7526.19|0.07|0.05|N|O|1995-11-05|1995-11-02|1995-11-11|DELIVER IN PERSON|FOB| idly pending pains | +1152|9|10|1|23|20907.00|0.06|0.04|A|F|1994-10-14|1994-10-22|1994-10-21|DELIVER IN PERSON|MAIL|equests alongside of the unusual | +1152|100|2|2|25|25002.50|0.04|0.08|R|F|1994-10-20|1994-09-18|1994-10-28|DELIVER IN PERSON|REG AIR|efully ironic accounts. sly instructions wa| +1152|42|9|3|6|5652.24|0.07|0.03|A|F|1994-12-07|1994-11-05|1994-12-25|DELIVER IN PERSON|FOB|p furiously; packages above th| +1153|86|7|1|15|14791.20|0.00|0.08|N|O|1996-04-24|1996-07-17|1996-04-29|TAKE BACK RETURN|SHIP|uctions boost fluffily according to| +1153|169|8|2|50|53458.00|0.00|0.07|N|O|1996-06-27|1996-07-13|1996-07-05|COLLECT COD|REG AIR|ronic asymptotes nag slyly. | +1153|44|5|3|25|23601.00|0.00|0.05|N|O|1996-06-18|1996-06-28|1996-07-09|NONE|TRUCK| theodolites| +1153|92|3|4|43|42659.87|0.01|0.00|N|O|1996-06-09|1996-06-01|1996-07-04|DELIVER IN PERSON|MAIL|special instructions are. unusual, final du| +1153|142|5|5|45|46896.30|0.00|0.02|N|O|1996-06-18|1996-06-20|1996-07-03|TAKE BACK RETURN|AIR|oss the ex| +1153|136|7|6|26|26939.38|0.02|0.03|N|O|1996-08-16|1996-07-12|1996-09-08|NONE|MAIL|kages haggle carefully. f| +1153|192|4|7|5|5460.95|0.02|0.03|N|O|1996-05-03|1996-06-12|1996-05-28|TAKE BACK RETURN|FOB|special excuses promi| +1154|143|10|1|31|32337.34|0.06|0.06|A|F|1992-04-17|1992-04-26|1992-05-17|COLLECT COD|AIR|ithely. final, blithe | +1154|148|7|2|50|52407.00|0.07|0.06|A|F|1992-04-22|1992-04-21|1992-05-01|NONE|TRUCK|ove the furiously bold Tires| +1154|97|1|3|5|4985.45|0.09|0.04|A|F|1992-06-07|1992-05-07|1992-07-05|DELIVER IN PERSON|MAIL|the furiously | +1154|1|2|4|35|31535.00|0.00|0.07|A|F|1992-03-30|1992-04-02|1992-04-21|DELIVER IN PERSON|TRUCK|the carefully regular pinto beans boost| +1154|36|2|5|18|16848.54|0.02|0.03|A|F|1992-02-26|1992-03-24|1992-03-20|TAKE BACK RETURN|REG AIR|y regular excuses cajole blithely. fi| +1154|196|8|6|50|54809.50|0.06|0.03|A|F|1992-03-04|1992-04-01|1992-04-01|TAKE BACK RETURN|TRUCK| even, special | +1155|70|1|1|4|3880.28|0.09|0.05|N|O|1997-10-19|1997-12-09|1997-11-02|DELIVER IN PERSON|SHIP|ic foxes according to the carefully final | +1155|196|9|2|39|42751.41|0.08|0.05|N|O|1998-01-29|1998-01-03|1998-02-01|COLLECT COD|TRUCK|ckly final pinto beans was.| +1155|147|4|3|23|24084.22|0.08|0.03|N|O|1997-11-24|1997-11-28|1997-12-06|DELIVER IN PERSON|FOB|ly unusual packages. iro| +1155|140|1|4|12|12481.68|0.01|0.06|N|O|1997-11-01|1998-01-03|1997-11-19|DELIVER IN PERSON|RAIL|packages do| +1155|5|2|5|49|44345.00|0.04|0.08|N|O|1997-12-07|1997-12-30|1997-12-08|NONE|AIR|ccounts are alongside of t| +1156|87|8|1|15|14806.20|0.07|0.06|N|O|1996-12-21|1997-01-03|1997-01-10|TAKE BACK RETURN|AIR|the furiously pen| +1156|33|4|2|21|19593.63|0.02|0.08|N|O|1996-11-07|1997-01-14|1996-12-03|NONE|AIR|dolphins. fluffily ironic packages sleep re| +1156|12|2|3|29|26448.29|0.09|0.06|N|O|1997-01-24|1996-12-26|1997-02-04|DELIVER IN PERSON|TRUCK|ts sleep sly| +1156|172|3|4|42|45031.14|0.02|0.00|N|O|1997-01-18|1997-01-12|1997-02-13|NONE|REG AIR|s. quickly bold pains are| +1156|74|4|5|49|47729.43|0.04|0.01|N|O|1996-11-16|1996-12-02|1996-12-05|COLLECT COD|AIR|ithely unusual in| +1156|195|9|6|42|45997.98|0.02|0.06|N|O|1997-01-27|1997-01-09|1997-01-28|DELIVER IN PERSON|MAIL|even requests boost ironic deposits. pe| +1156|47|6|7|20|18940.80|0.08|0.07|N|O|1997-01-01|1997-01-06|1997-01-16|COLLECT COD|MAIL|deposits sleep bravel| +1157|49|2|1|16|15184.64|0.06|0.00|N|O|1998-04-12|1998-03-09|1998-04-23|DELIVER IN PERSON|MAIL|tions hang| +1157|83|4|2|4|3932.32|0.10|0.05|N|O|1998-02-24|1998-03-30|1998-03-24|DELIVER IN PERSON|SHIP|ounts. ironic deposits| +1157|48|7|3|8|7584.32|0.02|0.00|N|O|1998-03-25|1998-03-16|1998-03-29|NONE|REG AIR|blithely even pa| +1157|77|8|4|46|44945.22|0.07|0.08|N|O|1998-04-19|1998-03-13|1998-04-23|NONE|FOB|slyly regular excuses. accounts| +1157|160|5|5|14|14842.24|0.03|0.03|N|O|1998-04-17|1998-03-03|1998-05-01|NONE|FOB|theodolites. fluffily re| +1158|45|2|1|5|4725.20|0.02|0.04|N|O|1996-10-20|1996-07-30|1996-11-14|COLLECT COD|AIR|symptotes along the care| +1158|157|9|2|23|24314.45|0.00|0.08|N|O|1996-10-21|1996-08-19|1996-10-31|COLLECT COD|MAIL|ularly ironic requests use care| +1159|109|10|1|39|39354.90|0.01|0.00|A|F|1992-11-20|1992-10-28|1992-12-18|TAKE BACK RETURN|FOB| blithely express reques| +1159|96|9|2|7|6972.63|0.08|0.00|A|F|1992-11-25|1992-10-27|1992-12-20|NONE|AIR|olve somet| +1159|98|10|3|11|10978.99|0.10|0.03|R|F|1992-12-09|1992-12-07|1992-12-18|DELIVER IN PERSON|MAIL|h furiousl| +1184|47|4|1|27|25570.08|0.01|0.00|N|O|1998-01-10|1997-12-02|1998-02-06|TAKE BACK RETURN|REG AIR|s wake fluffily. fl| +1184|147|10|2|4|4188.56|0.04|0.03|N|O|1997-12-25|1998-01-24|1998-01-18|DELIVER IN PERSON|RAIL| express packages. slyly expres| +1184|164|5|3|7|7449.12|0.05|0.00|N|O|1998-02-14|1998-01-06|1998-03-11|COLLECT COD|TRUCK|ckly warthogs. blithely bold foxes hag| +1184|126|9|4|3|3078.36|0.02|0.05|N|O|1998-01-15|1997-12-19|1998-02-02|NONE|REG AIR|ar packages. final packages cajol| +1185|72|1|1|8|7776.56|0.01|0.06|A|F|1992-12-05|1992-10-05|1992-12-28|DELIVER IN PERSON|MAIL|ely according to the furiously regular r| +1185|31|2|2|28|26068.84|0.07|0.06|A|F|1992-09-24|1992-10-07|1992-10-10|DELIVER IN PERSON|REG AIR|ke. slyly regular t| +1185|190|1|3|12|13082.28|0.05|0.06|R|F|1992-10-12|1992-09-26|1992-11-11|NONE|REG AIR|instructions. daringly pend| +1186|3|4|1|28|25284.00|0.08|0.07|N|O|1996-12-08|1996-10-17|1996-12-15|TAKE BACK RETURN|TRUCK|ffily spec| +1186|92|5|2|11|10912.99|0.07|0.05|N|O|1996-10-03|1996-10-21|1996-10-17|DELIVER IN PERSON|AIR|s haggle furiously; slyl| +1186|101|2|3|20|20022.00|0.07|0.07|N|O|1996-08-20|1996-10-23|1996-09-05|COLLECT COD|FOB|ely alongside of the blithel| +1186|106|7|4|27|27164.70|0.06|0.04|N|O|1996-10-08|1996-11-06|1996-10-09|TAKE BACK RETURN|SHIP|accounts. express, e| +1187|178|6|1|29|31266.93|0.01|0.04|R|F|1992-12-10|1993-02-09|1992-12-29|TAKE BACK RETURN|RAIL|riously express ac| +1187|131|7|2|15|15466.95|0.03|0.04|A|F|1992-12-22|1993-01-13|1993-01-01|NONE|TRUCK|ests. foxes wake. carefu| +1187|78|8|3|40|39122.80|0.08|0.06|R|F|1993-03-05|1992-12-31|1993-03-12|NONE|TRUCK|ar, brave deposits nag blithe| +1188|115|9|1|2|2030.22|0.00|0.04|N|O|1996-05-22|1996-05-23|1996-06-06|COLLECT COD|RAIL|its breach blit| +1188|113|4|2|9|9117.99|0.01|0.08|N|O|1996-08-04|1996-06-04|1996-08-19|NONE|REG AIR|ow carefully ironic d| +1188|179|10|3|41|44245.97|0.07|0.04|N|O|1996-06-29|1996-05-21|1996-07-21|TAKE BACK RETURN|TRUCK|althy packages. fluffily unusual ideas h| +1189|51|2|1|23|21874.15|0.06|0.00|R|F|1994-07-25|1994-06-07|1994-08-02|COLLECT COD|FOB|s. fluffy Tiresias run quickly. bra| +1189|105|2|2|32|32163.20|0.09|0.02|R|F|1994-05-06|1994-07-03|1994-05-15|TAKE BACK RETURN|FOB|e regular deposits. quickly quiet deposi| +1189|57|5|3|22|21055.10|0.05|0.03|R|F|1994-06-09|1994-06-29|1994-06-23|DELIVER IN PERSON|TRUCK|quickly unusual platelets lose forges. ca| +1190|84|5|1|32|31490.56|0.07|0.06|N|O|1997-05-08|1997-04-17|1997-06-01|COLLECT COD|FOB|y final packages? slyly even| +1191|49|6|1|29|27522.16|0.00|0.04|N|O|1996-01-24|1996-01-28|1996-02-17|COLLECT COD|AIR| regular pin| +1216|97|1|1|8|7976.72|0.03|0.04|R|F|1993-02-01|1993-03-06|1993-02-08|TAKE BACK RETURN|TRUCK| of the carefully express| +1216|75|3|2|48|46803.36|0.10|0.01|R|F|1993-01-17|1993-02-01|1993-02-13|COLLECT COD|SHIP|symptotes use against th| +1216|42|3|3|18|16956.72|0.00|0.03|A|F|1993-01-20|1993-01-28|1993-02-02|COLLECT COD|MAIL|y final packages nod | +1217|60|5|1|45|43202.70|0.07|0.02|A|F|1992-07-01|1992-06-23|1992-07-06|COLLECT COD|AIR|riously close ideas| +1218|140|6|1|16|16642.24|0.04|0.07|A|F|1994-06-26|1994-08-07|1994-06-30|TAKE BACK RETURN|FOB|ven realms be| +1218|94|6|2|41|40757.69|0.06|0.06|R|F|1994-08-04|1994-08-05|1994-08-11|TAKE BACK RETURN|SHIP|dolphins. theodolites beyond th| +1218|48|7|3|44|41713.76|0.07|0.06|A|F|1994-10-05|1994-09-03|1994-10-30|COLLECT COD|TRUCK|thely ironic accounts wake slyly| +1218|42|9|4|1|942.04|0.01|0.08|R|F|1994-09-15|1994-09-07|1994-10-03|COLLECT COD|TRUCK|press furio| +1219|132|3|1|6|6192.78|0.08|0.04|N|O|1995-11-13|1995-12-24|1995-11-18|NONE|MAIL|pecial, ironic requ| +1219|129|4|2|4|4116.48|0.01|0.04|N|O|1995-11-24|1995-11-22|1995-12-07|TAKE BACK RETURN|SHIP|lly quick requests. blithely even h| +1220|169|4|1|25|26729.00|0.10|0.03|N|O|1996-10-15|1996-11-07|1996-11-06|COLLECT COD|REG AIR| regular orbi| +1220|160|5|2|36|38165.76|0.01|0.02|N|O|1996-12-10|1996-11-14|1997-01-07|COLLECT COD|SHIP|ar packages. blithely final acc| +1220|37|8|3|3|2811.09|0.08|0.06|N|O|1996-09-06|1996-11-03|1996-09-10|COLLECT COD|REG AIR| final theodolites. blithely silent | +1220|6|1|4|36|32616.00|0.07|0.03|N|O|1996-12-12|1996-10-03|1996-12-15|TAKE BACK RETURN|TRUCK|unusual, silent pinto beans aga| +1220|49|2|5|25|23726.00|0.03|0.08|N|O|1996-09-11|1996-10-09|1996-09-25|DELIVER IN PERSON|RAIL|packages affi| +1221|81|2|1|43|42186.44|0.05|0.05|R|F|1992-06-22|1992-07-15|1992-07-20|DELIVER IN PERSON|FOB|y slyly above the slyly unusual ideas| +1221|170|1|2|12|12842.04|0.00|0.08|R|F|1992-08-07|1992-06-24|1992-08-13|COLLECT COD|AIR|yly ironic | +1221|69|6|3|3|2907.18|0.10|0.08|R|F|1992-07-01|1992-06-04|1992-07-27|COLLECT COD|TRUCK|ing to the fluffily| +1221|120|10|4|41|41824.92|0.06|0.02|A|F|1992-04-28|1992-07-02|1992-05-19|NONE|RAIL|ns. bold deposit| +1221|108|1|5|13|13105.30|0.10|0.00|R|F|1992-08-01|1992-06-29|1992-08-27|TAKE BACK RETURN|AIR|ajole furiously. blithely expres| +1221|85|6|6|7|6895.56|0.08|0.06|A|F|1992-06-27|1992-06-16|1992-07-23|TAKE BACK RETURN|RAIL|xpress accounts | +1222|72|10|1|12|11664.84|0.09|0.02|A|F|1993-02-12|1993-03-14|1993-03-12|TAKE BACK RETURN|RAIL|s print permanently unusual packages. | +1222|159|7|2|12|12709.80|0.08|0.01|A|F|1993-05-05|1993-03-27|1993-05-18|TAKE BACK RETURN|REG AIR| furiously bold instructions| +1222|8|1|3|26|23608.00|0.02|0.08|R|F|1993-02-13|1993-03-20|1993-02-22|TAKE BACK RETURN|MAIL|, even accounts are ironic| +1223|100|1|1|28|28002.80|0.10|0.06|N|O|1996-08-07|1996-07-24|1996-08-13|TAKE BACK RETURN|MAIL| quickly ironic requests. furious| +1248|164|5|1|45|47887.20|0.00|0.08|A|F|1992-04-17|1992-03-31|1992-05-13|NONE|RAIL|ter the pending pl| +1248|151|9|2|37|38892.55|0.06|0.06|R|F|1992-01-26|1992-02-05|1992-02-13|COLLECT COD|TRUCK|. final requests integrate quickly. blit| +1248|56|8|3|26|24857.30|0.09|0.06|A|F|1992-01-16|1992-03-01|1992-02-06|TAKE BACK RETURN|AIR| ironic dependen| +1248|156|7|4|49|51751.35|0.02|0.01|A|F|1992-04-24|1992-02-18|1992-05-03|TAKE BACK RETURN|AIR|beans run quickly according to the carefu| +1248|122|7|5|20|20442.40|0.08|0.00|A|F|1992-03-12|1992-03-23|1992-04-07|TAKE BACK RETURN|AIR|nal foxes cajole carefully slyl| +1248|62|9|6|30|28861.80|0.10|0.01|R|F|1992-02-01|1992-03-24|1992-02-08|TAKE BACK RETURN|MAIL|fily special foxes kindle am| +1249|59|4|1|49|46993.45|0.07|0.05|A|F|1994-03-03|1994-02-28|1994-03-08|NONE|RAIL|ffily express theodo| +1250|2|3|1|15|13530.00|0.10|0.06|A|F|1992-11-05|1992-12-17|1992-12-03|TAKE BACK RETURN|SHIP| regular, i| +1251|4|5|1|37|33448.00|0.08|0.08|N|O|1997-12-21|1998-01-12|1997-12-26|COLLECT COD|AIR|. furiously| +1251|78|9|2|36|35210.52|0.07|0.04|N|O|1997-11-29|1998-01-07|1997-12-03|TAKE BACK RETURN|RAIL|y ironic Tiresias are slyly furio| +1251|99|3|3|37|36966.33|0.09|0.02|N|O|1998-01-11|1997-12-01|1998-01-23|DELIVER IN PERSON|RAIL|finally bold requests| +1251|150|9|4|7|7351.05|0.07|0.00|N|O|1998-01-08|1997-12-27|1998-01-18|COLLECT COD|MAIL|riously pe| +1251|188|9|5|1|1088.18|0.02|0.03|N|O|1997-12-08|1998-01-06|1998-01-01|DELIVER IN PERSON|REG AIR| use quickly final packages. iron| +1252|87|8|1|13|12832.04|0.10|0.01|N|O|1997-09-07|1997-09-12|1997-10-01|COLLECT COD|REG AIR|sts dazzle| +1252|111|8|2|27|27299.97|0.00|0.08|N|O|1997-10-22|1997-10-10|1997-11-10|TAKE BACK RETURN|REG AIR|packages hag| +1252|40|1|3|19|17860.76|0.07|0.02|N|O|1997-10-13|1997-10-23|1997-10-18|NONE|AIR|ts wake carefully-- packages sleep. quick | +1252|92|4|4|11|10912.99|0.10|0.01|N|O|1997-10-16|1997-09-22|1997-10-28|COLLECT COD|AIR|s are. slyly final requests among the| +1252|79|10|5|26|25455.82|0.05|0.05|N|O|1997-08-05|1997-10-24|1997-08-07|DELIVER IN PERSON|SHIP|onic pinto beans haggle furiously | +1253|180|8|1|14|15122.52|0.00|0.06|R|F|1993-04-03|1993-04-16|1993-04-27|TAKE BACK RETURN|MAIL|lar foxes sleep furiously final, final pack| +1253|54|9|2|13|12402.65|0.01|0.06|A|F|1993-03-05|1993-04-26|1993-03-08|DELIVER IN PERSON|FOB|al packages| +1253|70|1|3|22|21341.54|0.05|0.06|A|F|1993-02-23|1993-04-06|1993-03-07|TAKE BACK RETURN|SHIP|telets cajole alongside of the final reques| +1253|176|5|4|23|24751.91|0.09|0.02|R|F|1993-04-18|1993-04-18|1993-05-07|COLLECT COD|FOB| the slyly silent re| +1253|114|8|5|19|19268.09|0.05|0.05|A|F|1993-04-01|1993-04-22|1993-04-14|TAKE BACK RETURN|AIR|al pinto bea| +1254|193|5|1|6|6559.14|0.08|0.01|N|O|1996-02-02|1996-03-21|1996-02-29|NONE|REG AIR|lithely even deposits eat!| +1254|200|3|2|47|51709.40|0.05|0.06|N|O|1996-03-07|1996-02-20|1996-04-05|COLLECT COD|MAIL| platelets cajol| +1254|135|6|3|35|36229.55|0.05|0.06|N|O|1996-04-08|1996-02-29|1996-04-18|DELIVER IN PERSON|FOB|ckages boost. furious warhorses cajole| +1255|192|4|1|12|13106.28|0.00|0.02|A|F|1994-08-17|1994-06-29|1994-09-04|TAKE BACK RETURN|REG AIR| regular, express accounts are | +1255|194|8|2|46|50332.74|0.07|0.05|R|F|1994-07-06|1994-07-14|1994-08-05|NONE|MAIL|ons nag qui| +1280|129|8|1|17|17495.04|0.01|0.01|A|F|1993-02-04|1993-04-10|1993-02-07|NONE|FOB|ructions integrate across the th| +1280|189|10|2|6|6535.08|0.05|0.06|R|F|1993-03-30|1993-02-16|1993-04-18|DELIVER IN PERSON|AIR|gular deposits | +1280|33|4|3|13|12129.39|0.03|0.02|R|F|1993-03-06|1993-03-11|1993-03-18|DELIVER IN PERSON|TRUCK|blithely final accounts use evenly | +1280|175|3|4|5|5375.85|0.06|0.03|R|F|1993-02-03|1993-02-11|1993-02-23|DELIVER IN PERSON|AIR|beans haggle. quickly bold instructions h| +1280|52|10|5|24|22849.20|0.07|0.02|R|F|1993-03-20|1993-03-01|1993-04-09|COLLECT COD|RAIL|y pending orbits boost after the slyly| +1280|66|3|6|9|8694.54|0.00|0.05|R|F|1993-04-18|1993-03-28|1993-05-04|DELIVER IN PERSON|FOB|usual accou| +1280|92|6|7|19|18849.71|0.02|0.06|A|F|1993-02-07|1993-02-28|1993-02-12|NONE|TRUCK|lyly along the furiously regular | +1281|138|4|1|33|34258.29|0.07|0.08|R|F|1995-02-01|1995-01-18|1995-03-03|NONE|REG AIR|dencies. thinly final pinto beans wake| +1281|7|2|2|37|33559.00|0.08|0.03|A|F|1995-03-19|1995-02-02|1995-03-27|NONE|AIR|ounts detect| +1281|94|7|3|2|1988.18|0.05|0.06|A|F|1994-12-27|1995-01-26|1995-01-21|TAKE BACK RETURN|FOB|ly unusual requests. final reques| +1281|154|2|4|38|40057.70|0.04|0.06|R|F|1995-03-28|1995-01-11|1995-04-14|TAKE BACK RETURN|MAIL| ideas-- blithely regular| +1281|152|10|5|13|13677.95|0.03|0.07|A|F|1995-02-06|1995-02-13|1995-02-18|DELIVER IN PERSON|TRUCK|fully final platelets wa| +1281|50|9|6|4|3800.20|0.07|0.04|R|F|1995-03-15|1995-02-21|1995-03-20|NONE|SHIP|ggle against the even requests. requests | +1281|78|6|7|43|42057.01|0.10|0.02|R|F|1995-01-28|1995-02-08|1995-02-10|DELIVER IN PERSON|AIR|final accounts. final packages slee| +1282|23|4|1|14|12922.28|0.04|0.02|R|F|1992-06-29|1992-04-05|1992-07-21|TAKE BACK RETURN|REG AIR|ecial deposit| +1282|30|9|2|10|9300.30|0.09|0.06|R|F|1992-04-10|1992-04-16|1992-05-01|DELIVER IN PERSON|SHIP|r theodolite| +1282|160|1|3|19|20143.04|0.01|0.03|R|F|1992-05-07|1992-04-07|1992-05-13|NONE|RAIL|ts x-ray across the furi| +1282|59|10|4|19|18221.95|0.00|0.05|A|F|1992-06-20|1992-04-17|1992-07-05|DELIVER IN PERSON|REG AIR|nto beans. carefully close theodo| +1283|93|7|1|47|46675.23|0.05|0.03|N|O|1996-10-21|1996-10-29|1996-11-12|DELIVER IN PERSON|TRUCK|even instructions boost slyly blithely | +1283|106|1|2|1|1006.10|0.00|0.08|N|O|1996-10-07|1996-10-12|1996-10-08|NONE|RAIL|d the sauternes. slyly ev| +1283|138|4|3|18|18686.34|0.02|0.01|N|O|1996-10-14|1996-11-07|1996-10-22|DELIVER IN PERSON|AIR|equests use along the fluff| +1283|192|4|4|40|43687.60|0.07|0.03|N|O|1996-11-09|1996-11-23|1996-11-28|NONE|MAIL|riously. even, ironic instructions after| +1283|124|9|5|43|44037.16|0.01|0.04|N|O|1996-09-29|1996-11-19|1996-10-26|TAKE BACK RETURN|RAIL|requests sleep slyly about the | +1283|8|5|6|30|27240.00|0.06|0.07|N|O|1996-11-22|1996-11-22|1996-12-15|COLLECT COD|TRUCK|t the fluffily| +1283|197|8|7|21|23040.99|0.04|0.03|N|O|1996-09-12|1996-10-02|1996-10-12|NONE|REG AIR|fully regular | +1284|178|7|1|49|52830.33|0.00|0.06|N|O|1996-04-11|1996-03-04|1996-04-16|NONE|MAIL|lar packages. special packages ac| +1284|6|7|2|4|3624.00|0.07|0.06|N|O|1996-02-29|1996-02-11|1996-03-01|TAKE BACK RETURN|TRUCK| regular asymptotes. | +1284|133|4|3|39|40292.07|0.08|0.00|N|O|1996-01-11|1996-02-07|1996-02-05|COLLECT COD|MAIL|even accoun| +1284|59|10|4|1|959.05|0.01|0.07|N|O|1996-04-28|1996-04-02|1996-05-08|DELIVER IN PERSON|SHIP|al packages use carefully express de| +1284|34|5|5|9|8406.27|0.05|0.06|N|O|1996-03-03|1996-03-19|1996-04-01|DELIVER IN PERSON|REG AIR|after the pending| +1285|22|3|1|12|11064.24|0.00|0.06|A|F|1992-06-21|1992-08-16|1992-07-12|COLLECT COD|MAIL|ss foxes. blithe theodolites cajole slyly| +1285|143|10|2|45|46941.30|0.01|0.02|R|F|1992-09-05|1992-08-08|1992-10-02|COLLECT COD|REG AIR| special requests haggle blithely.| +1285|189|10|3|4|4356.72|0.09|0.06|A|F|1992-07-20|1992-08-17|1992-07-26|DELIVER IN PERSON|FOB|l packages sleep slyly quiet i| +1285|188|9|4|39|42439.02|0.05|0.01|A|F|1992-09-15|1992-08-05|1992-10-05|DELIVER IN PERSON|TRUCK|uctions. car| +1285|84|5|5|33|32474.64|0.00|0.08|R|F|1992-09-08|1992-08-25|1992-09-16|NONE|SHIP|ites affix| +1286|178|9|1|49|52830.33|0.08|0.01|R|F|1993-06-24|1993-08-12|1993-06-26|DELIVER IN PERSON|SHIP|gged accoun| +1286|49|6|2|48|45553.92|0.01|0.04|A|F|1993-07-11|1993-07-11|1993-08-01|COLLECT COD|TRUCK|unts alongs| +1286|189|10|3|11|11980.98|0.03|0.04|R|F|1993-08-08|1993-07-30|1993-09-05|DELIVER IN PERSON|FOB| slyly even packages. requ| +1286|184|5|4|37|40114.66|0.00|0.02|R|F|1993-05-27|1993-07-11|1993-06-01|COLLECT COD|SHIP|lyly ironic pinto beans cajole furiously s| +1286|165|10|5|14|14912.24|0.00|0.01|R|F|1993-05-23|1993-08-09|1993-06-01|NONE|REG AIR|blithely bo| +1286|146|5|6|41|42891.74|0.04|0.05|R|F|1993-08-02|1993-08-06|1993-08-07|TAKE BACK RETURN|FOB| the furiously expre| +1287|174|3|1|35|37595.95|0.09|0.06|A|F|1994-09-07|1994-09-12|1994-09-30|TAKE BACK RETURN|FOB|s wake unusual grou| +1287|95|8|2|10|9950.90|0.08|0.03|R|F|1994-07-08|1994-08-28|1994-07-10|TAKE BACK RETURN|RAIL|thely alongside of the unusual, ironic pa| +1287|1|2|3|30|27030.00|0.00|0.07|R|F|1994-07-12|1994-09-23|1994-08-07|NONE|RAIL|ar packages. even, even| +1287|62|7|4|10|9620.60|0.01|0.05|A|F|1994-09-03|1994-08-12|1994-09-16|TAKE BACK RETURN|REG AIR|ding, regular accounts| +1287|179|8|5|21|22662.57|0.06|0.02|A|F|1994-10-06|1994-09-25|1994-10-16|TAKE BACK RETURN|TRUCK|y quickly bold theodoli| +1287|21|10|6|26|23946.52|0.03|0.08|R|F|1994-10-03|1994-09-27|1994-10-30|DELIVER IN PERSON|RAIL|egular foxes. theodolites nag along t| +1312|81|2|1|9|8829.72|0.04|0.08|R|F|1994-07-19|1994-06-29|1994-07-24|TAKE BACK RETURN|MAIL|. furiously | +1312|136|7|2|28|29011.64|0.06|0.06|A|F|1994-09-09|1994-08-01|1994-10-02|TAKE BACK RETURN|FOB|uriously final frays should use quick| +1312|173|1|3|18|19317.06|0.03|0.07|A|F|1994-09-13|1994-07-08|1994-09-22|TAKE BACK RETURN|MAIL|. slyly ironic| +1313|52|4|1|48|45698.40|0.01|0.03|A|F|1994-12-20|1994-10-29|1995-01-07|COLLECT COD|MAIL|s are quick| +1314|198|10|1|5|5490.95|0.03|0.01|A|F|1994-05-26|1994-08-06|1994-05-31|TAKE BACK RETURN|AIR|equests nag across the furious| +1314|110|5|2|39|39394.29|0.01|0.03|R|F|1994-08-09|1994-06-14|1994-08-31|TAKE BACK RETURN|TRUCK| unusual accounts slee| +1314|41|2|3|11|10351.44|0.01|0.04|A|F|1994-05-16|1994-07-30|1994-05-31|COLLECT COD|REG AIR|tegrate furious| +1315|96|8|1|27|26894.43|0.01|0.03|N|O|1998-07-04|1998-06-13|1998-07-28|NONE|SHIP|latelets. fluffily ironic account| +1315|16|6|2|15|13740.15|0.05|0.01|N|O|1998-07-12|1998-06-10|1998-08-07|COLLECT COD|AIR|. foxes integrate carefully special| +1315|168|3|3|25|26704.00|0.01|0.08|N|O|1998-06-26|1998-06-10|1998-07-06|TAKE BACK RETURN|FOB|lites. unusual foxes affi| +1315|161|6|4|19|20162.04|0.02|0.05|N|O|1998-07-05|1998-05-23|1998-08-04|TAKE BACK RETURN|SHIP|nal, regular warhorses about the fu| +1315|159|7|5|32|33892.80|0.10|0.05|N|O|1998-03-30|1998-06-12|1998-04-25|NONE|SHIP|neath the final p| +1316|127|6|1|46|47247.52|0.05|0.04|A|F|1994-01-13|1994-01-24|1994-02-03|COLLECT COD|TRUCK|ges haggle of the| +1316|79|9|2|15|14686.05|0.02|0.01|R|F|1994-03-12|1994-03-02|1994-03-14|COLLECT COD|FOB|se. furiously final depo| +1316|198|9|3|33|36240.27|0.10|0.06|R|F|1994-03-31|1994-01-23|1994-04-20|TAKE BACK RETURN|AIR|manently; blithely special deposits| +1316|66|3|4|15|14490.90|0.00|0.06|R|F|1993-12-17|1994-02-04|1993-12-20|NONE|RAIL|fully express dugouts. furiously silent ide| +1316|41|2|5|40|37641.60|0.01|0.03|R|F|1994-02-04|1994-02-09|1994-02-27|NONE|REG AIR|l dugouts. co| +1316|4|7|6|7|6328.00|0.05|0.04|A|F|1993-12-09|1994-01-12|1993-12-30|TAKE BACK RETURN|MAIL|. furiously even accounts a| +1316|163|8|7|8|8505.28|0.10|0.04|A|F|1994-03-26|1994-02-08|1994-04-19|NONE|SHIP|packages against the express requests wa| +1317|134|5|1|34|35160.42|0.08|0.04|N|O|1995-08-13|1995-08-08|1995-09-10|COLLECT COD|RAIL|deposits boost thinly blithely final id| +1317|160|2|2|7|7421.12|0.05|0.01|A|F|1995-06-08|1995-08-03|1995-06-16|TAKE BACK RETURN|SHIP| pinto beans according to the final, pend| +1317|158|9|3|26|27511.90|0.01|0.02|N|O|1995-07-13|1995-06-26|1995-08-06|COLLECT COD|RAIL|leep along th| +1317|106|3|4|35|35213.50|0.05|0.02|N|O|1995-07-16|1995-07-07|1995-07-22|TAKE BACK RETURN|FOB|r packages impress blithely car| +1317|150|9|5|36|37805.40|0.02|0.00|N|O|1995-09-03|1995-07-06|1995-09-04|DELIVER IN PERSON|AIR| deposits. quic| +1318|114|4|1|24|24338.64|0.08|0.06|N|O|1998-09-27|1998-09-15|1998-10-12|TAKE BACK RETURN|AIR|ual, unusual packages. fluffy, iro| +1318|46|3|2|26|24597.04|0.01|0.03|N|O|1998-09-26|1998-08-09|1998-10-07|DELIVER IN PERSON|FOB|ly. regular, u| +1318|129|4|3|31|31902.72|0.01|0.04|N|O|1998-08-25|1998-07-31|1998-08-31|COLLECT COD|AIR|ve the carefully expr| +1319|61|8|1|21|20182.26|0.03|0.04|N|O|1996-10-05|1996-12-02|1996-10-28|COLLECT COD|FOB|s: carefully express | +1319|37|8|2|12|11244.36|0.09|0.05|N|O|1996-11-05|1996-12-12|1996-11-29|DELIVER IN PERSON|TRUCK|packages integrate furiously. expres| +1344|141|4|1|15|15617.10|0.10|0.07|A|F|1992-06-22|1992-06-24|1992-06-23|TAKE BACK RETURN|MAIL|rding to the blithely ironic theodolite| +1344|190|1|2|29|31615.51|0.09|0.00|A|F|1992-07-17|1992-06-07|1992-07-21|NONE|REG AIR|ffily quiet foxes wake blithely. slyly | +1345|198|9|1|49|53811.31|0.08|0.00|A|F|1992-12-27|1993-01-23|1993-01-06|NONE|FOB|sly. furiously final accounts are blithely | +1345|12|9|2|37|33744.37|0.10|0.07|A|F|1992-11-27|1992-12-11|1992-12-07|COLLECT COD|FOB|e slyly express requests. ironic accounts c| +1345|57|8|3|31|29668.55|0.08|0.07|R|F|1992-12-02|1992-12-29|1992-12-14|COLLECT COD|REG AIR|. slyly silent accounts sublat| +1346|160|8|1|29|30744.64|0.07|0.05|A|F|1992-08-18|1992-09-15|1992-09-17|TAKE BACK RETURN|REG AIR|the pinto | +1346|125|6|2|48|49205.76|0.06|0.03|A|F|1992-09-28|1992-07-22|1992-10-13|TAKE BACK RETURN|REG AIR| along the carefully spec| +1346|54|5|3|13|12402.65|0.10|0.04|A|F|1992-07-22|1992-08-10|1992-08-06|NONE|SHIP|arefully brave deposits into the slyly iro| +1346|124|5|4|6|6144.72|0.02|0.02|R|F|1992-09-13|1992-07-21|1992-09-27|TAKE BACK RETURN|AIR|inst the furiously final theodolites. caref| +1346|187|8|5|30|32615.40|0.01|0.07|R|F|1992-10-01|1992-07-22|1992-10-24|NONE|SHIP| nag blithely. unusual, ru| +1346|16|6|6|45|41220.45|0.02|0.04|A|F|1992-09-11|1992-08-06|1992-09-12|COLLECT COD|FOB|press deposits.| +1347|81|2|1|45|44148.60|0.02|0.05|N|O|1997-08-24|1997-09-03|1997-09-08|COLLECT COD|AIR|ages wake around t| +1347|143|6|2|34|35466.76|0.07|0.04|N|O|1997-06-25|1997-09-08|1997-07-24|COLLECT COD|FOB|r packages. f| +1347|185|6|3|23|24959.14|0.03|0.04|N|O|1997-07-31|1997-08-25|1997-08-21|COLLECT COD|SHIP|ronic pinto beans. express reques| +1347|113|7|4|28|28367.08|0.01|0.00|N|O|1997-07-30|1997-07-22|1997-08-18|TAKE BACK RETURN|FOB|foxes after the blithely special i| +1347|65|6|5|9|8685.54|0.01|0.03|N|O|1997-08-28|1997-09-16|1997-09-26|DELIVER IN PERSON|AIR| detect blithely above the fina| +1347|153|8|6|21|22116.15|0.06|0.04|N|O|1997-10-10|1997-08-16|1997-11-02|NONE|FOB|g pinto beans affix car| +1347|51|3|7|10|9510.50|0.02|0.07|N|O|1997-07-04|1997-07-23|1997-07-05|DELIVER IN PERSON|SHIP|y ironic pin| +1348|95|7|1|13|12936.17|0.01|0.01|N|O|1998-04-28|1998-06-05|1998-05-12|TAKE BACK RETURN|SHIP| blithely r| +1348|22|5|2|41|37802.82|0.07|0.03|N|O|1998-05-02|1998-05-26|1998-05-09|COLLECT COD|RAIL|kages. platelets about the ca| +1348|199|10|3|40|43967.60|0.07|0.05|N|O|1998-08-14|1998-07-10|1998-08-27|COLLECT COD|AIR|fter the regu| +1348|98|1|4|2|1996.18|0.01|0.04|N|O|1998-05-30|1998-06-20|1998-06-05|COLLECT COD|MAIL|lly final packages use fluffily express ac| +1349|181|2|1|1|1081.18|0.06|0.03|N|O|1998-01-07|1998-01-14|1998-02-03|COLLECT COD|REG AIR| express inst| +1349|118|2|2|45|45814.95|0.03|0.02|N|O|1997-12-24|1998-01-17|1997-12-28|NONE|AIR| ironic, unusual deposits wake carefu| +1350|54|9|1|21|20035.05|0.04|0.04|A|F|1993-12-17|1993-10-17|1993-12-25|COLLECT COD|REG AIR|lyly above the evenly | +1350|44|5|2|32|30209.28|0.03|0.00|R|F|1993-11-18|1993-09-30|1993-12-16|COLLECT COD|MAIL|ic, final | +1351|108|9|1|25|25202.50|0.06|0.04|N|O|1998-06-02|1998-05-25|1998-06-22|COLLECT COD|SHIP|iously regul| +1376|169|8|1|22|23521.52|0.01|0.03|N|O|1997-08-05|1997-07-08|1997-09-03|NONE|REG AIR|inst the final, pending | +1377|154|6|1|5|5270.75|0.06|0.05|N|O|1998-05-06|1998-07-08|1998-06-01|TAKE BACK RETURN|FOB| final, final grouches. accoun| +1377|33|9|2|3|2799.09|0.10|0.04|N|O|1998-04-30|1998-07-02|1998-05-14|DELIVER IN PERSON|REG AIR|yly enticing requ| +1377|84|5|3|26|25586.08|0.07|0.07|N|O|1998-05-28|1998-06-11|1998-06-25|COLLECT COD|SHIP|egular deposits. quickly regular acco| +1377|121|4|4|39|39823.68|0.00|0.03|N|O|1998-07-27|1998-07-18|1998-08-13|DELIVER IN PERSON|SHIP|e ironic, regular requests. carefully | +1377|33|9|5|19|17727.57|0.10|0.00|N|O|1998-06-20|1998-06-27|1998-07-20|NONE|AIR|ught to are bold foxes| +1377|154|6|6|17|17920.55|0.03|0.04|N|O|1998-06-19|1998-07-20|1998-07-14|NONE|REG AIR|s must have to mold b| +1378|197|10|1|34|37304.46|0.09|0.07|N|O|1996-07-08|1996-04-23|1996-07-09|COLLECT COD|RAIL|le furiously slyly final accounts. careful| +1378|124|9|2|18|18434.16|0.05|0.02|N|O|1996-06-19|1996-05-16|1996-06-21|DELIVER IN PERSON|RAIL| theodolites. i| +1378|73|4|3|11|10703.77|0.10|0.03|N|O|1996-06-07|1996-05-09|1996-07-05|COLLECT COD|TRUCK| blithely express hoc| +1378|171|2|4|12|12854.04|0.02|0.06|N|O|1996-06-16|1996-05-23|1996-07-09|COLLECT COD|SHIP|notornis. b| +1378|156|7|5|9|9505.35|0.06|0.05|N|O|1996-04-20|1996-04-13|1996-05-09|COLLECT COD|REG AIR|e carefully. carefully iron| +1378|194|6|6|29|31731.51|0.05|0.05|N|O|1996-04-15|1996-04-23|1996-05-14|NONE|REG AIR|ual packages are furiously blith| +1379|73|3|1|13|12649.91|0.04|0.01|N|O|1998-06-08|1998-07-13|1998-06-16|NONE|AIR|ully across the furiously iron| +1379|118|2|2|50|50905.50|0.07|0.08|N|O|1998-08-31|1998-07-13|1998-09-02|TAKE BACK RETURN|FOB|olphins. ca| +1379|13|7|3|24|21912.24|0.05|0.02|N|O|1998-07-06|1998-07-09|1998-07-29|DELIVER IN PERSON|MAIL|ages cajole carefully idly express re| +1380|149|2|1|6|6294.84|0.00|0.04|N|O|1996-08-06|1996-10-01|1996-08-14|NONE|RAIL|e foxes. slyly specia| +1380|141|4|2|40|41645.60|0.02|0.02|N|O|1996-10-01|1996-08-14|1996-10-20|COLLECT COD|RAIL|ly final frets. ironic,| +1380|78|9|3|15|14671.05|0.05|0.02|N|O|1996-07-14|1996-08-12|1996-08-03|NONE|FOB|riously ironic foxes aff| +1380|61|10|4|33|31714.98|0.04|0.07|N|O|1996-08-23|1996-10-01|1996-09-18|TAKE BACK RETURN|SHIP|e ironic, even excuses haggle | +1381|144|1|1|47|49074.58|0.08|0.04|N|O|1998-09-22|1998-08-12|1998-10-12|DELIVER IN PERSON|AIR|ly ironic deposits| +1381|34|10|2|12|11208.36|0.07|0.08|N|O|1998-08-13|1998-08-12|1998-08-28|TAKE BACK RETURN|AIR| furiously regular package| +1382|162|3|1|18|19118.88|0.08|0.03|R|F|1993-08-30|1993-10-19|1993-09-03|DELIVER IN PERSON|AIR|hely regular deposits. fluffy s| +1382|181|2|2|29|31354.22|0.08|0.04|A|F|1993-10-08|1993-11-11|1993-10-10|COLLECT COD|FOB| haggle: closely even asymptot| +1382|178|7|3|43|46361.31|0.10|0.04|A|F|1993-09-02|1993-10-06|1993-09-15|DELIVER IN PERSON|AIR|ress deposits. slyly ironic foxes are blit| +1382|181|2|4|11|11892.98|0.04|0.04|R|F|1993-09-17|1993-09-29|1993-09-21|NONE|SHIP|furiously unusual packages play quickly | +1382|157|8|5|31|32771.65|0.07|0.03|R|F|1993-10-26|1993-10-15|1993-11-09|TAKE BACK RETURN|FOB|hely regular dependencies. f| +1382|10|5|6|38|34580.38|0.07|0.07|R|F|1993-11-17|1993-09-28|1993-11-20|COLLECT COD|SHIP|ake pending pinto beans. s| +1382|23|4|7|5|4615.10|0.07|0.01|R|F|1993-10-02|1993-09-29|1993-10-12|DELIVER IN PERSON|REG AIR|ter the carefully final excuses. blit| +1383|193|7|1|14|15304.66|0.07|0.06|A|F|1993-08-25|1993-07-09|1993-09-12|DELIVER IN PERSON|RAIL|ole carefully silent requests. car| +1383|161|10|2|19|20162.04|0.06|0.04|R|F|1993-05-24|1993-07-07|1993-06-14|NONE|AIR|lyly unusual accounts sle| +1408|148|7|1|29|30396.06|0.03|0.04|N|O|1998-03-12|1998-02-14|1998-03-17|COLLECT COD|MAIL|en accounts grow furiousl| +1408|173|2|2|7|7512.19|0.05|0.06|N|O|1998-01-14|1998-03-21|1998-01-29|COLLECT COD|AIR|fully final instructions. theodolites ca| +1408|76|6|3|11|10736.77|0.00|0.03|N|O|1998-04-04|1998-01-29|1998-04-18|NONE|REG AIR|y even accounts thrash care| +1408|148|5|4|20|20962.80|0.06|0.00|N|O|1998-04-21|1998-01-25|1998-05-12|DELIVER IN PERSON|TRUCK| blithely fluffi| +1408|170|1|5|41|43876.97|0.02|0.06|N|O|1998-02-25|1998-02-03|1998-03-13|COLLECT COD|REG AIR|ep along the fina| +1408|134|10|6|42|43433.46|0.05|0.08|N|O|1998-01-30|1998-02-07|1998-02-18|TAKE BACK RETURN|REG AIR|even packages. even accounts cajole| +1408|55|6|7|26|24831.30|0.00|0.00|N|O|1998-03-19|1998-03-14|1998-04-01|COLLECT COD|RAIL|ic foxes ca| +1409|99|1|1|23|22979.07|0.01|0.03|A|F|1993-04-18|1993-02-25|1993-05-06|DELIVER IN PERSON|FOB|ions. slyly ironic packages wake quick| +1409|65|2|2|36|34742.16|0.09|0.02|A|F|1993-01-27|1993-01-31|1993-02-07|COLLECT COD|FOB|ncies sleep carefully r| +1409|160|1|3|17|18022.72|0.07|0.00|R|F|1993-04-15|1993-03-01|1993-04-29|NONE|REG AIR|pending accounts poach. care| +1410|121|10|1|15|15316.80|0.06|0.05|N|O|1997-05-25|1997-07-08|1997-06-15|NONE|SHIP| bold packages are fluf| +1410|179|9|2|18|19425.06|0.03|0.00|N|O|1997-06-03|1997-05-17|1997-06-07|TAKE BACK RETURN|RAIL|gle furiously fluffily regular requests| +1410|109|4|3|37|37336.70|0.02|0.01|N|O|1997-04-17|1997-06-18|1997-04-19|COLLECT COD|TRUCK|to beans b| +1410|188|9|4|22|23939.96|0.10|0.00|N|O|1997-07-31|1997-05-17|1997-08-19|TAKE BACK RETURN|RAIL|gular account| +1410|66|1|5|25|24151.50|0.09|0.02|N|O|1997-05-07|1997-07-10|1997-05-16|NONE|REG AIR|unts haggle against the furiously fina| +1411|17|7|1|9|8253.09|0.06|0.04|A|F|1995-03-08|1995-03-04|1995-03-11|DELIVER IN PERSON|AIR|accounts. furiou| +1411|107|8|2|26|26184.60|0.02|0.02|A|F|1995-04-12|1995-01-24|1995-05-03|TAKE BACK RETURN|TRUCK|c packages. | +1411|27|6|3|37|34299.74|0.00|0.06|A|F|1995-02-27|1995-03-02|1995-03-24|NONE|MAIL|d excuses. furiously final pear| +1411|200|3|4|20|22004.00|0.01|0.03|R|F|1995-04-06|1995-03-16|1995-04-17|COLLECT COD|FOB|s against the| +1411|83|4|5|46|45221.68|0.08|0.05|A|F|1995-04-03|1995-01-20|1995-04-05|DELIVER IN PERSON|REG AIR|ly daring instructions| +1411|77|6|6|30|29312.10|0.09|0.04|A|F|1995-01-12|1995-02-01|1995-01-23|DELIVER IN PERSON|MAIL|ious foxes wake courts. caref| +1412|58|3|1|37|35447.85|0.06|0.01|A|F|1993-04-10|1993-04-19|1993-04-12|DELIVER IN PERSON|RAIL|hely express excuses are | +1412|156|1|2|20|21123.00|0.10|0.05|A|F|1993-07-04|1993-05-18|1993-07-22|DELIVER IN PERSON|REG AIR|odolites sleep ironically| +1412|23|2|3|2|1846.04|0.10|0.07|R|F|1993-04-01|1993-05-03|1993-04-12|DELIVER IN PERSON|REG AIR|s among the requests are a| +1412|167|8|4|11|11738.76|0.05|0.07|R|F|1993-05-27|1993-05-30|1993-06-07|DELIVER IN PERSON|MAIL|en packages. regular packages dete| +1412|158|6|5|11|11639.65|0.08|0.06|A|F|1993-03-30|1993-05-25|1993-04-21|NONE|FOB|se slyly. special, unusual accounts nag bl| +1413|178|9|1|18|19407.06|0.08|0.05|N|O|1997-10-11|1997-08-17|1997-10-25|NONE|FOB|yly bold packages haggle quickly acr| +1413|165|10|2|49|52192.84|0.07|0.06|N|O|1997-08-28|1997-08-23|1997-09-12|DELIVER IN PERSON|MAIL|nstructions br| +1413|42|9|3|6|5652.24|0.04|0.02|N|O|1997-09-07|1997-07-30|1997-09-21|TAKE BACK RETURN|MAIL|lithely excuses. f| +1414|38|4|1|39|36583.17|0.10|0.03|N|O|1995-09-22|1995-09-30|1995-10-07|NONE|MAIL|quickly aro| +1414|107|8|2|4|4028.40|0.02|0.05|N|O|1995-09-16|1995-11-01|1995-10-02|COLLECT COD|AIR| haggle quickly| +1415|149|10|1|25|26228.50|0.06|0.00|A|F|1994-09-03|1994-07-12|1994-09-13|DELIVER IN PERSON|RAIL|ect never fluff| +1440|193|6|1|3|3279.57|0.06|0.01|N|O|1995-10-30|1995-10-17|1995-11-08|COLLECT COD|SHIP|instructions boost. fluffily regul| +1440|114|4|2|46|46649.06|0.02|0.03|N|O|1995-09-21|1995-10-19|1995-10-19|NONE|RAIL|blithely even instructions. | +1441|144|7|1|5|5220.70|0.04|0.01|N|O|1997-05-17|1997-05-11|1997-05-30|COLLECT COD|MAIL|egular courts. fluffily even grouches | +1441|177|7|2|5|5385.85|0.02|0.05|N|O|1997-04-25|1997-04-16|1997-05-23|COLLECT COD|FOB|he quickly enticing pac| +1441|118|5|3|14|14253.54|0.01|0.03|N|O|1997-06-30|1997-04-29|1997-07-24|DELIVER IN PERSON|REG AIR|special requests ha| +1441|160|8|4|37|39225.92|0.01|0.00|N|O|1997-04-26|1997-04-27|1997-04-29|NONE|REG AIR|accounts. slyly special dolphins b| +1441|72|10|5|34|33050.38|0.09|0.00|N|O|1997-06-12|1997-05-11|1997-06-29|TAKE BACK RETURN|RAIL|e carefully. blithely ironic dep| +1441|25|4|6|15|13875.30|0.09|0.08|N|O|1997-05-21|1997-05-06|1997-06-04|NONE|REG AIR| dependencies-- cour| +1441|96|10|7|50|49804.50|0.03|0.01|N|O|1997-06-07|1997-05-12|1997-06-08|NONE|SHIP| requests. blithely e| +1442|26|5|1|8|7408.16|0.05|0.01|A|F|1994-10-31|1994-09-04|1994-11-25|COLLECT COD|AIR|c deposits haggle after the even| +1443|34|10|1|47|43899.41|0.04|0.06|N|O|1997-02-05|1997-02-02|1997-03-03|NONE|RAIL|carefully ironic requests sl| +1444|170|5|1|42|44947.14|0.01|0.02|R|F|1994-12-22|1995-03-03|1994-12-31|NONE|SHIP|ly bold packages boost regular ideas. spe| +1444|57|2|2|34|32539.70|0.04|0.08|A|F|1995-02-22|1995-02-15|1995-03-19|TAKE BACK RETURN|AIR|y. doggedly pend| +1444|155|3|3|34|35875.10|0.02|0.07|R|F|1994-12-17|1995-01-12|1995-01-03|COLLECT COD|AIR|ular accounts | +1444|119|6|4|6|6114.66|0.06|0.03|A|F|1995-01-07|1995-03-05|1995-01-17|COLLECT COD|RAIL|al accounts. br| +1444|20|1|5|35|32200.70|0.02|0.05|A|F|1995-02-25|1995-03-05|1995-03-24|DELIVER IN PERSON|SHIP|aggle furiou| +1444|33|4|6|42|39187.26|0.00|0.02|A|F|1994-12-16|1995-02-18|1994-12-22|DELIVER IN PERSON|RAIL|ss requests. ironic ideas wake above| +1444|82|3|7|12|11784.96|0.00|0.03|R|F|1994-12-23|1995-01-15|1995-01-13|COLLECT COD|TRUCK|ly among the bol| +1445|100|1|1|24|24002.40|0.01|0.00|A|F|1995-02-21|1995-02-22|1995-03-18|DELIVER IN PERSON|SHIP|al accounts use furiously a| +1445|67|8|2|48|46418.88|0.10|0.02|A|F|1995-02-28|1995-03-16|1995-03-12|COLLECT COD|MAIL|. final ideas are carefully dar| +1445|192|4|3|7|7645.33|0.10|0.04|A|F|1995-04-25|1995-02-25|1995-05-10|NONE|SHIP|structions: slyly regular re| +1445|28|1|4|17|15776.34|0.04|0.07|A|F|1995-04-02|1995-04-04|1995-05-01|COLLECT COD|FOB|ges. furiously regular pint| +1445|135|1|5|24|24843.12|0.10|0.06|R|F|1995-04-23|1995-02-16|1995-05-18|NONE|REG AIR|rate after the carefully reg| +1445|168|9|6|39|41658.24|0.03|0.02|A|F|1995-02-05|1995-02-20|1995-02-06|NONE|MAIL|ully unusual reques| +1446|72|3|1|31|30134.17|0.10|0.02|N|O|1998-05-01|1998-05-17|1998-05-30|NONE|REG AIR|. slyly reg| +1447|167|4|1|19|20276.04|0.06|0.04|A|F|1993-01-31|1992-12-07|1993-02-04|COLLECT COD|MAIL|. quickly ironic | +1447|32|3|2|6|5592.18|0.01|0.05|A|F|1992-10-24|1992-12-10|1992-11-05|DELIVER IN PERSON|AIR|as! regular packages poach above the| +1447|39|5|3|9|8451.27|0.04|0.00|R|F|1992-11-15|1993-01-07|1992-11-29|DELIVER IN PERSON|MAIL|counts wake s| +1447|22|5|4|8|7376.16|0.09|0.08|R|F|1992-11-20|1993-01-12|1992-12-14|COLLECT COD|FOB|ost carefully | +1447|130|1|5|23|23692.99|0.02|0.07|A|F|1992-12-07|1992-12-25|1993-01-06|TAKE BACK RETURN|AIR| dazzle quickly deposits. f| +1447|200|3|6|41|45108.20|0.08|0.02|R|F|1993-01-06|1993-01-05|1993-01-13|TAKE BACK RETURN|MAIL|rts boost s| +1472|8|5|1|36|32688.00|0.04|0.05|N|O|1996-11-06|1996-11-13|1996-11-12|COLLECT COD|SHIP|riously silent deposits to the pending d| +1472|133|4|2|26|26861.38|0.03|0.05|N|O|1996-11-08|1996-11-13|1996-12-02|DELIVER IN PERSON|FOB|ic packages w| +1472|1|8|3|6|5406.00|0.08|0.01|N|O|1996-10-24|1996-11-19|1996-11-23|COLLECT COD|FOB|onic theodolites hinder slyly slyly r| +1473|54|9|1|50|47702.50|0.04|0.03|N|O|1997-05-05|1997-05-20|1997-05-09|NONE|TRUCK|requests wake express deposits. special, ir| +1473|68|3|2|32|30977.92|0.00|0.08|N|O|1997-04-18|1997-05-12|1997-05-10|DELIVER IN PERSON|REG AIR|out the packages lose furiously ab| +1474|15|5|1|5|4575.05|0.05|0.04|A|F|1995-04-22|1995-02-20|1995-05-06|COLLECT COD|SHIP|ully final a| +1474|123|8|2|30|30693.60|0.04|0.02|A|F|1995-03-23|1995-02-11|1995-04-17|DELIVER IN PERSON|TRUCK|usly. evenly express | +1474|92|5|3|18|17857.62|0.06|0.02|A|F|1995-01-23|1995-03-28|1995-02-03|NONE|RAIL|after the special| +1475|168|3|1|15|16022.40|0.08|0.06|N|O|1998-02-12|1997-12-17|1998-03-02|TAKE BACK RETURN|SHIP|xpress requests haggle after the final, fi| +1475|118|9|2|18|18325.98|0.07|0.00|N|O|1998-03-08|1998-01-18|1998-03-10|TAKE BACK RETURN|AIR|al deposits use. ironic packages along the | +1475|144|1|3|30|31324.20|0.03|0.02|N|O|1998-03-11|1997-12-30|1998-03-15|COLLECT COD|REG AIR| regular theodolites mold across th| +1475|187|8|4|50|54359.00|0.03|0.05|N|O|1997-12-14|1997-12-13|1997-12-21|COLLECT COD|AIR|. slyly bold re| +1475|32|3|5|33|30756.99|0.01|0.06|N|O|1998-01-02|1998-01-27|1998-01-11|NONE|FOB|quickly fluffy| +1475|50|7|6|12|11400.60|0.04|0.04|N|O|1998-01-09|1997-12-30|1998-01-23|NONE|TRUCK|arefully-- excuses sublate| +1475|112|3|7|23|23278.53|0.02|0.00|N|O|1998-02-13|1998-02-05|1998-03-08|NONE|TRUCK|hely regular hocke| +1476|31|7|1|20|18620.60|0.02|0.03|N|O|1996-08-11|1996-09-18|1996-08-26|TAKE BACK RETURN|AIR|. bold deposits are carefully amo| +1477|72|1|1|31|30134.17|0.00|0.06|N|O|1997-12-16|1997-09-30|1997-12-17|COLLECT COD|RAIL| requests. fluffily final | +1477|110|7|2|8|8080.88|0.09|0.05|N|O|1997-10-25|1997-10-18|1997-11-16|COLLECT COD|MAIL|ironic realms wake unusual, even ac| +1477|125|6|3|42|43055.04|0.06|0.00|N|O|1997-11-02|1997-11-02|1997-11-20|DELIVER IN PERSON|SHIP|lithely after the ir| +1477|107|8|4|32|32227.20|0.05|0.08|N|O|1997-09-12|1997-10-26|1997-10-12|TAKE BACK RETURN|AIR|; quickly regula| +1477|115|6|5|41|41619.51|0.04|0.06|N|O|1997-12-16|1997-10-31|1998-01-12|DELIVER IN PERSON|REG AIR|y. final pearls kindle. accounts | +1477|69|6|6|49|47483.94|0.06|0.00|N|O|1997-11-18|1997-11-06|1997-11-27|COLLECT COD|FOB|ise according to the sly, bold p| +1477|120|4|7|33|33663.96|0.06|0.00|N|O|1997-11-12|1997-11-06|1997-11-24|DELIVER IN PERSON|TRUCK|yly regular p| +1478|34|5|1|21|19614.63|0.00|0.06|N|O|1997-09-20|1997-10-25|1997-10-06|TAKE BACK RETURN|MAIL| fluffily pending acc| +1479|149|6|1|33|34621.62|0.10|0.01|N|O|1996-03-12|1996-02-28|1996-03-31|DELIVER IN PERSON|FOB| carefully special courts affix. fluff| +1504|82|3|1|42|41247.36|0.02|0.03|R|F|1992-10-18|1992-10-14|1992-11-10|TAKE BACK RETURN|FOB|ep. carefully ironic excuses haggle quickl| +1504|103|10|2|22|22068.20|0.04|0.03|A|F|1992-09-09|1992-10-29|1992-09-10|NONE|REG AIR| accounts sleep. furiou| +1504|178|8|3|9|9703.53|0.07|0.02|R|F|1992-11-02|1992-10-12|1992-11-15|TAKE BACK RETURN|RAIL|y slyly regular courts.| +1504|115|2|4|10|10151.10|0.04|0.07|A|F|1992-09-22|1992-10-22|1992-10-13|TAKE BACK RETURN|TRUCK|final theodolites. furiously e| +1504|20|10|5|7|6440.14|0.02|0.00|R|F|1992-11-20|1992-11-23|1992-12-13|COLLECT COD|MAIL|y final packa| +1505|120|7|1|4|4080.48|0.09|0.00|A|F|1992-12-14|1992-11-11|1993-01-02|COLLECT COD|SHIP|side of the s| +1505|123|8|2|50|51156.00|0.00|0.02|R|F|1992-11-22|1992-09-24|1992-11-26|TAKE BACK RETURN|FOB|lyly special platelets. requests ar| +1506|133|4|1|46|47523.98|0.04|0.05|R|F|1993-01-18|1992-11-11|1993-02-09|COLLECT COD|REG AIR|sits whithout the blithely ironic packages| +1506|114|4|2|30|30423.30|0.07|0.02|A|F|1992-11-22|1992-10-25|1992-12-04|DELIVER IN PERSON|FOB|deposits cajole | +1506|191|3|3|28|30553.32|0.10|0.06|A|F|1992-09-22|1992-11-19|1992-10-09|TAKE BACK RETURN|AIR| unwind carefully: theodolit| +1506|28|7|4|37|34336.74|0.00|0.03|R|F|1992-11-04|1992-12-01|1992-11-23|TAKE BACK RETURN|TRUCK|carefully bold dolphins. accounts su| +1506|195|8|5|15|16427.85|0.05|0.00|R|F|1992-09-24|1992-11-11|1992-10-05|NONE|REG AIR| carefully fluffy packages-- caref| +1506|50|3|6|38|36101.90|0.05|0.02|R|F|1992-12-02|1992-12-19|1992-12-29|NONE|REG AIR|xpress, regular excuse| +1506|169|6|7|4|4276.64|0.07|0.00|R|F|1993-01-03|1992-12-06|1993-01-05|COLLECT COD|REG AIR|posits. furiou| +1507|68|5|1|25|24201.50|0.01|0.08|R|F|1994-01-07|1994-01-06|1994-01-11|NONE|RAIL|xes. slyly busy de| +1507|40|6|2|33|31021.32|0.04|0.02|A|F|1993-10-29|1993-12-23|1993-11-14|DELIVER IN PERSON|REG AIR| asymptotes nag furiously above t| +1507|86|7|3|39|38457.12|0.03|0.07|R|F|1993-11-04|1993-12-16|1993-12-03|TAKE BACK RETURN|REG AIR|ly even instructions.| +1508|51|3|1|16|15216.80|0.02|0.06|N|O|1998-06-21|1998-05-30|1998-07-11|COLLECT COD|MAIL|riously across the ironic, unusua| +1508|25|4|2|20|18500.40|0.06|0.01|N|O|1998-04-17|1998-06-11|1998-05-17|DELIVER IN PERSON|MAIL|nic platelets. carefully final fra| +1508|93|7|3|43|42702.87|0.01|0.02|N|O|1998-06-01|1998-06-24|1998-06-03|TAKE BACK RETURN|TRUCK|ndencies h| +1508|148|7|4|1|1048.14|0.02|0.02|N|O|1998-07-13|1998-06-03|1998-07-17|TAKE BACK RETURN|AIR|s the blithely bold instruction| +1508|135|6|5|29|30018.77|0.02|0.00|N|O|1998-08-03|1998-07-08|1998-08-22|COLLECT COD|RAIL|r instructions. carefully| +1508|3|10|6|5|4515.00|0.06|0.08|N|O|1998-05-22|1998-07-06|1998-06-04|COLLECT COD|REG AIR|cording to the furiously ironic depe| +1508|117|8|7|38|38650.18|0.03|0.06|N|O|1998-04-30|1998-06-23|1998-05-18|DELIVER IN PERSON|RAIL|tes wake furiously regular w| +1509|28|7|1|14|12992.28|0.04|0.01|A|F|1993-10-04|1993-09-25|1993-10-21|NONE|TRUCK|nal realms| +1509|11|2|2|46|41906.46|0.08|0.02|A|F|1993-10-15|1993-10-04|1993-11-01|TAKE BACK RETURN|FOB|uriously regula| +1509|107|8|3|17|17120.70|0.06|0.05|A|F|1993-07-25|1993-08-28|1993-08-19|DELIVER IN PERSON|AIR| furiously. blithely regular ideas haggle c| +1509|20|4|4|11|10120.22|0.03|0.08|R|F|1993-11-04|1993-10-03|1993-11-14|TAKE BACK RETURN|FOB|ily ironic packages nod carefully.| +1509|90|1|5|37|36633.33|0.01|0.08|A|F|1993-08-31|1993-09-10|1993-09-24|NONE|FOB|he slyly even deposits wake a| +1509|187|8|6|31|33702.58|0.04|0.03|A|F|1993-07-14|1993-08-21|1993-08-06|COLLECT COD|SHIP|ic deposits cajole carefully. quickly bold | +1509|157|2|7|27|28543.05|0.01|0.01|A|F|1993-09-29|1993-09-08|1993-10-04|TAKE BACK RETURN|FOB|lithely after the | +1510|98|2|1|11|10978.99|0.09|0.04|N|O|1996-09-23|1996-12-03|1996-10-01|DELIVER IN PERSON|RAIL|e of the unusual accounts. stealthy deposit| +1510|84|5|2|24|23617.92|0.05|0.04|N|O|1996-10-07|1996-10-22|1996-11-03|DELIVER IN PERSON|REG AIR|yly brave theod| +1510|190|1|3|36|39246.84|0.07|0.02|N|O|1996-10-02|1996-11-23|1996-10-05|NONE|SHIP|old deposits along the carefully| +1510|182|3|4|8|8657.44|0.01|0.08|N|O|1996-10-26|1996-11-07|1996-10-30|TAKE BACK RETURN|RAIL|blithely express| +1510|59|10|5|27|25894.35|0.08|0.06|N|O|1996-10-20|1996-12-05|1996-11-02|NONE|MAIL|he blithely regular req| +1510|14|5|6|3|2742.03|0.05|0.02|N|O|1996-10-31|1996-12-03|1996-11-13|COLLECT COD|RAIL|along the slyly regular pin| +1510|22|1|7|50|46101.00|0.04|0.05|N|O|1996-11-01|1996-10-17|1996-11-28|NONE|MAIL|even packages. carefully regular fo| +1511|98|2|1|29|28944.61|0.01|0.04|N|O|1997-03-17|1997-02-11|1997-03-27|DELIVER IN PERSON|AIR|s cajole furiously against | +1511|62|9|2|32|30785.92|0.04|0.01|N|O|1997-01-06|1997-03-21|1997-01-26|TAKE BACK RETURN|REG AIR| deposits. carefully ironi| +1536|194|5|1|5|5470.95|0.08|0.03|N|O|1997-02-08|1997-03-11|1997-03-02|COLLECT COD|MAIL|requests sleep pe| +1537|18|2|1|17|15606.17|0.01|0.03|A|F|1992-04-12|1992-04-19|1992-04-13|NONE|TRUCK|he regular pack| +1537|179|8|2|50|53958.50|0.08|0.00|R|F|1992-05-30|1992-05-14|1992-06-23|TAKE BACK RETURN|MAIL|special packages haggle slyly at the silent| +1537|13|4|3|44|40172.44|0.05|0.04|R|F|1992-04-01|1992-03-31|1992-04-21|NONE|TRUCK|lar courts.| +1537|140|6|4|3|3120.42|0.08|0.07|R|F|1992-03-20|1992-04-14|1992-03-21|TAKE BACK RETURN|SHIP|s, final ideas detect sl| +1538|102|5|1|32|32067.20|0.05|0.05|N|O|1995-07-08|1995-07-29|1995-08-01|TAKE BACK RETURN|RAIL|uses maintain blithely. fluffily| +1538|192|3|2|27|29489.13|0.05|0.01|N|O|1995-09-19|1995-08-03|1995-09-24|DELIVER IN PERSON|TRUCK|ngly even packag| +1538|130|3|3|36|37084.68|0.08|0.04|N|O|1995-07-11|1995-09-10|1995-07-26|DELIVER IN PERSON|MAIL|al deposits mo| +1538|104|1|4|28|28114.80|0.10|0.04|N|O|1995-09-19|1995-08-27|1995-10-10|COLLECT COD|RAIL|bout the fluffily unusual| +1538|178|7|5|13|14016.21|0.01|0.05|N|O|1995-06-26|1995-07-30|1995-07-25|NONE|SHIP|ly. packages sleep f| +1538|128|3|6|42|43181.04|0.08|0.08|N|O|1995-10-10|1995-09-12|1995-11-08|DELIVER IN PERSON|TRUCK|equests cajole blithely | +1539|196|9|1|21|23019.99|0.08|0.02|R|F|1995-04-19|1995-05-10|1995-04-27|COLLECT COD|TRUCK|ounts haggle. busy| +1539|86|7|2|11|10846.88|0.01|0.08|A|F|1995-05-27|1995-04-13|1995-06-10|TAKE BACK RETURN|TRUCK|ly express requests. furiously | +1539|68|5|3|7|6776.42|0.09|0.04|R|F|1995-05-14|1995-04-16|1995-05-30|DELIVER IN PERSON|AIR|. fluffily reg| +1540|173|1|1|38|40780.46|0.03|0.01|R|F|1992-09-30|1992-10-27|1992-10-12|TAKE BACK RETURN|SHIP| final grouches bo| +1540|60|2|2|35|33602.10|0.02|0.07|R|F|1992-10-31|1992-09-04|1992-11-05|TAKE BACK RETURN|SHIP|e blithely a| +1540|8|3|3|25|22700.00|0.08|0.04|R|F|1992-11-15|1992-10-24|1992-12-14|DELIVER IN PERSON|SHIP|ironic deposits amo| +1540|25|8|4|6|5550.12|0.09|0.03|R|F|1992-08-28|1992-09-17|1992-09-14|COLLECT COD|MAIL|ing to the slyly express asymptote| +1540|87|8|5|27|26651.16|0.10|0.08|R|F|1992-12-02|1992-10-18|1992-12-31|NONE|SHIP|carefully final packages; b| +1541|64|3|1|44|42418.64|0.10|0.05|N|O|1995-08-24|1995-07-13|1995-08-26|TAKE BACK RETURN|MAIL|o beans boost fluffily abou| +1541|26|7|2|8|7408.16|0.10|0.08|N|F|1995-06-05|1995-08-07|1995-06-21|TAKE BACK RETURN|TRUCK|y pending packages. blithely fi| +1542|58|9|1|37|35447.85|0.07|0.06|A|F|1993-12-15|1993-10-17|1994-01-07|TAKE BACK RETURN|REG AIR|e blithely unusual accounts. quic| +1542|3|6|2|12|10836.00|0.09|0.06|R|F|1993-10-29|1993-11-02|1993-11-09|TAKE BACK RETURN|RAIL|carefully | +1542|6|7|3|18|16308.00|0.05|0.05|R|F|1993-10-17|1993-11-15|1993-10-26|TAKE BACK RETURN|FOB|pending instr| +1542|143|10|4|21|21905.94|0.01|0.05|R|F|1993-10-13|1993-12-13|1993-11-12|NONE|RAIL|y pending foxes nag blithely | +1542|155|7|5|46|48536.90|0.00|0.00|R|F|1993-09-28|1993-11-03|1993-10-15|COLLECT COD|FOB|ial instructions. ironically| +1543|71|10|1|34|33016.38|0.02|0.08|N|O|1997-05-25|1997-03-30|1997-06-04|NONE|AIR|ic requests are ac| +1543|115|9|2|6|6090.66|0.09|0.01|N|O|1997-04-16|1997-05-20|1997-05-16|DELIVER IN PERSON|MAIL| among the carefully bold or| +1543|67|8|3|42|40616.52|0.06|0.01|N|O|1997-05-26|1997-03-30|1997-06-12|DELIVER IN PERSON|FOB|its sleep until the fur| +1543|189|10|4|42|45745.56|0.05|0.06|N|O|1997-04-11|1997-04-11|1997-04-23|TAKE BACK RETURN|MAIL|xpress instructions. regular acc| +1543|40|1|5|9|8460.36|0.08|0.06|N|O|1997-03-14|1997-05-19|1997-03-26|DELIVER IN PERSON|FOB|ravely special requests | +1543|49|8|6|3|2847.12|0.10|0.04|N|O|1997-03-29|1997-05-10|1997-04-22|COLLECT COD|MAIL|sleep along the furiou| +1543|68|7|7|3|2904.18|0.00|0.02|N|O|1997-03-22|1997-04-06|1997-03-30|NONE|AIR|quickly. final accounts haggle slyl| +1568|90|1|1|36|35643.24|0.02|0.03|N|O|1997-05-31|1997-04-22|1997-06-21|TAKE BACK RETURN|RAIL|platelets-- furiously sly excu| +1568|9|2|2|46|41814.00|0.04|0.00|N|O|1997-04-06|1997-04-08|1997-04-23|TAKE BACK RETURN|MAIL|g the blithely even acco| +1569|75|3|1|5|4875.35|0.07|0.00|N|O|1998-04-16|1998-06-21|1998-04-18|COLLECT COD|REG AIR| packages. ironic, even excuses a| +1569|39|10|2|16|15024.48|0.01|0.08|N|O|1998-04-26|1998-06-16|1998-05-26|COLLECT COD|MAIL|deposits. blithely final asymptotes ac| +1569|49|10|3|43|40808.72|0.10|0.03|N|O|1998-06-05|1998-05-31|1998-06-28|DELIVER IN PERSON|FOB| instructions.| +1569|70|1|4|30|29102.10|0.02|0.03|N|O|1998-07-19|1998-06-04|1998-08-10|NONE|SHIP|packages. excuses lose evenly carefully reg| +1570|183|4|1|25|27079.50|0.00|0.06|N|O|1998-05-03|1998-06-02|1998-06-02|DELIVER IN PERSON|REG AIR|its. slyly regular sentiments| +1570|86|7|2|7|6902.56|0.05|0.05|N|O|1998-07-10|1998-06-01|1998-07-23|TAKE BACK RETURN|MAIL|requests boost quickly re| +1571|52|3|1|47|44746.35|0.00|0.05|R|F|1992-12-07|1993-02-24|1993-01-01|TAKE BACK RETURN|REG AIR|ng to the fluffily unusual | +1571|183|4|2|6|6499.08|0.03|0.00|A|F|1993-01-08|1993-02-13|1993-02-07|COLLECT COD|SHIP| special, ironic depo| +1571|59|7|3|18|17262.90|0.05|0.08|A|F|1993-01-09|1993-01-12|1993-01-31|COLLECT COD|AIR| pending grouches | +1571|101|4|4|48|48052.80|0.05|0.05|A|F|1992-12-28|1993-01-04|1993-01-04|DELIVER IN PERSON|RAIL|slyly pending p| +1571|42|5|5|10|9420.40|0.03|0.06|R|F|1992-12-12|1993-02-13|1992-12-29|DELIVER IN PERSON|AIR|lets. carefully regular ideas wake| +1571|34|10|6|24|22416.72|0.05|0.07|A|F|1993-03-22|1993-01-31|1993-04-09|NONE|TRUCK|warthogs wake carefully acro| +1572|24|5|1|41|37884.82|0.02|0.00|N|O|1996-05-16|1996-04-09|1996-05-28|TAKE BACK RETURN|REG AIR|. pinto beans alongside| +1572|93|7|2|10|9930.90|0.04|0.06|N|O|1996-05-17|1996-03-26|1996-05-19|NONE|AIR| accounts affix slyly. | +1573|186|7|1|5|5430.90|0.05|0.01|A|F|1993-04-24|1993-03-13|1993-05-17|TAKE BACK RETURN|MAIL|ymptotes could u| +1573|31|2|2|17|15827.51|0.00|0.06|R|F|1993-02-24|1993-02-16|1993-03-08|TAKE BACK RETURN|TRUCK|carefully regular deposits. | +1573|83|4|3|16|15729.28|0.04|0.03|A|F|1993-03-15|1993-03-16|1993-03-31|COLLECT COD|AIR|ely. furiously final requests wake slyl| +1573|194|7|4|11|12036.09|0.09|0.01|R|F|1993-03-23|1993-03-24|1993-04-12|TAKE BACK RETURN|RAIL|nently pending| +1573|137|8|5|7|7259.91|0.00|0.01|R|F|1993-01-30|1993-03-14|1993-02-27|DELIVER IN PERSON|SHIP|eodolites sleep slyly. slyly f| +1573|154|6|6|30|31624.50|0.03|0.01|A|F|1992-12-29|1993-03-06|1993-01-02|DELIVER IN PERSON|TRUCK|. blithely even theodolites boos| +1574|48|7|1|41|38869.64|0.06|0.02|N|O|1997-03-08|1997-02-09|1997-04-01|COLLECT COD|AIR|s. slyly regular depen| +1574|191|5|2|50|54559.50|0.00|0.05|N|O|1996-12-14|1997-02-14|1996-12-16|TAKE BACK RETURN|FOB|le regular, regular foxes. blithely e| +1574|55|3|3|25|23876.25|0.06|0.02|N|O|1997-01-16|1997-02-14|1997-02-12|DELIVER IN PERSON|TRUCK|ly silent accounts.| +1574|191|4|4|6|6547.14|0.03|0.05|N|O|1997-02-24|1997-02-03|1997-03-01|NONE|AIR|e silent, final packages. speci| +1574|109|4|5|6|6054.60|0.05|0.05|N|O|1997-02-09|1997-03-02|1997-02-14|COLLECT COD|MAIL|nic, final ideas snooze. | +1574|5|2|6|42|38010.00|0.07|0.01|N|O|1996-12-19|1997-01-13|1996-12-28|NONE|FOB|o beans according t| +1574|136|7|7|14|14505.82|0.04|0.01|N|O|1996-12-30|1997-01-19|1997-01-20|NONE|AIR|ily bold a| +1575|29|10|1|42|39018.84|0.05|0.08|N|O|1995-10-21|1995-11-25|1995-10-24|DELIVER IN PERSON|RAIL|ly pending pinto beans.| +1575|36|7|2|39|36505.17|0.00|0.06|N|O|1995-10-30|1995-10-15|1995-11-10|COLLECT COD|TRUCK| ironic requests snooze ironic, regular acc| +1575|2|5|3|12|10824.00|0.01|0.05|N|O|1995-12-27|1995-11-11|1996-01-23|TAKE BACK RETURN|AIR| bold accounts. furi| +1575|111|1|4|39|39433.29|0.07|0.00|N|O|1995-09-23|1995-11-05|1995-09-25|TAKE BACK RETURN|TRUCK| after the unusual asym| +1575|83|4|5|10|9830.80|0.09|0.00|N|O|1996-01-10|1995-11-20|1996-01-13|DELIVER IN PERSON|RAIL|k excuses. pinto beans wake a| +1575|178|6|6|14|15094.38|0.08|0.06|N|O|1995-10-31|1995-12-06|1995-11-30|NONE|AIR|beans breach among the furiously specia| +1575|117|1|7|48|48821.28|0.08|0.04|N|O|1995-11-19|1995-10-25|1995-12-07|DELIVER IN PERSON|TRUCK|cies. regu| +1600|172|10|1|20|21443.40|0.02|0.01|R|F|1993-06-16|1993-04-23|1993-07-02|COLLECT COD|FOB|pths sleep blithely about the| +1600|44|3|2|48|45313.92|0.07|0.02|R|F|1993-04-17|1993-04-14|1993-05-03|DELIVER IN PERSON|FOB|furiously silent foxes could wake. car| +1600|39|10|3|8|7512.24|0.04|0.07|R|F|1993-03-07|1993-04-22|1993-03-26|TAKE BACK RETURN|FOB|cajole furiously fluf| +1600|69|8|4|25|24226.50|0.00|0.06|A|F|1993-05-25|1993-04-07|1993-06-05|TAKE BACK RETURN|REG AIR|press packages. ironic excuses bo| +1600|147|8|5|30|31414.20|0.03|0.08|R|F|1993-06-03|1993-05-03|1993-06-07|DELIVER IN PERSON|RAIL|al escapades alongside of the depo| +1601|167|8|1|6|6402.96|0.00|0.00|A|F|1994-10-19|1994-09-28|1994-10-23|COLLECT COD|SHIP| bold sheaves. furiously per| +1601|175|3|2|50|53758.50|0.03|0.02|R|F|1994-12-24|1994-10-23|1995-01-11|COLLECT COD|FOB|ideas doubt| +1601|90|1|3|14|13861.26|0.04|0.08|R|F|1994-09-17|1994-11-22|1994-10-03|DELIVER IN PERSON|RAIL|he special, fin| +1602|183|4|1|4|4332.72|0.08|0.06|R|F|1993-10-31|1993-09-05|1993-11-21|NONE|RAIL|y. even excuses| +1603|39|5|1|1|939.03|0.08|0.00|R|F|1993-08-17|1993-09-04|1993-08-22|TAKE BACK RETURN|REG AIR|d accounts. special warthogs use fur| +1603|66|5|2|29|28015.74|0.06|0.08|A|F|1993-09-28|1993-09-20|1993-10-28|NONE|SHIP|ses wake furiously. theodolite| +1604|42|3|1|15|14130.60|0.09|0.08|R|F|1993-09-22|1993-09-03|1993-09-29|TAKE BACK RETURN|MAIL| instructions haggle| +1604|141|4|2|37|38522.18|0.06|0.06|A|F|1993-08-22|1993-09-21|1993-09-10|COLLECT COD|SHIP|requests. blithely ironic somas s| +1604|114|8|3|19|19268.09|0.09|0.07|A|F|1993-10-15|1993-10-04|1993-11-09|COLLECT COD|RAIL| ideas. bol| +1604|175|4|4|15|16127.55|0.03|0.00|R|F|1993-09-10|1993-08-31|1993-09-30|TAKE BACK RETURN|RAIL|ending realms along the special, p| +1604|21|4|5|23|21183.46|0.08|0.05|A|F|1993-10-11|1993-08-30|1993-10-18|DELIVER IN PERSON|RAIL|en requests. blithely fin| +1605|142|1|1|47|48980.58|0.00|0.01|N|O|1998-04-29|1998-06-12|1998-05-20|DELIVER IN PERSON|AIR|. carefully r| +1605|180|8|2|18|19443.24|0.10|0.00|N|O|1998-05-13|1998-06-17|1998-06-03|COLLECT COD|REG AIR|ly regular foxes wake carefully. bol| +1605|59|10|3|39|37402.95|0.02|0.03|N|O|1998-07-12|1998-06-05|1998-08-09|DELIVER IN PERSON|MAIL|nal dependencies-- quickly final frets acc| +1605|183|4|4|25|27079.50|0.06|0.02|N|O|1998-05-26|1998-06-14|1998-06-05|COLLECT COD|AIR|ole carefully car| +1606|115|6|1|21|21317.31|0.04|0.00|N|O|1997-06-02|1997-07-02|1997-06-27|DELIVER IN PERSON|RAIL| pending theodolites prom| +1606|174|3|2|35|37595.95|0.00|0.02|N|O|1997-06-20|1997-06-19|1997-06-22|COLLECT COD|TRUCK|carefully sil| +1606|100|4|3|23|23002.30|0.00|0.06|N|O|1997-04-19|1997-06-26|1997-04-30|NONE|MAIL|ously final requests. slowly ironic ex| +1606|97|9|4|20|19941.80|0.02|0.04|N|O|1997-05-01|1997-05-26|1997-05-28|TAKE BACK RETURN|TRUCK|fily carefu| +1606|71|10|5|14|13594.98|0.10|0.01|N|O|1997-05-19|1997-07-05|1997-06-10|COLLECT COD|FOB|structions haggle f| +1607|190|1|1|2|2180.38|0.02|0.00|N|O|1996-01-11|1996-02-15|1996-01-19|DELIVER IN PERSON|MAIL|packages haggle. regular requests boost s| +1607|119|3|2|37|37707.07|0.05|0.02|N|O|1996-02-27|1996-02-18|1996-03-16|NONE|AIR|alongside | +1607|123|4|3|39|39901.68|0.00|0.00|N|O|1996-02-01|1996-02-12|1996-02-16|NONE|FOB|uches cajole. accounts ar| +1607|76|6|4|34|33186.38|0.05|0.06|N|O|1996-01-06|1996-02-24|1996-01-10|DELIVER IN PERSON|SHIP| quickly above the | +1607|178|8|5|48|51752.16|0.00|0.05|N|O|1996-02-22|1996-02-13|1996-03-09|TAKE BACK RETURN|MAIL|ular forges. deposits a| +1632|191|5|1|47|51285.93|0.08|0.00|N|O|1997-01-25|1997-02-09|1997-02-19|TAKE BACK RETURN|RAIL|g to the closely special no| +1632|148|7|2|14|14673.96|0.08|0.05|N|O|1997-01-15|1997-02-25|1997-01-28|NONE|RAIL|oxes. deposits nag slyly along the slyly | +1632|177|6|3|47|50626.99|0.03|0.04|N|O|1997-01-29|1997-03-03|1997-02-21|NONE|MAIL|sts. blithely regular | +1632|57|9|4|33|31582.65|0.09|0.02|N|O|1997-04-01|1997-02-24|1997-04-29|TAKE BACK RETURN|REG AIR|ructions! slyly| +1632|142|1|5|43|44812.02|0.10|0.03|N|O|1997-02-24|1997-02-19|1997-03-25|DELIVER IN PERSON|FOB|ts. blithe, bold ideas cajo| +1633|178|7|1|35|37735.95|0.01|0.02|N|O|1996-01-09|1995-12-02|1996-01-21|COLLECT COD|REG AIR|ly against the dolph| +1633|5|6|2|15|13575.00|0.00|0.05|N|O|1995-12-13|1995-11-13|1996-01-04|TAKE BACK RETURN|FOB|ges wake fluffil| +1634|48|9|1|21|19908.84|0.00|0.00|N|O|1996-10-04|1996-10-22|1996-11-01|NONE|MAIL|counts alo| +1634|172|3|2|44|47175.48|0.05|0.01|N|O|1996-09-17|1996-11-09|1996-10-03|COLLECT COD|SHIP|requests affix slyly. quickly even pack| +1634|19|10|3|21|19299.21|0.06|0.07|N|O|1996-11-16|1996-10-21|1996-11-27|NONE|TRUCK|y along the excuses.| +1634|68|3|4|17|16457.02|0.08|0.07|N|O|1996-10-29|1996-10-15|1996-11-02|TAKE BACK RETURN|SHIP|cial, bold platelets alongside of the f| +1634|76|7|5|2|1952.14|0.07|0.04|N|O|1996-11-22|1996-10-28|1996-12-17|NONE|SHIP|ly. carefully regular asymptotes wake| +1634|170|9|6|11|11771.87|0.01|0.08|N|O|1996-10-04|1996-12-06|1996-10-14|DELIVER IN PERSON|SHIP|final requests | +1634|13|7|7|35|31955.35|0.06|0.02|N|O|1996-11-25|1996-11-25|1996-12-12|TAKE BACK RETURN|RAIL|cies. regular, special de| +1635|71|1|1|3|2913.21|0.06|0.08|N|O|1997-03-13|1997-03-25|1997-03-27|COLLECT COD|FOB| quickly ironic r| +1635|90|1|2|8|7920.72|0.04|0.05|N|O|1997-04-30|1997-04-21|1997-05-09|DELIVER IN PERSON|AIR|ravely carefully express | +1635|114|5|3|20|20282.20|0.07|0.01|N|O|1997-05-19|1997-04-01|1997-06-17|TAKE BACK RETURN|FOB|oost according to the carefully even accou| +1635|77|5|4|40|39082.80|0.01|0.04|N|O|1997-02-25|1997-03-20|1997-03-12|TAKE BACK RETURN|RAIL|uriously up the ironic deposits. slyly i| +1636|85|6|1|2|1970.16|0.09|0.03|N|O|1997-09-26|1997-08-22|1997-10-05|NONE|TRUCK|nal foxes cajole above the blithely reg| +1636|169|10|2|45|48112.20|0.03|0.01|N|O|1997-07-14|1997-08-08|1997-07-27|COLLECT COD|RAIL|ely express reque| +1636|108|1|3|24|24194.40|0.07|0.08|N|O|1997-10-07|1997-08-12|1997-11-04|TAKE BACK RETURN|MAIL|e carefully unusual ideas are f| +1636|153|1|4|43|45285.45|0.06|0.00|N|O|1997-08-23|1997-08-10|1997-09-17|NONE|REG AIR|blithely special r| +1636|19|6|5|22|20218.22|0.05|0.02|N|O|1997-07-22|1997-08-18|1997-08-03|COLLECT COD|AIR|ular, regu| +1636|63|2|6|34|32744.04|0.10|0.01|N|O|1997-08-11|1997-09-09|1997-08-23|NONE|TRUCK|ular depos| +1636|114|1|7|7|7098.77|0.04|0.00|N|O|1997-07-28|1997-09-10|1997-07-31|NONE|MAIL|ronic instructions. final| +1637|86|7|1|49|48317.92|0.02|0.03|N|F|1995-06-08|1995-04-19|1995-07-01|COLLECT COD|REG AIR|. blithely i| +1637|73|2|2|1|973.07|0.10|0.02|A|F|1995-02-14|1995-03-26|1995-03-09|TAKE BACK RETURN|AIR|ly final pinto beans. furiously| +1637|22|1|3|10|9220.20|0.02|0.05|R|F|1995-02-21|1995-03-17|1995-03-11|NONE|AIR|uriously? blithely even sauternes wake. | +1637|93|5|4|42|41709.78|0.06|0.01|A|F|1995-03-18|1995-04-24|1995-03-31|COLLECT COD|SHIP|blithely a| +1637|5|8|5|25|22625.00|0.05|0.00|R|F|1995-06-07|1995-03-26|1995-06-08|COLLECT COD|RAIL| haggle carefully silent accou| +1637|109|4|6|38|38345.80|0.02|0.08|R|F|1995-03-20|1995-05-05|1995-04-14|DELIVER IN PERSON|SHIP|even, pending foxes nod regular| +1637|52|10|7|21|19993.05|0.07|0.08|A|F|1995-04-30|1995-04-30|1995-05-05|COLLECT COD|SHIP|ly ironic theodolites use b| +1638|6|7|1|46|41676.00|0.03|0.02|N|O|1997-10-16|1997-10-28|1997-11-09|COLLECT COD|MAIL|otes haggle before the slyly bold instructi| +1638|149|10|2|30|31474.20|0.00|0.04|N|O|1997-12-05|1997-09-17|1997-12-06|NONE|REG AIR|s cajole boldly bold requests. closely | +1638|31|7|3|5|4655.15|0.08|0.07|N|O|1997-10-15|1997-11-01|1997-11-08|DELIVER IN PERSON|FOB|xcuses sleep furiou| +1638|56|8|4|19|18164.95|0.00|0.08|N|O|1997-10-15|1997-10-27|1997-11-03|DELIVER IN PERSON|MAIL| quickly expres| +1638|143|6|5|25|26078.50|0.05|0.03|N|O|1997-10-06|1997-09-30|1997-11-02|DELIVER IN PERSON|REG AIR|gle final, ironic pinto beans. | +1638|155|10|6|46|48536.90|0.07|0.08|N|O|1997-08-20|1997-10-10|1997-09-09|COLLECT COD|AIR|ckages are carefully even instru| +1639|187|8|1|24|26092.32|0.07|0.00|N|O|1995-08-24|1995-10-06|1995-08-31|COLLECT COD|REG AIR| the regular packages. courts dou| +1639|43|6|2|38|35835.52|0.01|0.04|N|O|1995-08-23|1995-11-09|1995-08-29|TAKE BACK RETURN|FOB|y regular packages. b| +1639|171|10|3|41|43917.97|0.04|0.02|N|O|1995-12-19|1995-11-11|1996-01-12|DELIVER IN PERSON|FOB|structions w| +1664|118|5|1|48|48869.28|0.04|0.02|N|O|1996-06-21|1996-05-01|1996-07-19|TAKE BACK RETURN|RAIL| use. ironic deposits integrate. slyly unu| +1664|173|2|2|30|32195.10|0.06|0.05|N|O|1996-04-04|1996-05-04|1996-05-03|COLLECT COD|FOB|ess multip| +1664|151|2|3|10|10511.50|0.00|0.06|N|O|1996-04-10|1996-05-13|1996-05-07|TAKE BACK RETURN|RAIL|instructions up the acc| +1664|155|3|4|35|36930.25|0.00|0.04|N|O|1996-03-06|1996-05-16|1996-03-09|DELIVER IN PERSON|REG AIR|y regular ide| +1664|57|8|5|9|8613.45|0.07|0.04|N|O|1996-04-15|1996-05-14|1996-05-11|DELIVER IN PERSON|TRUCK|ges. fluffil| +1664|141|8|6|40|41645.60|0.09|0.07|N|O|1996-04-02|1996-04-22|1996-04-17|COLLECT COD|REG AIR|se blithely unusual pains. carefully| +1665|47|6|1|4|3788.16|0.02|0.03|A|F|1994-09-01|1994-06-07|1994-09-12|DELIVER IN PERSON|TRUCK|ely final requests. requests| +1665|78|6|2|1|978.07|0.03|0.05|R|F|1994-05-22|1994-07-06|1994-05-24|TAKE BACK RETURN|TRUCK|sly final p| +1666|185|6|1|30|32555.40|0.04|0.03|N|O|1995-10-28|1995-11-30|1995-11-18|TAKE BACK RETURN|AIR| breach evenly final accounts. r| +1666|64|1|2|20|19281.20|0.01|0.00|N|O|1996-01-27|1995-12-12|1996-01-31|NONE|REG AIR|uietly regular foxes wake quick| +1666|134|10|3|31|32058.03|0.05|0.07|N|O|1996-02-11|1996-01-11|1996-02-28|COLLECT COD|RAIL|ding to the express, bold accounts. fu| +1666|169|8|4|41|43835.56|0.06|0.08|N|O|1995-11-29|1996-01-04|1995-12-24|NONE|TRUCK|ly regular excuses; regular ac| +1667|21|4|1|6|5526.12|0.04|0.02|N|O|1997-12-07|1997-11-16|1998-01-02|COLLECT COD|FOB|riously busy requests. blithely final a| +1667|22|1|2|29|26738.58|0.06|0.07|N|O|1997-10-15|1997-11-09|1997-11-11|TAKE BACK RETURN|MAIL|l accounts. furiously final courts h| +1667|95|8|3|48|47764.32|0.05|0.01|N|O|1998-01-27|1998-01-06|1998-02-09|TAKE BACK RETURN|SHIP|tes sleep furiously. carefully eve| +1667|59|1|4|24|23017.20|0.04|0.01|N|O|1997-10-14|1997-12-01|1997-11-09|TAKE BACK RETURN|MAIL|hrash final requests. care| +1667|195|9|5|2|2190.38|0.07|0.00|N|O|1997-12-17|1997-11-22|1998-01-16|NONE|SHIP|pecial requests hag| +1667|48|7|6|6|5688.24|0.01|0.03|N|O|1998-01-21|1997-12-19|1998-01-28|NONE|TRUCK| nag quickly above th| +1667|40|6|7|19|17860.76|0.09|0.03|N|O|1998-01-23|1997-11-24|1998-01-26|DELIVER IN PERSON|SHIP|around the pinto beans. express, special| +1668|132|8|1|8|8257.04|0.06|0.01|N|O|1997-07-23|1997-10-09|1997-08-06|DELIVER IN PERSON|FOB|arefully regular tithes! slyl| +1668|1|8|2|25|22525.00|0.01|0.06|N|O|1997-08-08|1997-09-28|1997-09-01|NONE|TRUCK|y ironic requests. bold, final ideas a| +1668|75|5|3|42|40952.94|0.08|0.01|N|O|1997-08-09|1997-09-08|1997-08-31|NONE|FOB|ole carefully excuses. final| +1668|191|5|4|9|9820.71|0.05|0.03|N|O|1997-10-17|1997-09-05|1997-11-01|COLLECT COD|RAIL|wake furiously even instructions. sil| +1668|128|9|5|25|25703.00|0.01|0.02|N|O|1997-10-08|1997-09-20|1997-10-11|DELIVER IN PERSON|REG AIR|even platelets across the silent | +1668|10|3|6|38|34580.38|0.07|0.01|N|O|1997-08-26|1997-09-17|1997-09-05|DELIVER IN PERSON|TRUCK|ep slyly across the furi| +1669|79|10|1|24|23497.68|0.04|0.08|N|O|1997-09-04|1997-07-30|1997-09-20|DELIVER IN PERSON|RAIL| regular, final deposits use quick| +1670|32|3|1|41|38213.23|0.07|0.01|N|O|1997-07-19|1997-08-20|1997-07-23|DELIVER IN PERSON|TRUCK|thely according to the sly| +1670|122|3|2|10|10221.20|0.07|0.03|N|O|1997-09-14|1997-08-16|1997-09-23|NONE|SHIP|fily special ideas | +1670|186|7|3|41|44533.38|0.07|0.07|N|O|1997-07-19|1997-08-05|1997-07-26|COLLECT COD|SHIP|al gifts. speci| +1671|149|2|1|21|22031.94|0.02|0.07|N|O|1996-07-28|1996-09-28|1996-08-08|TAKE BACK RETURN|AIR|s accounts slee| +1671|96|10|2|4|3984.36|0.05|0.00|N|O|1996-08-30|1996-09-19|1996-09-23|DELIVER IN PERSON|TRUCK|lyly regular ac| +1671|124|3|3|11|11265.32|0.06|0.08|N|O|1996-09-16|1996-10-21|1996-09-18|NONE|SHIP|tes sleep blithely| +1671|178|7|4|5|5390.85|0.00|0.00|N|O|1996-11-14|1996-10-20|1996-11-25|TAKE BACK RETURN|FOB|luffily regular deposits| +1671|127|8|5|12|12325.44|0.07|0.04|N|O|1996-11-17|1996-09-02|1996-12-17|COLLECT COD|RAIL|special, ironic| +1671|197|9|6|46|50470.74|0.08|0.05|N|O|1996-09-13|1996-10-14|1996-09-28|TAKE BACK RETURN|REG AIR|. slyly bold instructions boost. furiousl| +1696|16|3|1|8|7328.08|0.04|0.02|N|O|1998-04-28|1998-02-07|1998-05-10|NONE|TRUCK|the blithely| +1696|139|5|2|13|13508.69|0.08|0.06|N|O|1998-03-01|1998-03-25|1998-03-24|TAKE BACK RETURN|TRUCK|tructions play slyly q| +1696|2|5|3|19|17138.00|0.08|0.05|N|O|1998-05-03|1998-03-13|1998-05-28|TAKE BACK RETURN|REG AIR|its maintain alongside of the f| +1696|193|4|4|21|22956.99|0.05|0.00|N|O|1998-05-04|1998-02-18|1998-05-07|NONE|MAIL|y players sleep along the final, pending | +1696|94|7|5|43|42745.87|0.03|0.06|N|O|1998-02-14|1998-03-29|1998-02-20|COLLECT COD|FOB|arefully regular dep| +1697|75|5|1|6|5850.42|0.05|0.00|N|O|1997-01-28|1996-11-27|1997-01-31|NONE|FOB|accounts breach slyly even de| +1697|104|7|2|24|24098.40|0.00|0.08|N|O|1996-12-29|1996-12-19|1997-01-10|NONE|SHIP|ts cajole carefully above the carefully| +1697|124|9|3|27|27651.24|0.06|0.00|N|O|1997-01-20|1996-12-02|1997-02-05|COLLECT COD|MAIL|ly regular packages across the silent, b| +1697|94|5|4|49|48710.41|0.08|0.04|N|O|1996-12-07|1997-01-02|1996-12-31|COLLECT COD|TRUCK|lar foxes. fluffily furious ideas doubt qu| +1697|35|1|5|19|17765.57|0.03|0.07|N|O|1997-01-08|1996-11-12|1997-01-11|DELIVER IN PERSON|FOB|ons? special, special accounts after| +1698|97|8|1|44|43871.96|0.05|0.05|N|O|1997-05-16|1997-07-05|1997-05-27|NONE|RAIL|ts wake slyly after t| +1698|93|5|2|6|5958.54|0.08|0.00|N|O|1997-08-21|1997-06-08|1997-09-03|DELIVER IN PERSON|RAIL| pending packages affix ne| +1698|21|6|3|22|20262.44|0.03|0.04|N|O|1997-08-07|1997-05-28|1997-08-24|DELIVER IN PERSON|TRUCK|oward the furiously iro| +1698|112|6|4|19|19230.09|0.00|0.07|N|O|1997-07-04|1997-06-21|1997-08-01|NONE|RAIL| fluffily e| +1698|53|4|5|37|35262.85|0.00|0.03|N|O|1997-05-16|1997-05-29|1997-05-27|NONE|AIR|ly regular ideas. deposit| +1698|166|7|6|15|15992.40|0.10|0.01|N|O|1997-07-20|1997-06-07|1997-07-21|TAKE BACK RETURN|RAIL|final ideas. even, ironic | +1699|38|9|1|50|46901.50|0.00|0.06|A|F|1994-03-26|1994-03-23|1994-04-20|NONE|FOB|to the final requests are carefully silent | +1699|135|6|2|17|17597.21|0.07|0.02|R|F|1994-01-12|1994-03-12|1994-02-08|NONE|AIR|haggle blithely slyly| +1700|140|1|1|38|39525.32|0.04|0.04|N|O|1996-10-03|1996-07-27|1996-10-22|NONE|RAIL|ular dependencies engage slyly | +1700|156|7|2|49|51751.35|0.04|0.00|N|O|1996-09-26|1996-07-28|1996-10-16|NONE|TRUCK|kly even dependencies haggle fluffi| +1701|150|9|1|47|49357.05|0.08|0.05|R|F|1992-05-25|1992-06-29|1992-06-15|NONE|RAIL|slyly final requests cajole requests. f| +1701|54|5|2|2|1908.10|0.01|0.04|R|F|1992-06-24|1992-07-12|1992-06-29|COLLECT COD|SHIP|ween the pending, final accounts. | +1701|35|1|3|26|24310.78|0.10|0.06|R|F|1992-06-04|1992-07-11|1992-07-04|DELIVER IN PERSON|FOB| accounts. blithely pending pinto be| +1702|67|2|1|19|18374.14|0.02|0.01|N|F|1995-06-02|1995-06-30|1995-06-29|NONE|REG AIR|ies haggle blith| +1702|30|5|2|38|35341.14|0.00|0.00|N|O|1995-09-01|1995-06-10|1995-09-10|DELIVER IN PERSON|REG AIR|as believe blithely. bo| +1702|195|6|3|46|50378.74|0.00|0.08|N|O|1995-07-14|1995-06-30|1995-07-20|NONE|FOB|y even foxes. carefully final dependencies | +1702|93|4|4|28|27806.52|0.07|0.05|R|F|1995-06-10|1995-07-26|1995-06-16|TAKE BACK RETURN|AIR|nts haggle along the packa| +1702|89|10|5|34|33628.72|0.01|0.06|N|O|1995-07-04|1995-06-08|1995-07-28|DELIVER IN PERSON|AIR|y careful packages; dogged acco| +1702|42|9|6|28|26377.12|0.10|0.00|N|O|1995-08-14|1995-07-31|1995-09-08|COLLECT COD|RAIL|ackages sleep. furiously even excuses snooz| +1703|166|5|1|36|38381.76|0.09|0.01|R|F|1993-04-22|1993-03-05|1993-04-24|DELIVER IN PERSON|SHIP|riously express | +1703|137|8|2|35|36299.55|0.01|0.08|R|F|1993-04-14|1993-03-31|1993-04-27|NONE|RAIL|he carefully| +1703|124|5|3|48|49157.76|0.06|0.02|R|F|1993-02-07|1993-04-20|1993-02-24|TAKE BACK RETURN|AIR|ggle slyly furiously regular theodol| +1728|126|5|1|1|1026.12|0.07|0.04|N|O|1996-09-16|1996-08-19|1996-09-18|COLLECT COD|FOB|lly. carefully ex| +1728|105|8|2|23|23117.30|0.05|0.02|N|O|1996-09-08|1996-07-24|1996-09-20|NONE|FOB|ns. pending, final ac| +1728|165|10|3|44|46867.04|0.08|0.07|N|O|1996-07-31|1996-06-22|1996-08-06|COLLECT COD|FOB|ide of the slyly blithe| +1728|27|8|4|34|31518.68|0.08|0.05|N|O|1996-08-28|1996-07-20|1996-09-12|DELIVER IN PERSON|MAIL|special req| +1728|199|2|5|31|34074.89|0.09|0.02|N|O|1996-07-26|1996-06-28|1996-08-14|NONE|REG AIR|kly sly theodolites.| +1729|157|8|1|12|12685.80|0.08|0.04|A|F|1992-08-11|1992-07-24|1992-08-16|COLLECT COD|RAIL|y pending packages detect. carefully re| +1730|166|5|1|41|43712.56|0.01|0.03|N|O|1998-08-11|1998-08-29|1998-09-02|TAKE BACK RETURN|TRUCK| instructions. unusual, even Tiresi| +1730|162|3|2|15|15932.40|0.07|0.04|N|O|1998-09-07|1998-09-12|1998-09-30|TAKE BACK RETURN|AIR|pinto beans cajole. bravely bold| +1730|162|1|3|9|9559.44|0.10|0.00|N|O|1998-09-18|1998-09-15|1998-09-21|DELIVER IN PERSON|FOB|gular dependencies wake. blithely final e| +1730|10|7|4|40|36400.40|0.02|0.03|N|O|1998-10-02|1998-10-06|1998-10-03|NONE|SHIP|ven dinos slee| +1730|141|4|5|43|44769.02|0.04|0.06|N|O|1998-10-26|1998-10-22|1998-11-02|DELIVER IN PERSON|TRUCK|ng deposits cajo| +1731|184|5|1|36|39030.48|0.10|0.00|N|O|1996-04-18|1996-04-03|1996-04-29|TAKE BACK RETURN|MAIL|ngside of the even instruct| +1731|139|10|2|7|7273.91|0.04|0.07|N|O|1996-04-11|1996-02-13|1996-04-30|DELIVER IN PERSON|REG AIR|fily quick asymptotes| +1731|51|9|3|50|47552.50|0.05|0.04|N|O|1996-01-14|1996-03-13|1996-01-29|COLLECT COD|RAIL|ly slyly speci| +1731|196|10|4|23|25212.37|0.10|0.04|N|O|1996-04-22|1996-02-25|1996-05-16|TAKE BACK RETURN|RAIL|rays? bold, express pac| +1731|53|4|5|37|35262.85|0.10|0.05|N|O|1996-04-30|1996-03-17|1996-05-27|TAKE BACK RETURN|RAIL| beans use furiously slyly b| +1731|124|7|6|41|41988.92|0.03|0.08|N|O|1996-04-05|1996-02-28|1996-05-01|TAKE BACK RETURN|RAIL|haggle across the blithely ironi| +1732|5|6|1|50|45250.00|0.02|0.01|R|F|1993-12-05|1994-01-23|1993-12-20|TAKE BACK RETURN|FOB|fily final asymptotes according | +1732|99|10|2|36|35967.24|0.01|0.03|A|F|1994-03-15|1994-02-09|1994-04-02|DELIVER IN PERSON|TRUCK|ve the accounts. slowly ironic multip| +1732|161|8|3|41|43507.56|0.00|0.04|R|F|1994-02-20|1994-01-07|1994-02-27|TAKE BACK RETURN|AIR|quests sublate against the silent | +1732|152|3|4|9|9469.35|0.04|0.04|A|F|1994-02-25|1994-01-29|1994-03-16|TAKE BACK RETURN|FOB|ular platelets. deposits wak| +1732|169|8|5|25|26729.00|0.02|0.05|A|F|1994-02-15|1994-01-07|1994-02-21|COLLECT COD|REG AIR|nag slyly. even, special de| +1732|73|1|6|16|15569.12|0.01|0.05|R|F|1994-01-07|1994-01-02|1994-01-25|COLLECT COD|SHIP|ix carefully at the furiously regular pac| +1733|111|5|1|41|41455.51|0.08|0.01|N|O|1996-06-13|1996-07-08|1996-07-07|TAKE BACK RETURN|AIR|ess notornis. fur| +1733|24|7|2|16|14784.32|0.00|0.04|N|O|1996-08-28|1996-07-25|1996-09-27|COLLECT COD|MAIL|slyly express deposits sleep abo| +1733|120|10|3|29|29583.48|0.10|0.06|N|O|1996-07-16|1996-08-08|1996-07-28|NONE|TRUCK|ns detect among the special accounts. qu| +1733|136|7|4|38|39372.94|0.01|0.03|N|O|1996-08-26|1996-07-23|1996-08-28|NONE|FOB| deposits | +1733|34|5|5|22|20548.66|0.06|0.07|N|O|1996-07-16|1996-07-24|1996-07-30|COLLECT COD|AIR|gainst the final deposits. carefully final | +1733|66|7|6|9|8694.54|0.06|0.08|N|O|1996-05-25|1996-07-23|1996-06-10|COLLECT COD|TRUCK|ven foxes was according to t| +1733|146|9|7|13|13599.82|0.02|0.03|N|O|1996-08-03|1996-08-02|1996-08-18|NONE|MAIL|olites sleep furious| +1734|155|3|1|38|40095.70|0.03|0.03|R|F|1994-08-09|1994-09-07|1994-08-12|COLLECT COD|FOB|ts doubt b| +1734|118|2|2|4|4072.44|0.06|0.03|A|F|1994-08-20|1994-07-17|1994-08-25|DELIVER IN PERSON|AIR|final warhorses.| +1735|156|7|1|43|45414.45|0.02|0.06|A|F|1993-01-14|1993-03-25|1993-02-02|DELIVER IN PERSON|FOB|iously after the | +1735|139|5|2|49|50917.37|0.03|0.04|A|F|1992-12-31|1993-02-03|1993-01-25|TAKE BACK RETURN|TRUCK|y express accounts above the exp| +1760|96|9|1|38|37851.42|0.09|0.03|N|O|1996-06-15|1996-06-29|1996-07-11|NONE|MAIL|tions. blithely regular orbits against the | +1760|8|9|2|3|2724.00|0.00|0.06|N|O|1996-07-18|1996-07-01|1996-08-01|NONE|RAIL|lyly bold dolphins haggle carefully. sl| +1760|137|8|3|44|45633.72|0.05|0.01|N|O|1996-06-11|1996-06-16|1996-07-02|COLLECT COD|REG AIR|instructions poach slyly ironic theodolites| +1761|52|4|1|33|31417.65|0.09|0.03|R|F|1994-01-03|1994-01-23|1994-01-31|NONE|FOB|s. excuses a| +1761|52|3|2|37|35225.85|0.02|0.07|R|F|1994-02-17|1994-03-08|1994-03-16|NONE|RAIL| integrate. quickly unusual| +1761|49|6|3|37|35114.48|0.06|0.04|R|F|1994-01-02|1994-03-12|1994-01-25|DELIVER IN PERSON|TRUCK|regular packages wake after| +1761|73|1|4|49|47680.43|0.06|0.07|R|F|1994-01-08|1994-03-03|1994-02-05|TAKE BACK RETURN|FOB|y even packages promise| +1761|157|5|5|37|39114.55|0.03|0.04|R|F|1994-04-24|1994-03-14|1994-04-29|TAKE BACK RETURN|MAIL|express requests print blithely around the| +1761|24|7|6|12|11088.24|0.01|0.05|A|F|1994-04-16|1994-03-08|1994-04-21|DELIVER IN PERSON|AIR| sleep furiously. deposits are acco| +1761|1|6|7|13|11713.00|0.03|0.08|R|F|1994-03-06|1994-03-18|1994-03-22|DELIVER IN PERSON|TRUCK|ons boost fu| +1762|26|5|1|15|13890.30|0.04|0.08|A|F|1994-12-18|1994-10-29|1995-01-17|TAKE BACK RETURN|REG AIR|old packages thrash. care| +1762|50|3|2|39|37051.95|0.10|0.02|A|F|1994-09-12|1994-11-09|1994-10-08|DELIVER IN PERSON|MAIL| ironic platelets sleep along t| +1762|32|8|3|7|6524.21|0.05|0.01|R|F|1994-09-03|1994-10-02|1994-09-10|NONE|REG AIR|uickly express packages wake slyly-- regul| +1762|145|2|4|24|25083.36|0.03|0.03|A|F|1994-11-30|1994-11-02|1994-12-20|NONE|REG AIR|accounts solve alongside of the fluffily | +1762|8|9|5|49|44492.00|0.08|0.05|A|F|1994-10-20|1994-11-02|1994-11-10|TAKE BACK RETURN|SHIP| packages sleep fluffily pen| +1762|94|7|6|35|34793.15|0.05|0.05|A|F|1994-11-25|1994-10-21|1994-11-28|COLLECT COD|AIR|ind quickly. accounts ca| +1762|73|3|7|47|45734.29|0.03|0.01|A|F|1994-11-02|1994-10-07|1994-11-08|NONE|SHIP| blithely brave| +1763|12|9|1|22|20064.22|0.09|0.06|N|O|1997-01-17|1997-01-15|1997-02-03|TAKE BACK RETURN|SHIP|ld. fluffily final ideas boos| +1763|157|5|2|43|45457.45|0.04|0.04|N|O|1996-11-04|1996-12-09|1996-11-28|DELIVER IN PERSON|FOB|r deposits integrate blithely pending, quic| +1763|25|10|3|16|14800.32|0.06|0.02|N|O|1996-12-12|1996-12-04|1996-12-25|DELIVER IN PERSON|RAIL|ously pending asymptotes a| +1763|61|6|4|44|42286.64|0.04|0.05|N|O|1996-12-04|1997-01-06|1996-12-25|DELIVER IN PERSON|REG AIR| instructions need to integrate deposits. | +1763|147|4|5|13|13612.82|0.03|0.05|N|O|1996-11-23|1997-01-24|1996-12-05|TAKE BACK RETURN|SHIP|s sleep carefully. fluffily unusua| +1763|143|4|6|3|3129.42|0.05|0.03|N|O|1996-12-10|1996-12-06|1997-01-04|TAKE BACK RETURN|FOB|ut the slyly pending deposi| +1763|184|5|7|2|2168.36|0.05|0.07|N|O|1997-02-27|1996-12-04|1997-03-27|COLLECT COD|FOB|even pinto beans snooze fluffi| +1764|121|2|1|20|20422.40|0.09|0.02|A|F|1992-06-09|1992-05-22|1992-07-06|COLLECT COD|MAIL|y quickly regular packages. car| +1764|67|4|2|3|2901.18|0.07|0.07|R|F|1992-05-13|1992-06-07|1992-05-26|COLLECT COD|RAIL|es wake slowly. | +1764|78|6|3|27|26407.89|0.07|0.04|A|F|1992-05-06|1992-05-11|1992-05-23|COLLECT COD|TRUCK|ly final foxes wake blithely even requests| +1765|161|2|1|36|38201.76|0.08|0.04|N|O|1996-03-02|1996-02-17|1996-03-14|DELIVER IN PERSON|SHIP|he blithely pending accou| +1766|87|8|1|32|31586.56|0.08|0.01|N|O|1997-01-08|1996-11-11|1997-01-31|TAKE BACK RETURN|AIR|ess accounts. stealthily ironic accou| +1766|34|10|2|12|11208.36|0.05|0.01|N|O|1996-10-28|1996-12-18|1996-11-15|DELIVER IN PERSON|AIR|heodolites above the final, regular acc| +1766|111|1|3|1|1011.11|0.10|0.02|N|O|1997-01-21|1997-01-07|1997-02-19|NONE|TRUCK|ly blithely pending accounts. reg| +1767|25|4|1|32|29600.64|0.08|0.04|A|F|1995-05-22|1995-05-14|1995-05-23|COLLECT COD|SHIP|to the bravely ironic requests i| +1767|42|1|2|1|942.04|0.09|0.05|N|O|1995-06-23|1995-05-25|1995-07-03|TAKE BACK RETURN|RAIL|ing to the slyly fin| +1767|174|5|3|24|25780.08|0.06|0.03|R|F|1995-03-16|1995-04-29|1995-04-11|DELIVER IN PERSON|RAIL|luffy theodolites need to detect furi| +1767|23|8|4|50|46151.00|0.01|0.02|R|F|1995-05-29|1995-04-14|1995-06-15|NONE|REG AIR|y unusual foxe| +1767|52|10|5|40|38082.00|0.06|0.00|R|F|1995-04-16|1995-05-06|1995-04-21|TAKE BACK RETURN|AIR|ep. accounts nag blithely fu| +1792|88|9|1|9|8892.72|0.09|0.04|R|F|1994-02-28|1993-12-11|1994-03-12|TAKE BACK RETURN|AIR|final packages s| +1792|9|6|2|5|4545.00|0.04|0.02|R|F|1994-02-13|1994-01-03|1994-02-28|DELIVER IN PERSON|TRUCK|ely regular accounts are slyly. pending, bo| +1792|9|2|3|8|7272.00|0.01|0.04|A|F|1994-02-21|1994-01-26|1994-02-27|DELIVER IN PERSON|RAIL|nts. fluffily special instructions integr| +1792|191|3|4|45|49103.55|0.00|0.01|A|F|1994-02-27|1993-12-24|1994-03-07|DELIVER IN PERSON|MAIL|ests are. ironic, regular asy| +1792|199|2|5|35|38471.65|0.06|0.05|R|F|1994-01-31|1994-01-20|1994-02-17|NONE|FOB|e against the quic| +1793|48|5|1|29|27493.16|0.01|0.06|R|F|1992-10-24|1992-09-20|1992-11-23|NONE|MAIL|ar excuses. | +1793|126|9|2|4|4104.48|0.07|0.05|A|F|1992-07-28|1992-08-26|1992-08-21|COLLECT COD|RAIL|nic foxes along the even| +1793|131|7|3|6|6186.78|0.01|0.05|R|F|1992-09-21|1992-09-05|1992-10-01|DELIVER IN PERSON|REG AIR|uctions; depo| +1793|118|8|4|4|4072.44|0.00|0.08|R|F|1992-09-27|1992-09-21|1992-10-07|DELIVER IN PERSON|AIR|equests nod ac| +1793|25|6|5|42|38850.84|0.03|0.03|A|F|1992-10-13|1992-10-02|1992-11-06|NONE|RAIL|uctions sleep carefully special, fl| +1794|168|9|1|36|38453.76|0.09|0.08|N|O|1997-11-07|1997-11-01|1997-11-18|TAKE BACK RETURN|FOB|ely fluffily ironi| +1794|95|8|2|3|2985.27|0.02|0.03|N|O|1997-11-15|1997-12-16|1997-11-20|DELIVER IN PERSON|FOB| sentiments according to the q| +1794|117|8|3|23|23393.53|0.08|0.04|N|O|1997-10-13|1997-11-30|1997-10-28|TAKE BACK RETURN|AIR|usly unusual theodolites doze about | +1794|85|6|4|34|33492.72|0.06|0.08|N|O|1997-09-29|1997-11-13|1997-10-07|TAKE BACK RETURN|SHIP|rs above the accoun| +1794|117|4|5|47|47804.17|0.10|0.06|N|O|1998-01-15|1997-11-30|1998-02-14|DELIVER IN PERSON|TRUCK| haggle slyly. furiously express orbit| +1794|91|3|6|37|36670.33|0.01|0.01|N|O|1998-01-12|1997-12-21|1998-01-17|DELIVER IN PERSON|MAIL|ackages. pinto| +1795|137|8|1|44|45633.72|0.08|0.08|A|F|1994-04-28|1994-05-24|1994-05-27|NONE|AIR|ites sleep carefully slyly p| +1795|114|5|2|34|34479.74|0.08|0.00|A|F|1994-04-24|1994-06-01|1994-05-08|DELIVER IN PERSON|SHIP|closely regular instructions wake. | +1795|168|3|3|25|26704.00|0.07|0.01|A|F|1994-05-18|1994-05-22|1994-05-20|TAKE BACK RETURN|RAIL|he always express accounts ca| +1795|125|8|4|32|32803.84|0.03|0.06|R|F|1994-05-10|1994-04-21|1994-05-17|DELIVER IN PERSON|SHIP| asymptotes across the bold,| +1795|163|8|5|11|11694.76|0.08|0.02|R|F|1994-06-19|1994-04-24|1994-07-02|TAKE BACK RETURN|TRUCK|slyly. special pa| +1796|10|1|1|28|25480.28|0.08|0.04|A|F|1992-12-01|1993-01-01|1992-12-24|DELIVER IN PERSON|FOB|y quickly ironic accounts.| +1796|185|6|2|8|8681.44|0.00|0.08|R|F|1993-01-07|1993-01-04|1993-01-10|NONE|SHIP|slyly bold accounts are furiously agains| +1797|31|7|1|17|15827.51|0.01|0.02|N|O|1996-08-06|1996-07-11|1996-08-29|NONE|TRUCK| cajole carefully. unusual Tiresias e| +1797|145|2|2|16|16722.24|0.01|0.00|N|O|1996-06-03|1996-07-21|1996-06-07|NONE|FOB|o beans wake regular accounts. blit| +1797|12|9|3|21|19152.21|0.02|0.01|N|O|1996-08-05|1996-08-05|1996-08-06|DELIVER IN PERSON|AIR|ns. regular, regular deposit| +1798|109|10|1|43|43391.30|0.01|0.08|N|O|1997-08-27|1997-10-23|1997-09-09|DELIVER IN PERSON|MAIL|ld packages sleep furiously. depend| +1799|52|10|1|8|7616.40|0.04|0.08|R|F|1994-06-14|1994-05-27|1994-06-27|TAKE BACK RETURN|MAIL|ealms upon the special, ironic waters| +1799|27|10|2|42|38934.84|0.02|0.02|R|F|1994-04-05|1994-04-28|1994-04-09|DELIVER IN PERSON|FOB|es pending | +1824|120|10|1|45|45905.40|0.03|0.02|R|F|1994-08-21|1994-06-21|1994-09-19|NONE|RAIL|ent Tiresias. quickly express | +1824|69|4|2|40|38762.40|0.10|0.03|A|F|1994-05-08|1994-07-24|1994-06-06|NONE|FOB|es mold furiously final instructions. s| +1825|156|1|1|43|45414.45|0.05|0.05|A|F|1994-02-18|1994-02-19|1994-03-02|TAKE BACK RETURN|RAIL| accounts breach fluffily spe| +1825|148|5|2|39|40877.46|0.00|0.00|R|F|1994-04-01|1994-01-12|1994-04-21|DELIVER IN PERSON|REG AIR|ual, bold ideas haggle above the quickly ir| +1825|17|4|3|7|6419.07|0.04|0.03|A|F|1994-01-02|1994-01-30|1994-01-30|TAKE BACK RETURN|REG AIR|fully ironic requests. requests cajole ex| +1825|121|10|4|23|23485.76|0.05|0.01|R|F|1994-01-08|1994-02-08|1994-01-19|NONE|MAIL| wake express, even r| +1825|178|9|5|33|35579.61|0.04|0.04|A|F|1993-12-07|1994-03-01|1993-12-16|TAKE BACK RETURN|RAIL|about the ne| +1826|27|10|1|4|3708.08|0.06|0.00|R|F|1992-07-05|1992-06-12|1992-08-04|DELIVER IN PERSON|MAIL|alongside of the quickly unusual re| +1826|68|3|2|9|8712.54|0.07|0.07|R|F|1992-07-12|1992-07-11|1992-07-15|DELIVER IN PERSON|TRUCK| blithely special| +1826|176|4|3|14|15066.38|0.05|0.01|A|F|1992-04-28|1992-05-31|1992-05-25|COLLECT COD|TRUCK|uriously bold pinto beans are carefully ag| +1826|180|9|4|6|6481.08|0.05|0.04|R|F|1992-06-30|1992-05-17|1992-07-30|DELIVER IN PERSON|RAIL|kages. blithely silent| +1826|135|1|5|46|47615.98|0.05|0.06|R|F|1992-05-02|1992-06-25|1992-05-26|TAKE BACK RETURN|FOB|ously? quickly pe| +1826|108|3|6|43|43348.30|0.02|0.03|A|F|1992-07-28|1992-06-14|1992-08-03|NONE|MAIL|ss tithes use even ideas. fluffily final t| +1827|90|1|1|47|46534.23|0.00|0.01|N|O|1996-08-01|1996-08-07|1996-08-23|TAKE BACK RETURN|RAIL|. pending courts about the even e| +1827|154|9|2|48|50599.20|0.03|0.05|N|O|1996-08-28|1996-09-15|1996-09-01|COLLECT COD|RAIL|oxes. special, final asymptote| +1827|200|1|3|37|40707.40|0.01|0.07|N|O|1996-07-20|1996-08-18|1996-08-08|DELIVER IN PERSON|REG AIR|ously ironic theodolites serve quickly af| +1827|127|10|4|4|4108.48|0.04|0.04|N|O|1996-07-22|1996-09-10|1996-08-11|DELIVER IN PERSON|RAIL|special requests. blithely| +1827|80|10|5|24|23521.92|0.00|0.08|N|O|1996-08-07|1996-09-01|1996-09-04|DELIVER IN PERSON|SHIP|al gifts! re| +1827|21|2|6|7|6447.14|0.10|0.02|N|O|1996-08-28|1996-08-07|1996-08-31|DELIVER IN PERSON|AIR|egular foxes| +1827|6|7|7|38|34428.00|0.05|0.01|N|O|1996-10-17|1996-08-29|1996-11-07|TAKE BACK RETURN|SHIP| blithely. express, bo| +1828|100|4|1|33|33003.30|0.05|0.04|R|F|1994-06-27|1994-06-10|1994-07-24|COLLECT COD|FOB|s boost carefully. pending d| +1828|13|3|2|40|36520.40|0.08|0.07|R|F|1994-05-05|1994-07-02|1994-05-19|COLLECT COD|REG AIR|s use above the quietly fin| +1828|196|7|3|11|12058.09|0.07|0.08|R|F|1994-07-21|1994-05-28|1994-08-13|DELIVER IN PERSON|FOB| wake blithely | +1828|8|3|4|45|40860.00|0.02|0.05|R|F|1994-05-15|1994-05-29|1994-05-28|COLLECT COD|RAIL| accounts run slyly | +1828|79|7|5|14|13706.98|0.01|0.08|A|F|1994-05-20|1994-06-02|1994-05-25|TAKE BACK RETURN|SHIP|. final packages along the carefully bold| +1829|150|7|1|12|12601.80|0.05|0.06|A|F|1994-08-23|1994-07-13|1994-09-04|DELIVER IN PERSON|FOB|ges wake furiously express pinto| +1829|5|6|2|11|9955.00|0.04|0.05|A|F|1994-05-18|1994-06-13|1994-06-07|COLLECT COD|MAIL|ding orbits| +1829|104|9|3|49|49200.90|0.09|0.08|A|F|1994-08-26|1994-08-01|1994-09-16|NONE|TRUCK|ound the quickly | +1829|153|4|4|14|14744.10|0.03|0.06|A|F|1994-08-15|1994-06-08|1994-08-30|TAKE BACK RETURN|AIR|regular deposits alongside of the flu| +1829|166|5|5|6|6396.96|0.02|0.07|A|F|1994-08-09|1994-08-05|1994-09-05|DELIVER IN PERSON|MAIL|s haggle! slyl| +1829|115|5|6|36|36543.96|0.09|0.04|R|F|1994-06-10|1994-06-23|1994-06-22|NONE|FOB|ackages-- express requests sleep; pen| +1830|120|4|1|38|38764.56|0.00|0.07|R|F|1995-04-20|1995-05-22|1995-04-24|TAKE BACK RETURN|TRUCK|ely even a| +1830|25|10|2|9|8325.18|0.05|0.07|R|F|1995-03-09|1995-05-24|1995-03-14|NONE|SHIP|st furiously among | +1830|82|3|3|36|35354.88|0.07|0.07|R|F|1995-04-21|1995-04-14|1995-05-10|DELIVER IN PERSON|SHIP| slowly unusual orbits. carefull| +1831|136|2|1|9|9325.17|0.02|0.03|A|F|1993-12-17|1994-01-27|1993-12-26|NONE|TRUCK|mptotes. furiously regular dolphins al| +1831|48|9|2|9|8532.36|0.07|0.06|R|F|1994-03-22|1994-01-07|1994-04-06|COLLECT COD|MAIL|ent deposits. regular saute| +1831|115|5|3|17|17256.87|0.02|0.08|R|F|1994-01-18|1994-02-12|1994-01-30|TAKE BACK RETURN|MAIL|s boost ironic foxe| +1831|95|8|4|23|22887.07|0.06|0.02|R|F|1993-12-21|1994-02-08|1994-01-04|NONE|SHIP|ests. express pinto beans abou| +1856|55|10|1|10|9550.50|0.05|0.07|R|F|1992-05-11|1992-05-20|1992-06-02|TAKE BACK RETURN|FOB|he furiously even theodolites. account| +1856|97|10|2|47|46863.23|0.07|0.07|R|F|1992-03-22|1992-06-09|1992-04-17|DELIVER IN PERSON|FOB|ingly blithe theodolites. slyly pending | +1856|117|7|3|20|20342.20|0.04|0.06|R|F|1992-05-04|1992-05-06|1992-05-11|DELIVER IN PERSON|MAIL|ost carefully. slyly bold accounts| +1856|150|1|4|22|23103.30|0.08|0.02|A|F|1992-05-02|1992-05-26|1992-05-20|TAKE BACK RETURN|REG AIR|platelets detect slyly regular packages. ca| +1856|190|1|5|14|15262.66|0.01|0.01|A|F|1992-04-14|1992-05-02|1992-05-11|COLLECT COD|SHIP|ans are even requests. deposits caj| +1856|23|6|6|36|33228.72|0.03|0.05|A|F|1992-06-19|1992-05-12|1992-06-28|TAKE BACK RETURN|TRUCK|ly even foxes kindle blithely even realm| +1856|130|3|7|42|43265.46|0.04|0.00|R|F|1992-05-23|1992-06-06|1992-06-19|COLLECT COD|RAIL|usly final deposits| +1857|174|5|1|15|16112.55|0.10|0.03|R|F|1993-04-05|1993-02-28|1993-04-13|COLLECT COD|RAIL|egular, regular inst| +1857|167|6|2|40|42686.40|0.10|0.00|R|F|1993-02-15|1993-03-08|1993-02-21|NONE|AIR|slyly close d| +1857|119|3|3|8|8152.88|0.01|0.07|R|F|1993-01-27|1993-04-04|1993-02-20|TAKE BACK RETURN|AIR|slyly about the fluffily silent req| +1857|100|3|4|41|41004.10|0.07|0.07|A|F|1993-04-16|1993-02-16|1993-04-18|NONE|REG AIR| the slyly| +1858|14|8|1|33|30162.33|0.01|0.02|N|O|1997-12-28|1998-02-03|1998-01-13|NONE|RAIL|tect along the slyly final| +1859|75|6|1|18|17551.26|0.10|0.00|N|O|1997-08-08|1997-06-30|1997-08-26|TAKE BACK RETURN|SHIP|e carefully a| +1859|188|9|2|36|39174.48|0.02|0.01|N|O|1997-05-05|1997-07-08|1997-05-25|TAKE BACK RETURN|REG AIR|regular requests. carefully unusual theo| +1859|158|10|3|5|5290.75|0.06|0.03|N|O|1997-06-20|1997-05-20|1997-07-19|TAKE BACK RETURN|AIR|across the p| +1859|191|2|4|21|22914.99|0.00|0.03|N|O|1997-08-06|1997-05-29|1997-08-26|TAKE BACK RETURN|REG AIR|lar packages wake quickly exp| +1859|46|3|5|11|10406.44|0.06|0.06|N|O|1997-07-15|1997-06-05|1997-07-29|TAKE BACK RETURN|SHIP|ffily ironic pac| +1859|105|8|6|12|12061.20|0.08|0.03|N|O|1997-05-22|1997-06-08|1997-06-07|COLLECT COD|TRUCK|es. unusual, silent request| +1860|113|4|1|9|9117.99|0.04|0.04|N|O|1996-08-03|1996-05-31|1996-08-04|DELIVER IN PERSON|TRUCK|c realms print carefully car| +1861|68|5|1|7|6776.42|0.08|0.05|A|F|1994-01-14|1994-04-03|1994-01-16|COLLECT COD|RAIL|s foxes. slyly| +1861|27|8|2|31|28737.62|0.10|0.05|R|F|1994-01-29|1994-03-07|1994-02-15|TAKE BACK RETURN|RAIL|arefully unusual| +1861|24|9|3|23|21252.46|0.00|0.08|A|F|1994-04-09|1994-03-04|1994-04-11|DELIVER IN PERSON|MAIL|in packages sleep silent dolphins; sly| +1861|116|6|4|38|38612.18|0.10|0.05|R|F|1994-02-26|1994-02-05|1994-03-01|NONE|RAIL|pending deposits cajole quic| +1861|16|3|5|2|1832.02|0.03|0.08|R|F|1994-04-26|1994-03-15|1994-05-15|TAKE BACK RETURN|MAIL|e final, regular requests. carefully | +1862|30|5|1|41|38131.23|0.10|0.00|N|O|1998-06-05|1998-05-17|1998-07-04|COLLECT COD|FOB| carefully along| +1862|166|7|2|37|39447.92|0.06|0.02|N|O|1998-04-15|1998-05-15|1998-05-14|TAKE BACK RETURN|MAIL|l deposits. carefully even dep| +1862|104|1|3|26|26106.60|0.02|0.01|N|O|1998-03-25|1998-05-17|1998-04-17|TAKE BACK RETURN|TRUCK|g carefully: thinly ironic deposits af| +1863|63|2|1|48|46226.88|0.09|0.04|A|F|1993-10-10|1993-12-09|1993-10-19|NONE|FOB|ans hinder furiou| +1863|157|2|2|48|50743.20|0.04|0.08|A|F|1993-11-08|1993-11-05|1993-12-08|COLLECT COD|AIR|onic theodolites alongside of the pending a| +1888|98|10|1|27|26948.43|0.03|0.06|R|F|1994-02-13|1994-01-16|1994-02-25|NONE|REG AIR|. carefully special dolphins sle| +1888|74|5|2|38|37014.66|0.03|0.03|R|F|1993-11-29|1994-01-16|1993-12-08|TAKE BACK RETURN|TRUCK|dazzle carefull| +1888|80|1|3|49|48023.92|0.07|0.05|A|F|1994-02-27|1994-01-14|1994-03-28|DELIVER IN PERSON|FOB|lar accounts haggle carefu| +1888|19|10|4|9|8271.09|0.01|0.04|A|F|1994-02-09|1994-01-22|1994-02-19|NONE|AIR| packages are blithely. carefu| +1888|160|1|5|4|4240.64|0.03|0.06|R|F|1993-12-28|1993-12-19|1994-01-11|COLLECT COD|FOB|lphins. ironically special theodolit| +1888|53|8|6|48|45746.40|0.08|0.08|R|F|1994-02-28|1993-12-16|1994-03-15|COLLECT COD|TRUCK|ar ideas cajole. regular p| +1888|167|6|7|50|53358.00|0.04|0.07|R|F|1993-12-22|1994-01-10|1994-01-06|DELIVER IN PERSON|FOB|ependencies affix blithely regular warhors| +1889|152|4|1|41|43138.15|0.10|0.02|N|O|1997-06-15|1997-05-10|1997-07-08|NONE|AIR|s! furiously pending r| +1889|172|3|2|13|13938.21|0.05|0.00|N|O|1997-06-12|1997-04-28|1997-06-23|NONE|REG AIR|to the regular accounts. carefully express| +1889|138|9|3|36|37372.68|0.05|0.07|N|O|1997-05-19|1997-06-14|1997-05-23|NONE|SHIP|l pinto beans kindle | +1889|168|5|4|5|5340.80|0.02|0.07|N|O|1997-06-26|1997-06-09|1997-07-21|COLLECT COD|AIR|ording to the blithely silent r| +1890|141|8|1|26|27069.64|0.03|0.07|N|O|1997-04-02|1997-03-13|1997-04-22|DELIVER IN PERSON|FOB|ngage. slyly ironic | +1890|100|1|2|43|43004.30|0.07|0.03|N|O|1996-12-30|1997-01-31|1997-01-19|DELIVER IN PERSON|FOB|p ironic, express accounts. fu| +1890|59|1|3|24|23017.20|0.06|0.04|N|O|1997-02-09|1997-02-10|1997-02-12|COLLECT COD|MAIL|is wake carefully above the even id| +1890|68|9|4|43|41626.58|0.09|0.04|N|O|1997-04-08|1997-02-19|1997-04-30|TAKE BACK RETURN|FOB|lyly. instructions across the furiously| +1890|122|3|5|45|45995.40|0.08|0.05|N|O|1997-04-15|1997-03-16|1997-04-19|COLLECT COD|FOB|he carefully regular sauternes. ironic fret| +1890|181|2|6|16|17298.88|0.08|0.02|N|O|1997-02-13|1997-02-18|1997-03-12|TAKE BACK RETURN|TRUCK|ged pinto beans. regular, regular id| +1890|121|4|7|10|10211.20|0.01|0.04|N|O|1996-12-24|1997-02-19|1997-01-01|DELIVER IN PERSON|AIR|. even, unusual inst| +1891|77|8|1|45|43968.15|0.07|0.04|A|F|1994-12-20|1995-01-16|1995-01-05|NONE|RAIL|ests along| +1891|184|5|2|18|19515.24|0.06|0.00|A|F|1995-01-24|1995-01-29|1995-02-14|NONE|RAIL| foxes above the carefu| +1891|198|9|3|15|16472.85|0.03|0.00|R|F|1995-03-11|1995-03-05|1995-03-18|TAKE BACK RETURN|MAIL| accounts are furiou| +1892|113|7|1|48|48629.28|0.02|0.01|A|F|1994-06-16|1994-06-16|1994-06-28|NONE|RAIL|tornis detect regul| +1892|43|2|2|35|33006.40|0.04|0.08|R|F|1994-04-05|1994-05-09|1994-05-03|NONE|MAIL|hes nod furiously around the instruc| +1892|134|5|3|37|38262.81|0.10|0.03|R|F|1994-04-11|1994-06-04|1994-04-24|TAKE BACK RETURN|SHIP|nts. slyly regular asymptot| +1892|197|9|4|14|15360.66|0.06|0.07|R|F|1994-04-08|1994-06-12|1994-04-27|DELIVER IN PERSON|FOB|furiously about the furiously| +1893|99|1|1|43|42960.87|0.10|0.00|N|O|1998-01-25|1998-01-06|1998-02-14|COLLECT COD|SHIP|he carefully regular | +1893|148|9|2|49|51358.86|0.03|0.05|N|O|1998-01-19|1998-01-28|1998-02-02|TAKE BACK RETURN|FOB|y final foxes bo| +1893|45|6|3|3|2835.12|0.03|0.02|N|O|1998-02-10|1998-01-18|1998-02-25|DELIVER IN PERSON|MAIL|gular, even ideas. fluffily bol| +1893|101|6|4|18|18019.80|0.07|0.06|N|O|1998-01-24|1998-01-12|1998-02-13|TAKE BACK RETURN|RAIL|g packages. fluffily final reques| +1893|53|4|5|6|5718.30|0.10|0.02|N|O|1998-01-23|1997-12-22|1998-02-09|DELIVER IN PERSON|TRUCK|ar accounts use. daringly ironic packag| +1894|169|10|1|40|42766.40|0.03|0.07|R|F|1992-06-07|1992-05-11|1992-07-01|DELIVER IN PERSON|FOB|ily furiously bold packages. flu| +1895|161|6|1|43|45629.88|0.09|0.07|R|F|1994-07-26|1994-07-19|1994-08-11|NONE|AIR| carefully eve| +1920|96|7|1|24|23906.16|0.04|0.05|N|O|1998-09-27|1998-08-23|1998-10-15|DELIVER IN PERSON|AIR|thely. bold, pend| +1920|51|6|2|31|29482.55|0.05|0.06|N|O|1998-08-01|1998-08-30|1998-08-17|COLLECT COD|SHIP|lly. ideas wa| +1920|18|2|3|6|5508.06|0.01|0.05|N|O|1998-10-01|1998-08-20|1998-10-24|COLLECT COD|SHIP|l ideas boost slyly pl| +1920|84|5|4|50|49204.00|0.09|0.06|N|O|1998-10-03|1998-08-04|1998-10-29|DELIVER IN PERSON|MAIL|e blithely unusual foxes. brave packages| +1920|34|10|5|14|13076.42|0.08|0.05|N|O|1998-10-22|1998-08-10|1998-10-27|DELIVER IN PERSON|AIR|ickly ironic d| +1921|21|10|1|9|8289.18|0.08|0.00|R|F|1994-02-01|1994-03-20|1994-03-01|DELIVER IN PERSON|FOB|to beans. even excuses integrate specia| +1921|140|6|2|21|21842.94|0.02|0.06|R|F|1994-02-08|1994-03-28|1994-02-15|COLLECT COD|FOB|ckly regula| +1921|71|2|3|27|26218.89|0.00|0.04|A|F|1994-04-26|1994-04-07|1994-04-30|TAKE BACK RETURN|FOB|ing pinto beans above the pend| +1922|10|5|1|13|11830.13|0.05|0.03|N|O|1996-10-24|1996-09-21|1996-11-15|NONE|SHIP|quests. furiously| +1923|37|8|1|9|8433.27|0.01|0.08|N|O|1997-08-29|1997-09-13|1997-09-07|NONE|FOB|lites. ironic instructions integrate bravel| +1923|178|8|2|23|24797.91|0.07|0.05|N|O|1997-09-08|1997-08-11|1997-09-14|TAKE BACK RETURN|MAIL|aggle carefully. furiously permanent| +1923|180|1|3|11|11881.98|0.03|0.03|N|O|1997-07-12|1997-09-04|1997-08-01|TAKE BACK RETURN|REG AIR|ages wake slyly about the furiously regular| +1923|193|5|4|49|53566.31|0.06|0.05|N|O|1997-07-21|1997-08-08|1997-07-26|NONE|AIR|de of the carefully expre| +1923|184|5|5|25|27104.50|0.10|0.08|N|O|1997-08-18|1997-08-20|1997-09-12|DELIVER IN PERSON|TRUCK|the ideas: slyly pendin| +1923|37|3|6|50|46851.50|0.03|0.03|N|O|1997-11-04|1997-08-08|1997-11-25|NONE|TRUCK|uickly along the bold courts. bold the| +1924|73|1|1|7|6811.49|0.06|0.07|N|O|1997-01-01|1996-12-02|1997-01-08|COLLECT COD|SHIP|osits. even accounts nag furious| +1924|18|8|2|47|43146.47|0.02|0.06|N|O|1996-11-24|1996-10-18|1996-12-13|COLLECT COD|REG AIR|silent requests cajole blithely final pack| +1924|57|8|3|40|38282.00|0.04|0.08|N|O|1996-10-31|1996-11-30|1996-11-21|NONE|REG AIR|ains sleep carefully| +1924|34|5|4|31|28954.93|0.03|0.03|N|O|1996-09-20|1996-10-19|1996-10-19|DELIVER IN PERSON|SHIP| the slyly regular foxes. ruthle| +1924|36|7|5|17|15912.51|0.04|0.05|N|O|1996-12-31|1996-11-12|1997-01-25|COLLECT COD|TRUCK|e carefully theodolites. ironically ironic | +1924|76|4|6|15|14641.05|0.02|0.04|N|O|1997-01-04|1996-11-13|1997-01-27|NONE|SHIP|he package| +1924|40|1|7|21|19740.84|0.09|0.03|N|O|1996-09-21|1996-11-12|1996-10-02|TAKE BACK RETURN|AIR| blithely reg| +1925|184|5|1|50|54209.00|0.01|0.02|R|F|1992-04-12|1992-04-23|1992-05-08|TAKE BACK RETURN|TRUCK|usual pinto| +1925|135|1|2|35|36229.55|0.06|0.06|R|F|1992-05-11|1992-04-10|1992-05-14|TAKE BACK RETURN|AIR|counts. carefully ironic packages boost ab| +1925|116|10|3|40|40644.40|0.08|0.08|A|F|1992-05-17|1992-05-20|1992-06-08|TAKE BACK RETURN|AIR|e carefully regul| +1925|30|5|4|17|15810.51|0.06|0.02|R|F|1992-05-18|1992-04-06|1992-06-16|TAKE BACK RETURN|MAIL|instructions sleep. pinto bea| +1926|51|9|1|24|22825.20|0.06|0.05|N|O|1996-05-04|1996-03-14|1996-06-01|DELIVER IN PERSON|RAIL|e theodolites.| +1926|106|9|2|29|29176.90|0.09|0.08|N|O|1996-02-26|1996-03-14|1996-03-14|TAKE BACK RETURN|TRUCK|es. dependencies according to the fl| +1926|178|6|3|10|10781.70|0.02|0.03|N|O|1996-05-23|1996-03-02|1996-06-04|NONE|AIR|usly bold accounts. express accounts| +1926|68|9|4|13|12584.78|0.04|0.02|N|O|1996-04-26|1996-04-13|1996-05-08|DELIVER IN PERSON|MAIL|eans wake bli| +1926|40|1|5|29|27261.16|0.06|0.00|N|O|1996-02-29|1996-03-13|1996-03-24|DELIVER IN PERSON|MAIL|hily unusual packages are fluffily am| +1927|68|5|1|3|2904.18|0.00|0.05|N|O|1995-10-06|1995-12-08|1995-11-05|COLLECT COD|FOB|ccounts affi| +1927|73|2|2|15|14596.05|0.08|0.08|N|O|1995-12-25|1995-12-26|1995-12-31|COLLECT COD|RAIL| carefully regular requests sleep car| +1927|65|10|3|6|5790.36|0.05|0.05|N|O|1995-11-29|1995-11-20|1995-12-08|TAKE BACK RETURN|TRUCK|furiously even wat| +1952|53|8|1|7|6671.35|0.04|0.05|A|F|1994-05-06|1994-06-11|1994-05-12|NONE|RAIL|about the express, even requ| +1952|142|5|2|6|6252.84|0.06|0.05|A|F|1994-05-09|1994-05-21|1994-05-26|DELIVER IN PERSON|AIR|packages haggle. | +1953|128|1|1|25|25703.00|0.07|0.06|A|F|1994-01-07|1994-01-28|1994-01-29|TAKE BACK RETURN|RAIL|ular, regular i| +1953|14|5|2|35|31990.35|0.06|0.06|R|F|1994-02-03|1994-02-25|1994-02-14|DELIVER IN PERSON|FOB|among the fur| +1954|152|7|1|31|32616.65|0.06|0.06|N|O|1997-08-18|1997-07-07|1997-09-03|DELIVER IN PERSON|RAIL|against the packages. bold, ironic e| +1954|182|3|2|1|1082.18|0.03|0.01|N|O|1997-09-16|1997-07-08|1997-10-07|COLLECT COD|MAIL|te. furiously final deposits hag| +1954|199|2|3|11|12091.09|0.07|0.07|N|O|1997-08-07|1997-07-23|1997-08-25|DELIVER IN PERSON|TRUCK|y carefully ironi| +1954|159|4|4|12|12709.80|0.02|0.08|N|O|1997-07-19|1997-07-04|1997-08-06|COLLECT COD|AIR|ongside of the slyly unusual requests. reg| +1954|170|7|5|29|31034.93|0.08|0.08|N|O|1997-08-25|1997-07-15|1997-09-02|DELIVER IN PERSON|RAIL|use thinly furiously regular asy| +1954|177|8|6|13|14003.21|0.00|0.07|N|O|1997-06-15|1997-08-22|1997-06-20|TAKE BACK RETURN|MAIL|y ironic instructions cajole| +1954|194|5|7|49|53615.31|0.05|0.06|N|O|1997-06-04|1997-08-29|1997-06-14|COLLECT COD|TRUCK|eans. final pinto beans sleep furiousl| +1955|137|3|1|32|33188.16|0.02|0.02|A|F|1992-07-05|1992-06-29|1992-08-03|TAKE BACK RETURN|TRUCK|g to the carefully sile| +1955|18|8|2|2|1836.02|0.03|0.01|R|F|1992-07-06|1992-07-06|1992-08-01|COLLECT COD|TRUCK|ickly aroun| +1955|158|6|3|41|43384.15|0.08|0.06|A|F|1992-08-01|1992-06-04|1992-08-07|COLLECT COD|AIR| carefully against the furiously reg| +1955|9|4|4|16|14544.00|0.03|0.07|A|F|1992-04-30|1992-06-23|1992-05-23|TAKE BACK RETURN|FOB|odolites eat s| +1955|159|10|5|11|11650.65|0.09|0.01|A|F|1992-06-03|1992-07-04|1992-06-07|NONE|REG AIR|ously quickly pendi| +1956|177|8|1|8|8617.36|0.02|0.04|A|F|1992-12-25|1992-11-24|1993-01-12|TAKE BACK RETURN|AIR|efully about the ironic, ironic de| +1956|103|6|2|16|16049.60|0.00|0.05|R|F|1992-11-11|1992-11-11|1992-11-30|NONE|FOB|es cajole blithely. pen| +1956|139|5|3|39|40526.07|0.08|0.02|A|F|1992-09-24|1992-11-26|1992-10-15|DELIVER IN PERSON|REG AIR|r theodolites sleep above the b| +1956|29|10|4|11|10219.22|0.10|0.00|A|F|1992-12-19|1992-10-29|1993-01-07|TAKE BACK RETURN|AIR| the braids slee| +1956|155|10|5|16|16882.40|0.08|0.02|R|F|1992-09-28|1992-10-21|1992-09-30|TAKE BACK RETURN|FOB| wake after the | +1957|79|9|1|50|48953.50|0.09|0.05|N|O|1998-08-08|1998-09-28|1998-08-27|COLLECT COD|FOB|gainst the re| +1957|119|3|2|31|31592.41|0.10|0.08|N|O|1998-08-13|1998-08-31|1998-08-16|NONE|REG AIR|express packages maintain fluffi| +1958|73|2|1|9|8757.63|0.01|0.05|N|O|1995-12-08|1995-12-17|1995-12-18|DELIVER IN PERSON|REG AIR|ickly. slyly bold | +1958|176|7|2|29|31208.93|0.05|0.06|N|O|1996-01-19|1995-12-05|1996-02-14|COLLECT COD|SHIP|d pinto beans| +1958|102|3|3|4|4008.40|0.04|0.02|N|O|1995-10-24|1995-12-09|1995-10-28|DELIVER IN PERSON|AIR|he slyly even dependencies | +1958|83|4|4|38|37357.04|0.09|0.07|N|O|1995-10-09|1995-11-26|1995-11-05|COLLECT COD|TRUCK|yly. slyly regular courts use silentl| +1958|101|8|5|31|31034.10|0.08|0.01|N|O|1995-10-31|1995-11-12|1995-11-07|TAKE BACK RETURN|TRUCK|r deposits c| +1958|17|4|6|44|40348.44|0.08|0.04|N|O|1995-12-17|1995-11-30|1996-01-15|TAKE BACK RETURN|RAIL|c theodolites after the unusual deposit| +1958|39|5|7|29|27231.87|0.02|0.05|N|O|1995-10-14|1995-11-06|1995-11-01|NONE|REG AIR|final requests nag according to the | +1959|169|10|1|46|49181.36|0.04|0.00|N|O|1997-05-05|1997-03-03|1997-05-24|TAKE BACK RETURN|AIR| furiously ex| +1959|120|7|2|15|15301.80|0.08|0.07|N|O|1997-01-20|1997-02-18|1997-02-08|DELIVER IN PERSON|MAIL| quickly sp| +1984|53|5|1|45|42887.25|0.03|0.04|N|O|1998-04-09|1998-06-11|1998-05-01|COLLECT COD|AIR|p. quickly final ideas sle| +1984|70|7|2|35|33952.45|0.01|0.07|N|O|1998-05-18|1998-05-04|1998-06-01|COLLECT COD|RAIL|tes. quickly pending packages haggle boldl| +1985|28|1|1|33|30624.66|0.10|0.03|R|F|1994-12-04|1994-11-01|1994-12-05|DELIVER IN PERSON|FOB|s are express packages. pendin| +1985|21|6|2|50|46051.00|0.04|0.02|R|F|1994-09-30|1994-10-18|1994-10-12|COLLECT COD|AIR|ate carefully. carefully| +1985|134|10|3|20|20682.60|0.07|0.03|R|F|1994-10-29|1994-11-12|1994-11-27|NONE|TRUCK|regular requests. furiously express| +1985|199|10|4|30|32975.70|0.05|0.07|R|F|1994-09-06|1994-10-10|1994-09-26|NONE|RAIL|uickly. instr| +1985|124|9|5|42|43013.04|0.05|0.05|R|F|1994-10-25|1994-11-03|1994-11-19|DELIVER IN PERSON|SHIP| patterns? final requests after the sp| +1985|20|7|6|2|1840.04|0.02|0.00|A|F|1994-11-25|1994-10-09|1994-12-25|TAKE BACK RETURN|FOB| silent inst| +1986|92|3|1|12|11905.08|0.06|0.05|A|F|1994-08-17|1994-06-28|1994-09-02|COLLECT COD|RAIL|sleep furiously fluffily final| +1986|105|8|2|10|10051.00|0.10|0.03|R|F|1994-05-14|1994-06-21|1994-06-02|COLLECT COD|REG AIR|yly into the carefully even | +1986|63|2|3|14|13482.84|0.04|0.02|R|F|1994-07-14|1994-06-19|1994-08-08|NONE|SHIP|the packages. pending, unusual| +1987|16|6|1|7|6412.07|0.03|0.03|A|F|1994-07-30|1994-07-06|1994-08-29|NONE|REG AIR| regular a| +1988|72|1|1|36|34994.52|0.09|0.04|N|O|1996-01-21|1995-11-24|1996-01-27|NONE|RAIL|gular theodolites. | +1988|199|3|2|19|20884.61|0.08|0.08|N|O|1996-02-03|1995-12-10|1996-02-14|COLLECT COD|FOB|lly about the slyly thin instructions. f| +1988|54|6|3|8|7632.40|0.06|0.01|N|O|1995-10-20|1995-11-11|1995-11-18|DELIVER IN PERSON|AIR|le quickly ac| +1988|36|2|4|27|25272.81|0.08|0.00|N|O|1996-01-27|1995-12-24|1996-02-24|TAKE BACK RETURN|TRUCK|uests. regular requests are according to t| +1988|79|8|5|26|25455.82|0.08|0.04|N|O|1996-01-25|1995-12-15|1996-01-26|COLLECT COD|SHIP| ironic dolphins haggl| +1988|86|7|6|9|8874.72|0.08|0.03|N|O|1995-12-26|1996-01-02|1996-01-25|DELIVER IN PERSON|MAIL|lar platelets. slyly ironic packa| +1989|10|7|1|47|42770.47|0.10|0.02|R|F|1994-06-21|1994-05-27|1994-06-22|TAKE BACK RETURN|REG AIR|final deposits s| +1990|101|2|1|46|46050.60|0.01|0.07|R|F|1994-12-29|1995-03-14|1995-01-13|NONE|TRUCK|ar sentiments.| +1991|110|3|1|39|39394.29|0.06|0.02|A|F|1993-01-01|1992-11-29|1993-01-10|TAKE BACK RETURN|TRUCK|ckages? carefully bold depos| +1991|53|1|2|49|46699.45|0.08|0.06|R|F|1992-10-19|1992-11-29|1992-10-25|NONE|SHIP|nd the ideas affi| +1991|174|5|3|6|6445.02|0.02|0.01|A|F|1992-11-02|1992-10-08|1992-11-14|TAKE BACK RETURN|REG AIR|hes nag slyly| +1991|138|9|4|6|6228.78|0.10|0.06|A|F|1992-11-21|1992-11-03|1992-11-27|NONE|RAIL|uickly blithely final de| +1991|60|8|5|49|47042.94|0.06|0.00|R|F|1992-09-10|1992-11-30|1992-10-07|NONE|AIR|quests cajole blithely| +2016|147|4|1|2|2094.28|0.02|0.07|N|O|1996-10-12|1996-11-09|1996-10-31|DELIVER IN PERSON|TRUCK|carefully according to the | +2016|63|8|2|15|14445.90|0.04|0.05|N|O|1996-09-24|1996-10-05|1996-10-21|TAKE BACK RETURN|MAIL|uests haggle carefully furiously regul| +2016|122|7|3|8|8176.96|0.09|0.02|N|O|1996-09-19|1996-10-21|1996-10-13|TAKE BACK RETURN|SHIP|mptotes haggle ideas. packages wake flu| +2017|103|4|1|49|49151.90|0.10|0.06|N|O|1998-05-26|1998-07-01|1998-06-06|COLLECT COD|TRUCK| after the unusual instructions. sly| +2017|71|2|2|14|13594.98|0.07|0.04|N|O|1998-06-28|1998-06-15|1998-07-11|NONE|TRUCK|ily final w| +2017|84|5|3|11|10824.88|0.05|0.02|N|O|1998-05-22|1998-07-13|1998-05-26|TAKE BACK RETURN|TRUCK|gside of the slyly dogged dolp| +2018|195|6|1|2|2190.38|0.02|0.07|N|O|1995-06-25|1995-06-20|1995-07-04|NONE|TRUCK|ly ironic accounts against the slyly sly| +2018|129|10|2|23|23669.76|0.05|0.01|R|F|1995-05-05|1995-05-12|1995-05-22|TAKE BACK RETURN|RAIL|ingly even theodolites s| +2019|4|9|1|31|28024.00|0.07|0.03|R|F|1992-11-18|1992-12-26|1992-11-24|DELIVER IN PERSON|FOB|l ideas across the slowl| +2019|52|7|2|18|17136.90|0.04|0.03|R|F|1993-01-24|1992-12-22|1993-02-02|NONE|MAIL|are carefully furiously regular requ| +2020|34|10|1|50|46701.50|0.06|0.01|R|F|1993-07-12|1993-08-28|1993-08-02|COLLECT COD|TRUCK|ts against the pending ideas serve along| +2020|176|4|2|40|43046.80|0.09|0.00|A|F|1993-10-17|1993-09-14|1993-10-29|TAKE BACK RETURN|RAIL|ently across the| +2020|14|4|3|30|27420.30|0.07|0.04|A|F|1993-09-08|1993-08-11|1993-09-29|TAKE BACK RETURN|AIR|ly about the blithely ironic foxes. bold| +2020|61|8|4|27|25948.62|0.05|0.06|A|F|1993-07-14|1993-09-02|1993-08-03|NONE|FOB|e of the bold foxes haggle | +2021|85|6|1|7|6895.56|0.08|0.04|N|O|1995-10-17|1995-09-29|1995-10-20|NONE|MAIL| accounts boost blithely. blithely reg| +2021|166|3|2|19|20257.04|0.04|0.05|N|O|1995-08-14|1995-09-05|1995-08-23|NONE|RAIL| above the slyly fl| +2022|169|8|1|38|40628.08|0.00|0.08|R|F|1992-07-05|1992-04-20|1992-07-13|TAKE BACK RETURN|REG AIR| against the express accounts wake ca| +2022|55|3|2|38|36291.90|0.05|0.04|R|F|1992-06-17|1992-05-15|1992-06-28|COLLECT COD|SHIP|instructions dazzle carefull| +2022|49|10|3|48|45553.92|0.10|0.02|A|F|1992-06-14|1992-06-04|1992-07-12|DELIVER IN PERSON|SHIP|counts. slyly enticing accounts are during | +2022|182|3|4|16|17314.88|0.05|0.03|R|F|1992-06-23|1992-05-22|1992-07-07|NONE|TRUCK|ages wake slyly care| +2022|100|1|5|36|36003.60|0.05|0.02|R|F|1992-03-24|1992-05-07|1992-04-13|NONE|MAIL|ly after the foxes. regular, final inst| +2022|129|2|6|20|20582.40|0.08|0.08|A|F|1992-03-31|1992-04-17|1992-04-02|NONE|SHIP|r deposits kindle | +2022|78|9|7|13|12714.91|0.06|0.08|R|F|1992-04-04|1992-05-30|1992-04-21|NONE|FOB| orbits haggle fluffily fl| +2023|127|10|1|9|9244.08|0.05|0.04|R|F|1992-06-04|1992-06-30|1992-06-10|NONE|AIR|ly regular pinto beans poa| +2023|38|4|2|2|1876.06|0.01|0.00|R|F|1992-08-27|1992-07-16|1992-08-29|DELIVER IN PERSON|RAIL|ing packages. fluffily silen| +2023|19|6|3|25|22975.25|0.10|0.03|A|F|1992-07-19|1992-07-07|1992-08-15|NONE|REG AIR| wake furiously among the slyly final| +2023|185|6|4|9|9766.62|0.02|0.00|A|F|1992-07-23|1992-07-04|1992-08-20|TAKE BACK RETURN|AIR|nts maintain blithely alongside of the| +2023|20|10|5|22|20240.44|0.04|0.06|A|F|1992-06-15|1992-07-13|1992-06-21|TAKE BACK RETURN|SHIP|ronic attainments. | +2023|43|2|6|29|27348.16|0.02|0.06|A|F|1992-08-29|1992-07-28|1992-09-18|COLLECT COD|RAIL|usual instructions. bli| +2023|134|10|7|50|51706.50|0.00|0.03|R|F|1992-06-20|1992-07-04|1992-06-23|DELIVER IN PERSON|FOB|its! carefully ex| +2048|35|1|1|7|6545.21|0.06|0.01|R|F|1993-12-07|1994-01-31|1994-01-05|TAKE BACK RETURN|REG AIR|lent platelets boost deposits. carefully sp| +2048|8|5|2|5|4540.00|0.04|0.04|A|F|1994-01-18|1994-02-01|1994-01-29|TAKE BACK RETURN|TRUCK|affix carefully against | +2048|101|2|3|12|12013.20|0.01|0.05|R|F|1994-01-28|1994-01-19|1994-02-08|NONE|AIR| even theodoli| +2048|97|1|4|11|10967.99|0.10|0.03|R|F|1993-12-20|1994-01-19|1994-01-04|TAKE BACK RETURN|MAIL|totes. idly ironic packages nag| +2049|189|10|1|25|27229.50|0.08|0.00|N|O|1996-03-31|1996-02-29|1996-04-15|DELIVER IN PERSON|MAIL| excuses above the | +2049|35|1|2|31|28985.93|0.10|0.05|N|O|1995-12-25|1996-02-25|1995-12-29|TAKE BACK RETURN|MAIL| packages are slyly alongside| +2049|67|6|3|18|17407.08|0.05|0.05|N|O|1996-01-09|1996-01-22|1996-01-25|TAKE BACK RETURN|AIR| sleep fluffily. dependencies use never| +2049|6|7|4|39|35334.00|0.02|0.05|N|O|1996-01-17|1996-01-21|1996-02-03|TAKE BACK RETURN|MAIL|the even pinto beans | +2049|126|1|5|30|30783.60|0.04|0.06|N|O|1995-12-16|1996-02-04|1995-12-22|NONE|TRUCK|ial accounts are among the furiously perma| +2049|84|5|6|17|16729.36|0.07|0.00|N|O|1996-02-04|1996-03-01|1996-02-24|NONE|FOB|al, regular foxes. pending, | +2050|73|2|1|47|45734.29|0.05|0.03|A|F|1994-08-25|1994-07-18|1994-09-15|DELIVER IN PERSON|TRUCK|tside the blithely pending packages eat f| +2050|152|3|2|48|50503.20|0.05|0.01|A|F|1994-09-30|1994-08-23|1994-10-29|COLLECT COD|AIR| final packages. pinto| +2050|113|4|3|41|41537.51|0.10|0.04|A|F|1994-06-08|1994-08-27|1994-06-23|NONE|AIR| final theodolites. depende| +2050|32|8|4|11|10252.33|0.02|0.01|A|F|1994-07-27|1994-08-18|1994-08-02|DELIVER IN PERSON|REG AIR|ns. bold, final ideas cajole among the fi| +2050|168|9|5|16|17090.56|0.07|0.01|R|F|1994-08-17|1994-07-28|1994-09-05|DELIVER IN PERSON|REG AIR|al accounts. closely even | +2050|49|2|6|29|27522.16|0.00|0.05|A|F|1994-09-23|1994-08-01|1994-10-23|TAKE BACK RETURN|MAIL|oxes alongsid| +2050|48|5|7|25|23701.00|0.10|0.00|R|F|1994-08-18|1994-07-04|1994-09-04|TAKE BACK RETURN|RAIL|y according to | +2051|25|6|1|43|39775.86|0.08|0.04|N|O|1996-04-22|1996-06-16|1996-04-28|COLLECT COD|RAIL|ounts sleep fluffily even requ| +2051|130|1|2|48|49446.24|0.01|0.02|N|O|1996-05-04|1996-06-14|1996-05-19|NONE|TRUCK|unts. pending platelets believe about| +2052|68|7|1|50|48403.00|0.09|0.08|R|F|1992-06-22|1992-06-03|1992-07-19|DELIVER IN PERSON|AIR|wake after the decoy| +2052|135|1|2|35|36229.55|0.09|0.05|A|F|1992-05-29|1992-05-24|1992-06-11|NONE|TRUCK|ts according t| +2052|43|2|3|16|15088.64|0.01|0.08|A|F|1992-06-30|1992-07-09|1992-07-12|NONE|SHIP|y final deposits cajole according | +2052|96|7|4|47|46816.23|0.08|0.01|A|F|1992-06-18|1992-05-16|1992-07-02|TAKE BACK RETURN|REG AIR|final requests. stealt| +2053|101|4|1|20|20022.00|0.09|0.00|A|F|1995-04-25|1995-04-12|1995-05-13|NONE|TRUCK|ly ironic foxes haggle slyly speci| +2053|33|4|2|34|31723.02|0.07|0.00|A|F|1995-03-15|1995-03-20|1995-04-09|TAKE BACK RETURN|TRUCK|ions. unusual dependencies| +2053|65|2|3|46|44392.76|0.01|0.03|R|F|1995-04-01|1995-04-02|1995-04-18|NONE|RAIL|tions. furiously even requests hagg| +2053|121|6|4|31|31654.72|0.06|0.08|R|F|1995-03-23|1995-03-13|1995-04-16|DELIVER IN PERSON|SHIP|ts. fluffily final mul| +2054|113|4|1|11|11144.21|0.03|0.05|R|F|1992-08-13|1992-08-26|1992-08-22|NONE|AIR|ular accou| +2054|120|7|2|31|31623.72|0.05|0.08|A|F|1992-08-18|1992-09-04|1992-08-24|NONE|FOB|se bold, regular accounts. unusual depos| +2054|121|2|3|32|32675.84|0.06|0.00|A|F|1992-06-23|1992-07-08|1992-07-22|NONE|FOB| packages thrash. carefully final| +2054|174|3|4|14|15038.38|0.10|0.05|R|F|1992-06-25|1992-09-05|1992-07-14|DELIVER IN PERSON|SHIP|uickly final| +2054|6|1|5|40|36240.00|0.08|0.06|R|F|1992-06-23|1992-08-09|1992-07-04|TAKE BACK RETURN|RAIL|n pinto beans. ironic courts are iro| +2054|134|10|6|17|17580.21|0.08|0.01|A|F|1992-06-09|1992-08-28|1992-06-16|NONE|AIR|ges nag acc| +2054|11|1|7|4|3644.04|0.00|0.08|R|F|1992-08-12|1992-08-31|1992-08-15|DELIVER IN PERSON|AIR|lyly careful requests wake fl| +2055|45|6|1|15|14175.60|0.04|0.06|A|F|1993-09-15|1993-10-06|1993-10-07|NONE|REG AIR|furiously bold | +2055|9|10|2|15|13635.00|0.06|0.05|R|F|1993-10-30|1993-11-21|1993-11-22|COLLECT COD|RAIL|gular foxes. b| +2055|135|1|3|12|12421.56|0.00|0.02|A|F|1993-10-26|1993-11-23|1993-11-22|COLLECT COD|TRUCK|al pains. acco| +2055|134|10|4|16|16546.08|0.02|0.02|A|F|1993-11-16|1993-11-12|1993-11-28|NONE|TRUCK|arefully daringly regular accounts.| +2080|7|4|1|5|4535.00|0.08|0.05|R|F|1993-08-26|1993-08-07|1993-09-02|DELIVER IN PERSON|TRUCK|refully unusual theo| +2080|197|9|2|39|42790.41|0.07|0.04|A|F|1993-08-22|1993-09-09|1993-08-23|COLLECT COD|FOB|ic deposits haggle slyly carefully eve| +2081|89|10|1|26|25716.08|0.03|0.08|N|O|1997-10-21|1997-10-03|1997-11-10|NONE|FOB|among the slyly express accounts. silen| +2081|149|2|2|13|13638.82|0.07|0.05|N|O|1997-08-23|1997-08-22|1997-09-09|TAKE BACK RETURN|MAIL|fter the even deposi| +2081|13|10|3|32|29216.32|0.09|0.07|N|O|1997-09-05|1997-09-26|1997-10-03|TAKE BACK RETURN|SHIP|e. final, regular dependencies sleep slyly!| +2081|85|6|4|23|22656.84|0.03|0.08|N|O|1997-07-06|1997-09-11|1997-07-21|TAKE BACK RETURN|MAIL|ual requests wake blithely above the| +2081|113|7|5|19|19249.09|0.02|0.06|N|O|1997-10-01|1997-08-12|1997-10-18|COLLECT COD|SHIP|s affix sometimes express requests. quickly| +2081|142|9|6|31|32306.34|0.03|0.06|N|O|1997-09-19|1997-09-13|1997-09-27|NONE|AIR| silent, spe| +2082|75|3|1|36|35102.52|0.00|0.00|R|F|1995-01-20|1995-03-18|1995-01-31|COLLECT COD|MAIL|haggle furiously silent pinto beans| +2082|105|10|2|12|12061.20|0.08|0.05|A|F|1995-01-27|1995-02-11|1995-02-07|NONE|FOB| ironic instructions. carefull| +2083|24|3|1|37|34188.74|0.07|0.00|R|F|1993-09-07|1993-09-30|1993-09-18|TAKE BACK RETURN|MAIL|ng the special foxes wake packages. f| +2084|182|3|1|42|45451.56|0.03|0.05|A|F|1993-03-29|1993-05-05|1993-04-22|COLLECT COD|REG AIR|y fluffily even foxes. | +2084|180|10|2|23|24844.14|0.09|0.08|A|F|1993-06-05|1993-05-26|1993-06-06|DELIVER IN PERSON|AIR|es against | +2084|136|2|3|37|38336.81|0.07|0.05|A|F|1993-07-16|1993-04-20|1993-08-06|NONE|AIR|y careful courts.| +2084|94|8|4|9|8946.81|0.02|0.02|A|F|1993-03-18|1993-06-08|1993-03-30|NONE|TRUCK|heaves boost slyly after the pla| +2084|27|10|5|28|25956.56|0.07|0.02|R|F|1993-05-04|1993-05-14|1993-05-31|COLLECT COD|TRUCK|cajole quickly carefu| +2084|115|9|6|15|15226.65|0.09|0.04|A|F|1993-06-23|1993-04-25|1993-07-23|COLLECT COD|SHIP|tithes. bravely pendi| +2084|194|6|7|34|37202.46|0.09|0.02|R|F|1993-06-20|1993-05-28|1993-06-25|DELIVER IN PERSON|RAIL| carefully ironic requests. fluffil| +2085|41|8|1|45|42346.80|0.00|0.07|R|F|1994-02-27|1994-01-11|1994-03-29|TAKE BACK RETURN|MAIL|. carefully e| +2086|60|1|1|22|21121.32|0.03|0.07|R|F|1994-12-04|1994-12-16|1994-12-20|DELIVER IN PERSON|RAIL|idly busy acc| +2086|141|10|2|32|33316.48|0.04|0.06|A|F|1994-11-15|1995-01-05|1994-12-09|TAKE BACK RETURN|TRUCK|e carefully along th| +2086|105|6|3|44|44224.40|0.02|0.01|A|F|1994-12-04|1994-11-30|1994-12-21|DELIVER IN PERSON|FOB|latelets s| +2086|84|5|4|27|26570.16|0.02|0.00|A|F|1994-11-04|1995-01-14|1994-11-25|COLLECT COD|REG AIR|theodolites haggle blithely blithe p| +2086|156|1|5|33|34852.95|0.04|0.00|A|F|1995-02-06|1994-11-25|1995-02-15|NONE|SHIP| slyly regular foxes. un| +2086|200|3|6|20|22004.00|0.01|0.03|R|F|1994-11-30|1994-12-28|1994-12-07|COLLECT COD|FOB|lithely ironic acc| +2086|156|8|7|7|7393.05|0.04|0.05|R|F|1994-12-27|1994-12-10|1995-01-05|COLLECT COD|RAIL| beans haggle car| +2087|127|8|1|1|1027.12|0.05|0.04|N|O|1998-03-27|1998-03-24|1998-04-18|DELIVER IN PERSON|REG AIR|the quickly idle acco| +2087|168|3|2|46|49135.36|0.10|0.03|N|O|1998-02-24|1998-04-02|1998-03-04|DELIVER IN PERSON|AIR|ter the dolphins.| +2087|62|3|3|1|962.06|0.02|0.05|N|O|1998-05-27|1998-04-11|1998-06-12|COLLECT COD|REG AIR|hely final acc| +2087|59|1|4|6|5754.30|0.03|0.08|N|O|1998-04-23|1998-03-27|1998-05-18|DELIVER IN PERSON|REG AIR|dazzle after the slyly si| +2112|71|2|1|18|17479.26|0.02|0.05|N|O|1997-05-02|1997-03-16|1997-05-25|TAKE BACK RETURN|TRUCK|lphins solve ideas. even, special reque| +2113|123|8|1|40|40924.80|0.04|0.06|N|O|1998-01-16|1997-12-11|1998-02-06|TAKE BACK RETURN|TRUCK|bout the quickly ironic t| +2113|112|2|2|24|24290.64|0.03|0.02|N|O|1998-02-19|1998-01-08|1998-03-16|COLLECT COD|MAIL|kly regular accounts hinder about the| +2114|168|9|1|50|53408.00|0.05|0.05|A|F|1995-02-05|1995-03-18|1995-02-13|COLLECT COD|RAIL|pecial pinto bean| +2114|186|7|2|26|28240.68|0.02|0.02|A|F|1995-04-30|1995-04-16|1995-05-28|NONE|SHIP|ar asymptotes sleep | +2114|162|1|3|25|26554.00|0.07|0.01|A|F|1995-02-15|1995-03-13|1995-02-22|COLLECT COD|AIR|unts. regular, express accounts wake. b| +2115|196|8|1|27|29597.13|0.06|0.03|N|O|1998-09-01|1998-07-29|1998-09-04|NONE|AIR|de of the carefully bold accounts | +2115|184|5|2|43|46619.74|0.06|0.02|N|O|1998-07-14|1998-07-25|1998-07-24|COLLECT COD|FOB| carefully pending requests alongs| +2115|51|3|3|3|2853.15|0.03|0.04|N|O|1998-07-23|1998-07-30|1998-08-14|DELIVER IN PERSON|FOB|quickly ironic dolphin| +2115|49|10|4|47|44604.88|0.06|0.07|N|O|1998-08-29|1998-07-30|1998-09-05|TAKE BACK RETURN|REG AIR|regular accounts integrate brav| +2115|199|3|5|13|14289.47|0.04|0.00|N|O|1998-08-07|1998-08-06|1998-08-13|DELIVER IN PERSON|REG AIR|into beans. even accounts abou| +2116|131|2|1|2|2062.26|0.00|0.02|R|F|1994-10-16|1994-11-24|1994-11-09|DELIVER IN PERSON|TRUCK|r theodolites use blithely about the ir| +2116|140|1|2|47|48886.58|0.10|0.06|R|F|1994-09-01|1994-11-18|1994-09-25|COLLECT COD|MAIL|iously ironic dependencies around the iro| +2116|184|5|3|11|11925.98|0.03|0.05|R|F|1994-09-15|1994-10-21|1994-09-21|NONE|FOB| pinto beans. final, final sauternes play | +2117|165|2|1|36|38345.76|0.10|0.01|N|O|1997-08-06|1997-07-15|1997-08-07|DELIVER IN PERSON|SHIP|ronic accounts wake| +2117|61|6|2|19|18260.14|0.04|0.00|N|O|1997-07-30|1997-06-18|1997-08-13|DELIVER IN PERSON|REG AIR|s between the slyly regula| +2117|58|3|3|43|41196.15|0.04|0.03|N|O|1997-06-27|1997-06-12|1997-07-22|DELIVER IN PERSON|SHIP| foxes sleep furiously | +2117|91|4|4|24|23786.16|0.00|0.07|N|O|1997-06-15|1997-05-27|1997-06-18|COLLECT COD|SHIP|thely slyly pending platelets. ironic, | +2117|147|8|5|3|3141.42|0.02|0.05|N|O|1997-05-05|1997-07-20|1997-05-26|TAKE BACK RETURN|TRUCK|tes cajole| +2117|1|4|6|27|24327.00|0.09|0.08|N|O|1997-06-30|1997-06-27|1997-07-11|TAKE BACK RETURN|REG AIR| the carefully ironic ideas| +2118|160|1|1|24|25443.84|0.10|0.03|N|O|1997-01-06|1996-12-14|1997-01-14|TAKE BACK RETURN|RAIL|about the slyly bold depende| +2118|184|5|2|4|4336.72|0.08|0.01|N|O|1996-10-25|1996-11-10|1996-11-22|COLLECT COD|AIR|theodolites affix according | +2118|145|4|3|11|11496.54|0.05|0.04|N|O|1996-12-23|1996-12-20|1997-01-01|COLLECT COD|RAIL|y ironic accounts sleep upon the packages. | +2119|102|7|1|36|36075.60|0.04|0.00|N|O|1996-11-10|1996-10-25|1996-12-03|TAKE BACK RETURN|RAIL|ly bold foxes. ironic accoun| +2144|92|6|1|33|32738.97|0.00|0.07|R|F|1994-04-04|1994-06-20|1994-04-23|NONE|AIR| ironic excuses haggle final dependencies. | +2144|51|9|2|46|43748.30|0.03|0.08|R|F|1994-04-08|1994-04-29|1994-05-07|COLLECT COD|SHIP| foxes haggle blithel| +2144|4|9|3|29|26216.00|0.00|0.07|R|F|1994-05-03|1994-05-16|1994-06-01|DELIVER IN PERSON|FOB|ns wake carefully carefully ironic| +2144|158|9|4|10|10581.50|0.00|0.04|R|F|1994-06-16|1994-05-03|1994-07-05|COLLECT COD|AIR| furiously unusual ideas. carefull| +2145|78|8|1|13|12714.91|0.04|0.05|A|F|1992-11-12|1992-12-13|1992-12-07|TAKE BACK RETURN|MAIL|alongside of the slyly final| +2145|154|6|2|6|6324.90|0.05|0.01|A|F|1992-10-10|1992-11-29|1992-10-14|NONE|AIR|s. fluffily express accounts sleep. slyl| +2146|57|5|1|42|40196.10|0.10|0.01|A|F|1992-09-21|1992-11-02|1992-09-23|NONE|AIR|ns according to the doggedly | +2146|157|5|2|6|6342.90|0.07|0.05|A|F|1993-01-03|1992-10-24|1993-01-24|DELIVER IN PERSON|RAIL|ing to the requests. dependencies boost | +2146|25|8|3|14|12950.28|0.03|0.01|R|F|1992-09-16|1992-10-16|1992-09-20|COLLECT COD|SHIP|ecial, express a| +2146|26|9|4|31|28706.62|0.02|0.00|A|F|1993-01-04|1992-10-24|1993-01-15|DELIVER IN PERSON|TRUCK|lly even deposit| +2146|169|4|5|28|29936.48|0.02|0.05|R|F|1993-01-03|1992-10-17|1993-01-08|COLLECT COD|MAIL|r accounts sleep furio| +2146|71|9|6|32|31074.24|0.07|0.03|R|F|1993-01-10|1992-10-19|1993-02-05|COLLECT COD|TRUCK|y regular foxes wake among the final| +2146|25|6|7|39|36075.78|0.07|0.06|R|F|1993-01-05|1992-11-06|1993-01-14|DELIVER IN PERSON|TRUCK|uickly regular excuses detect. regular c| +2147|29|8|1|50|46451.00|0.04|0.06|R|F|1992-11-18|1992-11-30|1992-11-30|NONE|RAIL|al accounts. even, even foxes wake| +2147|101|2|2|4|4004.40|0.01|0.04|A|F|1992-09-27|1992-11-15|1992-10-22|NONE|AIR|mong the blithely special| +2147|44|7|3|34|32097.36|0.10|0.04|R|F|1992-11-29|1992-11-08|1992-12-22|TAKE BACK RETURN|REG AIR|egular deposits hang car| +2147|11|8|4|11|10021.11|0.06|0.07|A|F|1992-09-27|1992-11-16|1992-10-16|NONE|AIR| the fluffily| +2148|116|6|1|21|21338.31|0.09|0.01|R|F|1995-05-28|1995-05-26|1995-06-15|NONE|FOB|deposits ag| +2149|19|9|1|12|11028.12|0.05|0.07|R|F|1993-06-01|1993-05-06|1993-06-11|TAKE BACK RETURN|TRUCK|riously bl| +2149|99|10|2|10|9990.90|0.06|0.01|R|F|1993-06-09|1993-04-17|1993-06-16|DELIVER IN PERSON|TRUCK|eposits sleep above| +2149|49|2|3|47|44604.88|0.00|0.04|R|F|1993-06-27|1993-05-12|1993-07-11|COLLECT COD|AIR|hely final depo| +2149|129|8|4|18|18524.16|0.06|0.00|A|F|1993-04-05|1993-05-11|1993-04-23|DELIVER IN PERSON|REG AIR|uriously final pac| +2149|60|5|5|22|21121.32|0.06|0.04|R|F|1993-05-24|1993-04-23|1993-06-20|TAKE BACK RETURN|SHIP|ptotes sleep along the blithely ir| +2150|78|7|1|26|25429.82|0.00|0.03|A|F|1994-06-21|1994-08-05|1994-06-23|NONE|TRUCK|. always unusual packages| +2150|18|8|2|29|26622.29|0.04|0.03|A|F|1994-09-02|1994-08-04|1994-10-02|TAKE BACK RETURN|RAIL|y ironic theodolites. foxes ca| +2150|107|2|3|29|29205.90|0.04|0.08|R|F|1994-06-10|1994-07-31|1994-06-26|COLLECT COD|RAIL|arefully final att| +2150|54|6|4|39|37207.95|0.05|0.02|R|F|1994-07-31|1994-08-17|1994-08-11|TAKE BACK RETURN|TRUCK|ess accounts nag. unusual asymptotes haggl| +2150|183|4|5|35|37911.30|0.01|0.01|A|F|1994-09-27|1994-08-17|1994-10-13|COLLECT COD|RAIL|refully pending dependen| +2150|7|10|6|12|10884.00|0.09|0.03|A|F|1994-08-27|1994-08-22|1994-09-18|COLLECT COD|AIR|press platelets haggle until the slyly fi| +2151|167|2|1|23|24544.68|0.06|0.02|N|O|1996-11-20|1996-12-17|1996-11-30|DELIVER IN PERSON|AIR| silent dependencies about the slyl| +2151|15|9|2|29|26535.29|0.00|0.02|N|O|1997-03-04|1996-12-27|1997-03-21|TAKE BACK RETURN|SHIP| bold packages acro| +2151|165|2|3|49|52192.84|0.07|0.01|N|O|1997-01-20|1997-02-09|1997-02-18|NONE|FOB| packages. f| +2151|18|5|4|28|25704.28|0.10|0.08|N|O|1996-12-11|1996-12-26|1996-12-12|DELIVER IN PERSON|AIR|y special packages. carefully ironic instru| +2176|191|4|1|38|41465.22|0.02|0.08|R|F|1992-11-29|1993-01-14|1992-12-22|DELIVER IN PERSON|REG AIR|lithely ironic pinto beans. furious| +2176|95|8|2|14|13931.26|0.00|0.06|A|F|1992-11-17|1993-01-07|1992-12-03|DELIVER IN PERSON|SHIP|ely ironic platelets | +2176|160|1|3|25|26504.00|0.02|0.02|R|F|1993-02-23|1993-01-05|1993-03-07|COLLECT COD|RAIL| ruthless deposits according to the ent| +2176|143|6|4|2|2086.28|0.05|0.06|A|F|1993-02-26|1993-01-08|1993-03-23|DELIVER IN PERSON|AIR|s pinto beans| +2177|129|10|1|45|46310.40|0.02|0.01|N|O|1997-02-11|1997-02-27|1997-02-17|NONE|SHIP|. theodolites haggle carefu| +2177|139|5|2|27|28056.51|0.04|0.08|N|O|1997-01-29|1997-03-20|1997-02-04|DELIVER IN PERSON|SHIP|even, regula| +2177|81|2|3|23|22564.84|0.07|0.05|N|O|1997-01-28|1997-03-02|1997-02-13|DELIVER IN PERSON|AIR|he silent foxes. iro| +2177|55|3|4|34|32471.70|0.05|0.07|N|O|1997-02-03|1997-04-10|1997-02-21|COLLECT COD|REG AIR|tes are doggedly quickly| +2177|57|9|5|46|44024.30|0.09|0.05|N|O|1997-05-10|1997-02-23|1997-05-28|COLLECT COD|RAIL|ending asymptotes.| +2177|122|7|6|11|11243.32|0.02|0.04|N|O|1997-03-20|1997-03-07|1997-04-09|DELIVER IN PERSON|MAIL|gainst the ca| +2178|157|2|1|15|15857.25|0.10|0.01|N|O|1997-03-27|1997-03-10|1997-04-18|NONE|REG AIR|l accounts. quickly expr| +2178|16|10|2|27|24732.27|0.01|0.02|N|O|1997-02-26|1997-02-19|1997-03-25|NONE|MAIL| across the ironic reques| +2178|5|2|3|40|36200.00|0.00|0.03|N|O|1997-03-17|1997-02-09|1997-04-15|COLLECT COD|RAIL|foxes are slowly regularly specia| +2178|78|6|4|3|2934.21|0.07|0.07|N|O|1997-04-07|1997-01-23|1997-04-18|COLLECT COD|MAIL| permanentl| +2179|130|9|1|22|22662.86|0.05|0.08|N|O|1996-11-16|1996-11-03|1996-11-25|DELIVER IN PERSON|FOB|lphins cajole acr| +2179|139|5|2|20|20782.60|0.03|0.01|N|O|1996-09-30|1996-11-10|1996-10-30|NONE|REG AIR|ncies. fin| +2179|104|9|3|5|5020.50|0.03|0.02|N|O|1996-11-09|1996-10-08|1996-11-11|DELIVER IN PERSON|REG AIR|ts haggle blithely. ironic, careful theodol| +2179|6|3|4|24|21744.00|0.04|0.04|N|O|1996-10-26|1996-11-05|1996-11-16|COLLECT COD|RAIL| cajole carefully. | +2179|108|5|5|7|7056.70|0.00|0.02|N|O|1996-10-24|1996-11-14|1996-11-21|TAKE BACK RETURN|RAIL|gular dependencies. ironic packages haggle| +2180|16|3|1|31|28396.31|0.06|0.04|N|O|1996-10-20|1996-11-21|1996-11-06|COLLECT COD|REG AIR|n requests are furiously at the quickly| +2180|193|7|2|39|42634.41|0.01|0.00|N|O|1997-01-03|1996-10-29|1997-01-25|NONE|RAIL|ep furiously furiously final request| +2180|197|9|3|24|26332.56|0.03|0.00|N|O|1997-01-03|1996-10-24|1997-01-19|NONE|SHIP|uriously f| +2180|111|5|4|47|47522.17|0.07|0.02|N|O|1996-09-23|1996-12-08|1996-10-12|NONE|FOB|pending, regular ideas. iron| +2180|143|2|5|23|23992.22|0.02|0.06|N|O|1996-11-08|1996-10-25|1996-11-28|NONE|TRUCK|ggle alongside of the fluffily speci| +2180|55|6|6|48|45842.40|0.09|0.03|N|O|1996-12-30|1996-11-22|1997-01-16|DELIVER IN PERSON|RAIL|nic instructions haggle careful| +2181|178|9|1|4|4312.68|0.05|0.04|N|O|1995-09-25|1995-11-12|1995-09-28|COLLECT COD|FOB|tes. slyly silent packages use along th| +2181|88|9|2|46|45451.68|0.00|0.02|N|O|1995-11-28|1995-10-17|1995-12-26|COLLECT COD|AIR|osits. final packages sleep| +2181|91|2|3|15|14866.35|0.08|0.05|N|O|1995-10-05|1995-10-27|1995-11-03|DELIVER IN PERSON|FOB|e above the fluffily regul| +2181|55|10|4|28|26741.40|0.04|0.05|N|O|1995-12-21|1995-10-23|1996-01-04|TAKE BACK RETURN|AIR|s excuses sleep car| +2181|96|7|5|9|8964.81|0.06|0.07|N|O|1996-01-05|1995-12-05|1996-01-08|COLLECT COD|TRUCK|ward the quietly even requests. ir| +2182|132|8|1|27|27867.51|0.02|0.07|R|F|1994-05-10|1994-07-04|1994-06-04|DELIVER IN PERSON|SHIP|en platele| +2182|190|1|2|3|3270.57|0.05|0.03|R|F|1994-04-20|1994-07-04|1994-04-24|TAKE BACK RETURN|SHIP|y bold theodolites wi| +2182|94|6|3|34|33799.06|0.02|0.03|R|F|1994-05-28|1994-06-02|1994-06-10|COLLECT COD|MAIL| slow tithes. ironi| +2182|7|4|4|12|10884.00|0.04|0.07|A|F|1994-05-08|1994-06-02|1994-05-09|COLLECT COD|REG AIR|ments are fu| +2182|179|9|5|37|39929.29|0.06|0.02|A|F|1994-04-08|1994-06-29|1994-04-18|TAKE BACK RETURN|TRUCK|ges. blithely ironic| +2183|71|1|1|29|28161.03|0.05|0.01|N|O|1996-07-21|1996-08-24|1996-08-15|TAKE BACK RETURN|RAIL|ly unusual deposits sleep carefully| +2183|52|3|2|25|23801.25|0.06|0.02|N|O|1996-07-06|1996-08-21|1996-08-05|NONE|RAIL|he quickly f| +2208|58|3|1|48|45986.40|0.08|0.07|A|F|1995-05-13|1995-06-30|1995-05-20|COLLECT COD|MAIL|sits. idly permanent request| +2208|97|1|2|11|10967.99|0.08|0.01|A|F|1995-05-06|1995-07-19|1995-05-22|COLLECT COD|TRUCK|ding waters lose. furiously regu| +2208|74|4|3|41|39936.87|0.08|0.02|N|O|1995-08-18|1995-06-19|1995-09-05|COLLECT COD|RAIL|nd the furious, express dependencies.| +2208|43|2|4|50|47152.00|0.07|0.07|N|F|1995-06-11|1995-05-31|1995-06-29|TAKE BACK RETURN|FOB|al foxes will hav| +2208|30|5|5|43|39991.29|0.03|0.06|A|F|1995-05-10|1995-06-02|1995-06-09|TAKE BACK RETURN|MAIL|es. accounts cajole. fi| +2208|167|2|6|18|19208.88|0.02|0.08|R|F|1995-06-06|1995-06-10|1995-06-11|TAKE BACK RETURN|TRUCK|packages are quickly bold de| +2208|7|2|7|45|40815.00|0.00|0.08|A|F|1995-05-05|1995-06-10|1995-05-11|NONE|SHIP|e fluffily regular theodolites caj| +2209|23|2|1|40|36920.80|0.05|0.01|R|F|1992-11-01|1992-09-25|1992-11-08|DELIVER IN PERSON|SHIP|ully special sheaves serve| +2209|103|4|2|10|10031.00|0.00|0.02|R|F|1992-09-02|1992-09-24|1992-09-21|DELIVER IN PERSON|AIR|players. carefully reg| +2209|64|1|3|11|10604.66|0.01|0.01|A|F|1992-07-12|1992-08-24|1992-08-10|DELIVER IN PERSON|REG AIR|express, regular pinto be| +2209|181|2|4|39|42166.02|0.08|0.07|R|F|1992-11-04|1992-09-02|1992-11-11|TAKE BACK RETURN|MAIL|ly around the final packages. deposits ca| +2209|124|7|5|24|24578.88|0.08|0.06|R|F|1992-08-09|1992-08-18|1992-08-25|COLLECT COD|AIR| along the bol| +2209|178|7|6|7|7547.19|0.09|0.07|A|F|1992-08-18|1992-09-09|1992-09-12|DELIVER IN PERSON|AIR| quickly regular pack| +2210|78|7|1|36|35210.52|0.10|0.00|A|F|1992-03-04|1992-03-24|1992-03-21|DELIVER IN PERSON|AIR| requests wake enticingly final| +2211|48|1|1|25|23701.00|0.04|0.01|A|F|1994-10-09|1994-08-04|1994-11-03|TAKE BACK RETURN|RAIL|deas. carefully special theodolites along| +2211|140|6|2|40|41605.60|0.09|0.06|A|F|1994-09-30|1994-09-10|1994-10-26|NONE|MAIL|posits among the express dolphins| +2211|160|2|3|25|26504.00|0.00|0.07|A|F|1994-08-13|1994-08-17|1994-08-16|NONE|AIR|ly regular, express| +2211|85|6|4|23|22656.84|0.03|0.02|R|F|1994-10-05|1994-09-13|1994-10-17|DELIVER IN PERSON|AIR|ependencies | +2211|135|1|5|3|3105.39|0.02|0.04|A|F|1994-08-28|1994-09-10|1994-09-06|TAKE BACK RETURN|SHIP|pendencies after the regular f| +2211|187|8|6|18|19569.24|0.05|0.08|A|F|1994-08-31|1994-09-07|1994-09-22|NONE|TRUCK|c grouches. slyly express pinto | +2211|79|9|7|3|2937.21|0.06|0.05|R|F|1994-09-21|1994-08-10|1994-10-19|TAKE BACK RETURN|RAIL|y slyly final| +2212|71|10|1|18|17479.26|0.07|0.06|R|F|1994-06-22|1994-06-18|1994-06-25|TAKE BACK RETURN|FOB| cajole. final, pending ideas should are bl| +2213|118|8|1|20|20362.20|0.01|0.00|A|F|1993-01-21|1993-04-14|1993-01-29|COLLECT COD|REG AIR|iously express accounts; | +2213|60|1|2|4|3840.24|0.09|0.05|R|F|1993-04-15|1993-04-15|1993-05-05|COLLECT COD|SHIP| affix carefully furiously | +2213|70|5|3|1|970.07|0.05|0.05|A|F|1993-04-25|1993-04-06|1993-04-28|TAKE BACK RETURN|AIR|s along the ironic reques| +2213|174|3|4|39|41892.63|0.09|0.05|A|F|1993-05-12|1993-04-07|1993-05-23|TAKE BACK RETURN|SHIP|the blithely | +2213|38|9|5|43|40335.29|0.04|0.03|A|F|1993-04-18|1993-03-11|1993-05-11|TAKE BACK RETURN|RAIL|r packages are along the carefully bol| +2213|48|5|6|41|38869.64|0.01|0.00|R|F|1993-01-31|1993-03-31|1993-02-28|COLLECT COD|FOB| carefully pend| +2213|64|9|7|3|2892.18|0.02|0.04|A|F|1993-03-09|1993-03-17|1993-04-07|TAKE BACK RETURN|AIR|o wake. ironic platel| +2214|76|5|1|27|26353.89|0.04|0.04|N|O|1998-05-31|1998-06-07|1998-06-19|DELIVER IN PERSON|REG AIR|x fluffily along the even packages-- | +2214|194|5|2|50|54709.50|0.00|0.02|N|O|1998-07-06|1998-06-16|1998-07-16|TAKE BACK RETURN|MAIL|accounts. blith| +2214|113|7|3|42|42550.62|0.04|0.08|N|O|1998-05-26|1998-07-13|1998-06-22|COLLECT COD|FOB|ons. deposi| +2214|196|9|4|22|24116.18|0.01|0.01|N|O|1998-05-30|1998-07-02|1998-06-09|DELIVER IN PERSON|RAIL|t the blithely| +2215|73|1|1|33|32111.31|0.00|0.00|N|O|1996-07-19|1996-08-10|1996-07-30|COLLECT COD|RAIL|dolites cajole b| +2215|33|9|2|30|27990.90|0.01|0.00|N|O|1996-08-15|1996-09-10|1996-08-25|NONE|FOB|ckages caj| +2215|57|5|3|30|28711.50|0.07|0.03|N|O|1996-09-09|1996-07-20|1996-09-28|COLLECT COD|TRUCK|against the carefu| +2215|146|3|4|20|20922.80|0.02|0.02|N|O|1996-09-09|1996-08-10|1996-09-19|NONE|MAIL| unusual deposits haggle carefully. ide| +2240|164|3|1|6|6384.96|0.01|0.00|A|F|1992-06-23|1992-05-17|1992-07-20|COLLECT COD|AIR|ymptotes boost. furiously bold p| +2240|28|1|2|37|34336.74|0.03|0.07|R|F|1992-03-16|1992-05-31|1992-04-05|COLLECT COD|FOB| quickly after the packages? blithely si| +2240|53|5|3|39|37168.95|0.08|0.06|A|F|1992-05-22|1992-05-10|1992-06-08|NONE|FOB|y orbits. final depos| +2240|86|7|4|10|9860.80|0.09|0.00|A|F|1992-05-25|1992-04-14|1992-06-23|DELIVER IN PERSON|REG AIR|are across the ironic packages.| +2240|161|10|5|29|30773.64|0.02|0.06|A|F|1992-03-29|1992-05-08|1992-04-09|COLLECT COD|MAIL|lyly even ideas w| +2240|81|2|6|32|31394.56|0.06|0.06|R|F|1992-04-11|1992-04-18|1992-04-22|NONE|MAIL|ss thinly deposits. blithely bold package| +2240|78|7|7|24|23473.68|0.04|0.05|R|F|1992-05-13|1992-04-09|1992-05-14|DELIVER IN PERSON|FOB|ng the silent accounts. slyly ironic t| +2241|5|6|1|25|22625.00|0.00|0.08|R|F|1993-08-11|1993-07-23|1993-09-01|DELIVER IN PERSON|MAIL| final deposits use fluffily. even f| +2241|195|8|2|38|41617.22|0.04|0.06|A|F|1993-08-04|1993-07-31|1993-08-06|TAKE BACK RETURN|TRUCK| silent, unusual d| +2241|97|10|3|48|47860.32|0.08|0.04|A|F|1993-05-14|1993-07-30|1993-05-26|TAKE BACK RETURN|RAIL|ss accounts engage furiously. slyly even re| +2241|167|4|4|19|20276.04|0.10|0.00|A|F|1993-06-01|1993-08-05|1993-06-07|TAKE BACK RETURN|TRUCK| are furiously quickl| +2241|82|3|5|2|1964.16|0.04|0.03|A|F|1993-08-16|1993-08-02|1993-08-24|NONE|REG AIR|, express deposits. pear| +2241|116|3|6|22|22354.42|0.02|0.08|R|F|1993-08-13|1993-06-15|1993-08-16|DELIVER IN PERSON|TRUCK|, ironic depen| +2241|142|3|7|9|9379.26|0.09|0.03|A|F|1993-05-14|1993-07-12|1993-05-29|NONE|AIR|lyly final | +2242|123|4|1|15|15346.80|0.09|0.08|N|O|1997-08-04|1997-09-21|1997-08-11|COLLECT COD|FOB|its. carefully express packages cajole. bli| +2243|127|8|1|10|10271.20|0.04|0.06|N|O|1995-07-26|1995-07-18|1995-08-03|NONE|RAIL|express, daring foxes affix fur| +2244|51|6|1|3|2853.15|0.02|0.02|A|F|1993-04-30|1993-03-15|1993-05-19|TAKE BACK RETURN|FOB| beans for the regular platel| +2244|193|6|2|16|17491.04|0.01|0.06|R|F|1993-02-12|1993-03-09|1993-02-28|COLLECT COD|FOB|rate around the reques| +2245|76|7|1|44|42947.08|0.03|0.03|A|F|1993-06-12|1993-06-10|1993-06-16|NONE|TRUCK|refully even sheaves| +2245|74|3|2|28|27273.96|0.05|0.03|R|F|1993-08-19|1993-07-27|1993-09-04|COLLECT COD|TRUCK|e requests sleep furiou| +2245|86|7|3|33|32540.64|0.03|0.01|R|F|1993-06-26|1993-06-11|1993-07-17|TAKE BACK RETURN|AIR|ing to the carefully ruthless accounts| +2245|189|10|4|14|15248.52|0.02|0.04|R|F|1993-05-06|1993-07-21|1993-05-19|DELIVER IN PERSON|RAIL|nts. always unusual dep| +2245|80|8|5|33|32342.64|0.03|0.07|R|F|1993-06-16|1993-06-05|1993-07-07|NONE|MAIL| across the express reques| +2246|53|4|1|22|20967.10|0.02|0.01|N|O|1996-07-25|1996-08-03|1996-08-24|DELIVER IN PERSON|SHIP|ructions wake carefully fina| +2246|104|5|2|43|43176.30|0.07|0.06|N|O|1996-08-25|1996-08-23|1996-09-19|DELIVER IN PERSON|AIR|ainst the ironic theodolites haggle fi| +2246|18|8|3|11|10098.11|0.10|0.00|N|O|1996-06-21|1996-07-24|1996-07-18|TAKE BACK RETURN|TRUCK|quests alongside o| +2246|163|8|4|13|13821.08|0.08|0.05|N|O|1996-09-15|1996-07-21|1996-10-08|DELIVER IN PERSON|AIR|equests. fluffily special epitaphs use| +2247|172|2|1|12|12866.04|0.02|0.07|A|F|1992-09-06|1992-09-18|1992-09-26|NONE|MAIL|final accounts. requests across the furiou| +2272|90|1|1|18|17821.62|0.04|0.00|R|F|1993-08-01|1993-07-06|1993-08-25|NONE|MAIL|ons along the blithely e| +2272|34|10|2|40|37361.20|0.07|0.00|A|F|1993-04-25|1993-07-12|1993-05-15|DELIVER IN PERSON|FOB|lithely ir| +2272|56|4|3|36|34417.80|0.03|0.02|A|F|1993-05-25|1993-05-23|1993-06-09|TAKE BACK RETURN|RAIL|about the ironic packages; quickly iron| +2272|138|4|4|30|31143.90|0.09|0.07|A|F|1993-07-27|1993-05-15|1993-08-13|NONE|RAIL|quests at the foxes haggle evenly pack| +2272|76|4|5|12|11712.84|0.03|0.03|A|F|1993-04-19|1993-05-14|1993-04-23|NONE|RAIL| accounts cajole. quickly b| +2273|184|5|1|34|36862.12|0.02|0.03|N|O|1997-01-08|1997-02-02|1997-01-23|COLLECT COD|MAIL| furiously carefully bold de| +2273|85|6|2|35|34477.80|0.00|0.05|N|O|1997-01-02|1997-01-19|1997-01-14|NONE|REG AIR|arefully f| +2273|95|8|3|8|7960.72|0.00|0.04|N|O|1996-12-15|1997-02-27|1997-01-10|NONE|FOB|dependencies. slyly ir| +2273|161|6|4|20|21223.20|0.06|0.04|N|O|1997-03-05|1997-02-25|1997-04-01|NONE|RAIL|cuses. quickly enticing requests wake | +2273|162|7|5|18|19118.88|0.07|0.00|N|O|1996-12-16|1997-01-21|1997-01-03|COLLECT COD|TRUCK| beans. doggedly final packages wake| +2273|155|7|6|16|16882.40|0.10|0.03|N|O|1997-01-10|1997-02-03|1997-02-01|TAKE BACK RETURN|RAIL|furiously above the ironic requests. | +2273|20|1|7|7|6440.14|0.05|0.05|N|O|1997-02-19|1997-01-22|1997-02-21|TAKE BACK RETURN|TRUCK|ts. furiou| +2274|12|6|1|18|16416.18|0.04|0.03|R|F|1993-09-06|1993-12-03|1993-09-22|COLLECT COD|SHIP|usly final re| +2274|111|8|2|23|23255.53|0.04|0.03|R|F|1993-10-28|1993-11-03|1993-11-05|NONE|MAIL|kly special warhorse| +2274|129|10|3|18|18524.16|0.03|0.06|R|F|1993-09-28|1993-11-22|1993-10-12|DELIVER IN PERSON|SHIP| express packages. even accounts hagg| +2275|34|5|1|30|28020.90|0.08|0.05|R|F|1993-01-10|1992-11-21|1993-01-22|NONE|REG AIR|re slyly slyly special idea| +2275|91|4|2|11|10901.99|0.08|0.03|A|F|1993-01-16|1992-12-10|1993-01-25|COLLECT COD|REG AIR|ost across the never express instruction| +2276|119|9|1|5|5095.55|0.07|0.08|N|O|1996-05-09|1996-06-18|1996-05-13|DELIVER IN PERSON|FOB|ias instea| +2276|135|1|2|13|13456.69|0.08|0.04|N|O|1996-07-24|1996-06-18|1996-08-16|COLLECT COD|RAIL|arefully ironic foxes cajole q| +2276|171|2|3|27|28921.59|0.07|0.08|N|O|1996-07-30|1996-06-10|1996-07-31|DELIVER IN PERSON|RAIL|the carefully unusual accoun| +2276|109|6|4|38|38345.80|0.06|0.03|N|O|1996-07-07|1996-06-28|1996-07-17|COLLECT COD|RAIL|ans. pinto beans boost c| +2276|153|5|5|50|52657.50|0.03|0.05|N|O|1996-07-13|1996-06-25|1996-07-22|DELIVER IN PERSON|REG AIR| accounts dete| +2276|6|9|6|4|3624.00|0.10|0.03|N|O|1996-07-05|1996-06-30|1996-08-04|COLLECT COD|FOB|s. deposits | +2277|137|8|1|38|39410.94|0.03|0.07|R|F|1995-04-23|1995-03-25|1995-05-20|TAKE BACK RETURN|TRUCK|fully bold| +2277|8|1|2|2|1816.00|0.10|0.08|A|F|1995-02-01|1995-02-04|1995-03-02|TAKE BACK RETURN|AIR|endencies sleep idly pending p| +2277|198|10|3|4|4392.76|0.05|0.06|R|F|1995-04-27|1995-03-16|1995-04-29|TAKE BACK RETURN|SHIP|. quickly unusual deposi| +2277|159|4|4|31|32833.65|0.02|0.00|R|F|1995-03-07|1995-03-19|1995-03-26|TAKE BACK RETURN|MAIL|ic instructions detect ru| +2278|45|2|1|36|34021.44|0.04|0.05|N|O|1998-06-04|1998-06-06|1998-06-30|NONE|TRUCK|y ironic pinto beans br| +2278|45|2|2|50|47252.00|0.02|0.00|N|O|1998-08-09|1998-07-08|1998-09-05|DELIVER IN PERSON|RAIL|into beans. blit| +2278|97|9|3|22|21935.98|0.03|0.00|N|O|1998-05-15|1998-07-14|1998-06-04|TAKE BACK RETURN|REG AIR|ep regular accounts. blithely even| +2279|14|5|1|12|10968.12|0.07|0.08|A|F|1993-05-10|1993-03-25|1993-06-02|COLLECT COD|REG AIR|lets across the excuses nag quickl| +2279|41|2|2|38|35759.52|0.08|0.07|R|F|1993-06-09|1993-04-06|1993-06-26|COLLECT COD|TRUCK|s above the furiously express dep| +2279|4|7|3|3|2712.00|0.09|0.04|A|F|1993-05-31|1993-05-07|1993-06-05|COLLECT COD|REG AIR|ing foxes above the even accounts use slyly| +2279|52|4|4|42|39986.10|0.02|0.00|R|F|1993-02-28|1993-04-25|1993-03-02|TAKE BACK RETURN|REG AIR| above the furiously ironic deposits. | +2279|169|8|5|9|9622.44|0.05|0.04|R|F|1993-05-21|1993-03-29|1993-06-17|DELIVER IN PERSON|MAIL|ns cajole after the final platelets. s| +2279|147|10|6|12|12565.68|0.02|0.00|R|F|1993-05-04|1993-04-26|1993-05-28|DELIVER IN PERSON|FOB|ccounts. slyl| +2279|119|9|7|32|32611.52|0.05|0.05|A|F|1993-04-20|1993-05-22|1993-05-18|DELIVER IN PERSON|RAIL|re quickly. furiously ironic ide| +2304|200|2|1|42|46208.40|0.00|0.01|A|F|1994-01-20|1994-03-04|1994-02-05|COLLECT COD|RAIL|quests are blithely alongside of| +2304|19|9|2|48|44112.48|0.00|0.00|R|F|1994-02-12|1994-02-16|1994-03-10|COLLECT COD|REG AIR| deposits cajole blithely e| +2304|48|9|3|3|2844.12|0.00|0.05|R|F|1994-03-19|1994-03-04|1994-03-20|DELIVER IN PERSON|AIR|l excuses after the ev| +2305|174|4|1|3|3222.51|0.00|0.01|A|F|1993-03-24|1993-04-05|1993-03-29|NONE|AIR|kages haggle quickly across the blithely | +2305|60|8|2|39|37442.34|0.07|0.00|R|F|1993-04-16|1993-04-17|1993-04-22|COLLECT COD|MAIL|ms after the foxes | +2305|102|3|3|32|32067.20|0.03|0.06|A|F|1993-04-02|1993-03-18|1993-04-03|NONE|AIR| haggle caref| +2305|112|3|4|17|17205.87|0.00|0.05|A|F|1993-02-21|1993-03-30|1993-03-19|TAKE BACK RETURN|MAIL| carefully alongside of | +2305|155|7|5|26|27433.90|0.06|0.07|A|F|1993-05-14|1993-02-28|1993-06-04|NONE|SHIP|arefully final theodo| +2305|51|3|6|7|6657.35|0.06|0.00|R|F|1993-05-15|1993-04-25|1993-06-09|DELIVER IN PERSON|RAIL|gular deposits boost about the foxe| +2306|196|9|1|50|54809.50|0.09|0.01|N|O|1995-07-27|1995-09-26|1995-08-06|DELIVER IN PERSON|FOB|y quickly | +2306|149|2|2|39|40916.46|0.04|0.00|N|O|1995-09-07|1995-09-13|1995-10-03|COLLECT COD|SHIP|f the slyly unusual accounts. furiousl| +2306|178|6|3|35|37735.95|0.01|0.07|N|O|1995-08-18|1995-08-30|1995-08-20|TAKE BACK RETURN|RAIL|raids along the furiously unusual asympto| +2306|119|3|4|21|21401.31|0.06|0.01|N|O|1995-10-07|1995-09-18|1995-10-17|COLLECT COD|MAIL| ironic pinto | +2306|142|9|5|42|43769.88|0.04|0.07|N|O|1995-09-05|1995-08-25|1995-09-28|COLLECT COD|MAIL|furiously final acco| +2306|124|5|6|29|29699.48|0.00|0.03|N|O|1995-11-01|1995-09-01|1995-11-22|DELIVER IN PERSON|REG AIR|uld have to mold. s| +2306|176|4|7|19|20447.23|0.07|0.01|N|O|1995-11-17|1995-09-06|1995-11-30|DELIVER IN PERSON|AIR|tainments nag furiously carefull| +2307|142|9|1|24|25011.36|0.10|0.05|R|F|1993-10-07|1993-08-05|1993-10-20|COLLECT COD|AIR|stealthily special packages nag a| +2307|140|6|2|2|2080.28|0.01|0.00|A|F|1993-09-21|1993-08-22|1993-10-03|COLLECT COD|SHIP|ously. furiously furious requ| +2307|34|10|3|7|6538.21|0.07|0.04|R|F|1993-08-03|1993-09-04|1993-08-28|DELIVER IN PERSON|AIR|ven instructions wake fluffily | +2307|165|6|4|19|20238.04|0.08|0.06|R|F|1993-10-23|1993-09-09|1993-11-09|TAKE BACK RETURN|TRUCK|olites haggle furiously around the | +2307|143|4|5|7|7301.98|0.01|0.06|R|F|1993-09-01|1993-08-08|1993-09-29|NONE|AIR| packages cajo| +2308|118|9|1|24|24434.64|0.06|0.04|R|F|1993-02-23|1992-12-24|1993-03-10|NONE|MAIL|ts sleep. busy excuses along the s| +2308|56|1|2|36|34417.80|0.05|0.06|A|F|1992-11-11|1992-11-27|1992-11-23|NONE|MAIL|ong the pending hockey players. blithe| +2309|170|7|1|14|14982.38|0.10|0.03|N|O|1996-01-01|1995-10-22|1996-01-23|NONE|AIR|asymptotes. furiously pending acco| +2309|169|8|2|1|1069.16|0.01|0.05|N|O|1995-12-08|1995-11-03|1995-12-31|COLLECT COD|RAIL|eposits alongside of the final re| +2309|15|2|3|5|4575.05|0.01|0.00|N|O|1995-12-10|1995-10-29|1996-01-06|TAKE BACK RETURN|SHIP|s. requests wake blithely specia| +2309|139|10|4|46|47799.98|0.08|0.04|N|O|1995-10-02|1995-10-30|1995-10-30|NONE|REG AIR|sly according to the carefully | +2309|137|3|5|9|9334.17|0.00|0.07|N|O|1995-12-21|1995-10-10|1996-01-20|COLLECT COD|AIR|ding, unusual instructions. dep| +2309|195|8|6|21|22998.99|0.09|0.00|N|O|1995-11-05|1995-11-07|1995-11-22|NONE|AIR|unts around the dolphins ar| +2309|138|4|7|48|49830.24|0.03|0.05|N|O|1995-10-21|1995-11-21|1995-11-09|NONE|MAIL|ccounts. id| +2310|58|6|1|36|34489.80|0.03|0.03|N|O|1996-10-09|1996-10-28|1996-10-29|TAKE BACK RETURN|RAIL|iously against the slyly special accounts| +2310|171|2|2|6|6427.02|0.07|0.01|N|O|1996-11-08|1996-12-09|1996-12-07|COLLECT COD|REG AIR|e slyly about the quickly ironic theodo| +2310|42|1|3|48|45217.92|0.08|0.02|N|O|1996-10-04|1996-11-20|1996-10-25|TAKE BACK RETURN|FOB|ep slyly alongside of the | +2311|141|8|1|18|18740.52|0.01|0.01|N|F|1995-06-11|1995-06-18|1995-07-02|NONE|FOB| fluffily even patterns haggle blithely. re| +2311|122|1|2|49|50083.88|0.09|0.02|R|F|1995-05-14|1995-07-11|1995-05-20|COLLECT COD|FOB|ideas sleep| +2311|54|5|3|15|14310.75|0.08|0.04|N|O|1995-06-23|1995-06-06|1995-07-09|COLLECT COD|AIR|ve the blithely pending accounts. furio| +2311|90|1|4|42|41583.78|0.01|0.06|R|F|1995-06-03|1995-06-27|1995-06-11|DELIVER IN PERSON|MAIL|gle furiously. bold | +2311|47|10|5|1|947.04|0.05|0.02|A|F|1995-06-07|1995-06-20|1995-06-10|NONE|AIR|ptotes. furiously regular theodolite| +2311|12|9|6|32|29184.32|0.01|0.03|N|O|1995-07-19|1995-06-26|1995-07-26|NONE|RAIL|sts along the slyly| +2336|193|5|1|20|21863.80|0.01|0.03|N|O|1996-03-12|1996-02-25|1996-03-18|NONE|REG AIR|across the fi| +2337|45|2|1|49|46306.96|0.06|0.05|N|O|1997-08-08|1997-08-15|1997-08-31|TAKE BACK RETURN|FOB| along the packages. furiously p| +2338|52|7|1|30|28561.50|0.07|0.06|N|O|1997-12-10|1997-10-15|1997-12-11|TAKE BACK RETURN|REG AIR|ould have to nag quickly| +2339|192|3|1|22|24028.18|0.03|0.03|A|F|1994-01-06|1994-03-06|1994-01-10|NONE|FOB| furiously above | +2339|30|5|2|28|26040.84|0.00|0.00|R|F|1994-01-25|1994-01-22|1994-01-28|DELIVER IN PERSON|RAIL|e bold, even packag| +2339|117|4|3|13|13222.43|0.06|0.08|R|F|1994-03-10|1994-02-18|1994-03-20|TAKE BACK RETURN|REG AIR|ges. blithely special depend| +2340|138|4|1|9|9343.17|0.08|0.02|N|O|1996-05-01|1996-02-24|1996-05-16|COLLECT COD|RAIL|. carefully ironic| +2340|193|5|2|21|22956.99|0.06|0.02|N|O|1996-01-17|1996-03-04|1996-01-29|DELIVER IN PERSON|SHIP| asymptotes. unusual theo| +2341|47|10|1|12|11364.48|0.08|0.03|R|F|1993-06-06|1993-07-08|1993-06-17|DELIVER IN PERSON|FOB|. quickly final deposits sl| +2341|71|10|2|37|35929.59|0.07|0.08|A|F|1993-09-23|1993-07-25|1993-10-14|DELIVER IN PERSON|RAIL|was blithel| +2341|195|8|3|8|8761.52|0.03|0.07|R|F|1993-06-08|1993-07-09|1993-06-10|COLLECT COD|FOB|ns affix above the iron| +2342|42|1|1|12|11304.48|0.00|0.08|N|O|1996-07-31|1996-07-26|1996-08-14|NONE|TRUCK|print blithely even deposits. carefull| +2342|117|1|2|24|24410.64|0.10|0.06|N|O|1996-09-30|1996-07-22|1996-10-28|TAKE BACK RETURN|AIR|nstructions c| +2342|170|1|3|50|53508.50|0.10|0.01|N|O|1996-08-28|1996-07-18|1996-09-22|COLLECT COD|RAIL|cial asymptotes pr| +2342|36|7|4|1|936.03|0.04|0.06|N|O|1996-08-31|1996-08-09|1996-09-07|DELIVER IN PERSON|REG AIR|ffily. unusual pinto beans wake c| +2342|27|2|5|22|20394.44|0.08|0.01|N|O|1996-08-10|1996-08-02|1996-08-31|DELIVER IN PERSON|AIR|s. ironic | +2343|110|1|1|27|27272.97|0.00|0.00|N|O|1995-11-10|1995-11-17|1995-12-10|TAKE BACK RETURN|SHIP|old theodolites.| +2343|66|1|2|35|33812.10|0.03|0.06|N|O|1995-10-24|1995-11-09|1995-10-26|COLLECT COD|TRUCK|ges haggle furiously carefully regular req| +2343|179|7|3|21|22662.57|0.00|0.03|N|O|1995-09-07|1995-10-26|1995-10-07|TAKE BACK RETURN|RAIL|osits. unusual theodolites boost furio| +2368|152|3|1|16|16834.40|0.04|0.03|R|F|1993-10-31|1993-10-22|1993-11-06|NONE|REG AIR|telets wake carefully iro| +2368|14|5|2|32|29248.32|0.03|0.00|R|F|1993-09-23|1993-10-07|1993-09-27|COLLECT COD|TRUCK|gular courts use blithely around the| +2368|149|6|3|39|40916.46|0.08|0.03|R|F|1993-09-03|1993-09-20|1993-09-28|COLLECT COD|RAIL|ng the doggedly ironic requests are blithe| +2368|156|8|4|17|17954.55|0.10|0.08|A|F|1993-10-03|1993-09-27|1993-10-05|NONE|FOB|fily. slyly final ideas alongside o| +2369|24|7|1|30|27720.60|0.05|0.04|N|O|1997-04-23|1997-02-12|1997-05-21|COLLECT COD|REG AIR|pecial deposits sleep. blithely unusual w| +2369|169|10|2|47|50250.52|0.10|0.02|N|O|1997-01-02|1997-02-18|1997-01-13|COLLECT COD|RAIL| to the regular dep| +2370|46|3|1|3|2838.12|0.03|0.07|R|F|1994-03-24|1994-03-26|1994-04-15|COLLECT COD|SHIP|ly regular Tiresia| +2370|2|5|2|24|21648.00|0.00|0.05|A|F|1994-05-15|1994-04-09|1994-06-12|NONE|REG AIR|final depen| +2370|61|2|3|32|30753.92|0.05|0.02|A|F|1994-04-24|1994-03-03|1994-05-15|DELIVER IN PERSON|MAIL|ies since the final deposits| +2370|6|3|4|21|19026.00|0.04|0.01|R|F|1994-02-01|1994-02-19|1994-02-09|TAKE BACK RETURN|MAIL|ecial dependencies must have to | +2371|159|4|1|37|39188.55|0.05|0.05|N|O|1998-02-11|1998-03-24|1998-02-27|DELIVER IN PERSON|TRUCK|s boost fluffil| +2371|35|1|2|21|19635.63|0.00|0.05|N|O|1998-04-14|1998-02-14|1998-04-18|COLLECT COD|AIR|gle furiously regu| +2371|101|4|3|11|11012.10|0.05|0.02|N|O|1998-02-25|1998-04-06|1998-03-23|TAKE BACK RETURN|TRUCK|requests. regular pinto beans wake. car| +2371|43|6|4|33|31120.32|0.05|0.08|N|O|1998-03-30|1998-02-06|1998-04-05|DELIVER IN PERSON|AIR|deas are. express r| +2371|165|2|5|22|23433.52|0.02|0.05|N|O|1998-03-26|1998-03-19|1998-04-16|DELIVER IN PERSON|REG AIR|y daring accounts. regular ins| +2371|86|7|6|39|38457.12|0.05|0.03|N|O|1998-04-01|1998-03-13|1998-04-27|NONE|REG AIR|tructions. regular, stealthy packages wak| +2371|36|2|7|32|29952.96|0.07|0.07|N|O|1998-02-15|1998-04-03|1998-02-23|NONE|REG AIR|the ruthless accounts. | +2372|43|4|1|42|39607.68|0.08|0.02|N|O|1998-01-04|1998-01-02|1998-02-02|COLLECT COD|REG AIR|lar packages. regular| +2372|3|10|2|17|15351.00|0.07|0.01|N|O|1997-12-17|1998-01-17|1997-12-25|NONE|RAIL|xcuses. slyly ironic theod| +2372|164|1|3|12|12769.92|0.04|0.04|N|O|1998-03-21|1997-12-21|1998-04-12|DELIVER IN PERSON|SHIP|lyly according to| +2372|122|1|4|4|4088.48|0.00|0.07|N|O|1997-12-14|1997-12-28|1997-12-16|TAKE BACK RETURN|REG AIR|e carefully blithely even epitaphs. r| +2372|20|7|5|5|4600.10|0.02|0.04|N|O|1998-02-08|1998-01-18|1998-03-02|TAKE BACK RETURN|RAIL|ets against the | +2372|189|10|6|11|11980.98|0.02|0.06|N|O|1998-02-14|1998-01-18|1998-03-10|TAKE BACK RETURN|FOB| silent, pending de| +2372|57|8|7|19|18183.95|0.01|0.06|N|O|1997-12-26|1998-02-19|1998-01-02|COLLECT COD|SHIP| beans haggle sometimes| +2373|191|5|1|17|18550.23|0.02|0.01|R|F|1994-03-29|1994-05-19|1994-04-20|COLLECT COD|AIR|auternes. blithely even pinto bea| +2373|136|2|2|3|3108.39|0.08|0.08|R|F|1994-05-15|1994-06-10|1994-06-04|COLLECT COD|TRUCK|dependencies wake ironical| +2373|141|8|3|29|30193.06|0.05|0.02|A|F|1994-06-01|1994-05-14|1994-06-17|NONE|TRUCK|yly silent ideas affix furiousl| +2373|91|5|4|5|4955.45|0.10|0.01|R|F|1994-06-02|1994-05-03|1994-06-21|NONE|REG AIR|uffily blithely ironic requests| +2374|118|2|1|41|41742.51|0.07|0.00|A|F|1994-01-27|1993-12-11|1994-02-12|TAKE BACK RETURN|RAIL|heodolites. requests| +2374|160|2|2|24|25443.84|0.07|0.08|A|F|1994-02-02|1994-01-12|1994-02-04|DELIVER IN PERSON|TRUCK|. requests are above t| +2374|61|8|3|2|1922.12|0.06|0.02|R|F|1993-12-30|1994-01-24|1994-01-02|COLLECT COD|FOB|, unusual ideas. deposits cajole quietl| +2374|74|5|4|28|27273.96|0.04|0.08|R|F|1994-02-19|1993-12-16|1994-03-15|COLLECT COD|MAIL|ets cajole fu| +2374|1|2|5|25|22525.00|0.08|0.00|A|F|1993-11-26|1993-12-15|1993-12-10|COLLECT COD|RAIL|refully pending d| +2375|168|9|1|3|3204.48|0.02|0.08|N|O|1997-02-14|1996-12-25|1997-02-22|COLLECT COD|RAIL|slyly across the furiously e| +2375|132|8|2|9|9289.17|0.09|0.02|N|O|1997-02-17|1996-12-27|1997-02-27|DELIVER IN PERSON|MAIL|ly against the packages. bold pinto bean| +2375|47|4|3|26|24623.04|0.02|0.06|N|O|1997-03-18|1997-02-02|1997-03-29|TAKE BACK RETURN|TRUCK|rate across the| +2375|5|8|4|5|4525.00|0.01|0.00|N|O|1997-01-31|1997-01-25|1997-02-22|COLLECT COD|REG AIR|final packages cajole according to the furi| +2375|88|9|5|42|41499.36|0.01|0.08|N|O|1997-01-24|1997-02-15|1997-02-07|DELIVER IN PERSON|FOB|apades. idea| +2375|126|7|6|20|20522.40|0.09|0.08|N|O|1996-12-01|1996-12-26|1996-12-19|TAKE BACK RETURN|SHIP|ckages! blithely enticing deposi| +2400|103|6|1|48|48148.80|0.01|0.02|N|O|1998-10-07|1998-08-30|1998-11-03|DELIVER IN PERSON|MAIL|fore the car| +2400|90|1|2|1|990.09|0.04|0.07|N|O|1998-08-18|1998-09-12|1998-09-11|NONE|MAIL|silent deposits serve furious| +2400|53|5|3|23|21920.15|0.02|0.08|N|O|1998-08-05|1998-08-28|1998-08-30|NONE|SHIP|tions. fluffily ironic platelets cajole c| +2400|17|7|4|23|21091.23|0.09|0.04|N|O|1998-10-04|1998-10-04|1998-10-31|NONE|RAIL|ages lose carefully around the regula| +2401|182|3|1|39|42205.02|0.00|0.03|N|O|1997-09-29|1997-10-21|1997-10-17|DELIVER IN PERSON|FOB|ould affix | +2401|3|8|2|49|44247.00|0.05|0.07|N|O|1997-09-02|1997-09-11|1997-09-13|TAKE BACK RETURN|AIR|lites cajole carefully | +2402|86|7|1|43|42401.44|0.03|0.08|N|O|1996-09-17|1996-11-20|1996-09-22|DELIVER IN PERSON|RAIL|slyly slyly blithe sheaves| +2402|152|4|2|24|25251.60|0.02|0.05|N|O|1996-11-21|1996-10-19|1996-11-29|DELIVER IN PERSON|SHIP|as; blithely ironic requ| +2403|83|4|1|34|33424.72|0.04|0.07|N|O|1998-05-30|1998-06-19|1998-06-05|NONE|REG AIR| slyly bold re| +2403|152|4|2|19|19990.85|0.08|0.07|N|O|1998-04-20|1998-07-02|1998-05-13|DELIVER IN PERSON|FOB|sits. ironic in| +2403|193|4|3|27|29516.13|0.05|0.03|N|O|1998-07-27|1998-07-08|1998-08-03|NONE|SHIP|deposits sleep slyly special theodolit| +2403|31|2|4|30|27930.90|0.05|0.06|N|O|1998-08-08|1998-06-17|1998-08-20|NONE|TRUCK|ackages sleep furiously pendin| +2404|147|10|1|36|37697.04|0.07|0.00|N|O|1997-03-27|1997-05-16|1997-04-06|COLLECT COD|REG AIR|s nag furi| +2404|36|2|2|1|936.03|0.02|0.04|N|O|1997-05-22|1997-06-06|1997-05-28|DELIVER IN PERSON|MAIL|from the final orbits? even pinto beans hag| +2404|18|5|3|41|37638.41|0.02|0.06|N|O|1997-06-12|1997-05-03|1997-07-12|NONE|AIR| dolphins are| +2404|57|8|4|19|18183.95|0.09|0.03|N|O|1997-05-07|1997-05-24|1997-05-24|TAKE BACK RETURN|SHIP|cuses. quickly even in| +2404|4|9|5|18|16272.00|0.00|0.04|N|O|1997-06-25|1997-05-06|1997-07-02|NONE|RAIL|packages. even requests according to | +2405|89|10|1|18|17803.44|0.09|0.07|N|O|1997-01-23|1997-03-10|1997-02-03|COLLECT COD|REG AIR|carefully ironic accounts. slyly | +2405|27|10|2|30|27810.60|0.10|0.08|N|O|1997-03-24|1997-03-10|1997-04-14|TAKE BACK RETURN|AIR|y final deposits are slyly caref| +2405|17|8|3|49|44933.49|0.00|0.06|N|O|1996-12-24|1997-03-23|1997-01-01|TAKE BACK RETURN|FOB|cial requests. ironic, regu| +2405|177|7|4|23|24774.91|0.08|0.05|N|O|1996-12-28|1997-01-29|1997-01-07|NONE|AIR|t wake blithely blithely regular idea| +2406|170|5|1|18|19263.06|0.07|0.05|N|O|1997-02-17|1996-12-25|1997-02-19|COLLECT COD|MAIL|azzle furiously careful| +2406|41|8|2|40|37641.60|0.02|0.07|N|O|1997-01-09|1996-12-02|1997-01-16|NONE|SHIP|gular accounts caj| +2406|50|1|3|16|15200.80|0.07|0.03|N|O|1996-10-31|1996-11-28|1996-11-08|TAKE BACK RETURN|SHIP| special accou| +2406|146|9|4|34|35568.76|0.07|0.06|N|O|1996-12-01|1996-12-07|1996-12-16|NONE|AIR|hinly even accounts are slyly q| +2406|187|8|5|25|27179.50|0.08|0.02|N|O|1996-12-03|1996-12-14|1996-12-26|COLLECT COD|MAIL|al, regular in| +2406|59|4|6|22|21099.10|0.05|0.02|N|O|1996-11-22|1997-01-17|1996-12-15|NONE|TRUCK|hely even foxes unwind furiously aga| +2406|60|2|7|30|28801.80|0.07|0.07|N|O|1997-01-17|1997-01-12|1997-01-22|TAKE BACK RETURN|TRUCK| final pinto beans han| +2407|64|3|1|14|13496.84|0.04|0.02|N|O|1998-10-10|1998-08-25|1998-10-27|NONE|FOB|l dependencies s| +2407|166|7|2|9|9595.44|0.07|0.05|N|O|1998-08-06|1998-08-11|1998-08-20|TAKE BACK RETURN|TRUCK|ts. special deposits are closely.| +2407|131|2|3|39|40214.07|0.02|0.02|N|O|1998-08-20|1998-09-12|1998-08-22|DELIVER IN PERSON|MAIL|iously final deposits solv| +2407|91|4|4|10|9910.90|0.01|0.07|N|O|1998-08-14|1998-09-10|1998-08-29|COLLECT COD|FOB| pending instructions. theodolites x-| +2407|198|1|5|14|15374.66|0.04|0.05|N|O|1998-09-24|1998-08-18|1998-10-06|DELIVER IN PERSON|FOB|tructions wake stealt| +2407|71|9|6|18|17479.26|0.04|0.01|N|O|1998-10-03|1998-08-30|1998-10-19|TAKE BACK RETURN|MAIL| wake carefully. fluffily | +2407|161|8|7|7|7428.12|0.07|0.03|N|O|1998-09-11|1998-08-15|1998-09-30|TAKE BACK RETURN|MAIL|totes are carefully accordin| +2432|50|3|1|30|28501.50|0.03|0.02|N|O|1996-09-05|1996-10-10|1996-10-05|TAKE BACK RETURN|TRUCK| requests wake alongside of| +2432|162|3|2|8|8497.28|0.07|0.01|N|O|1996-10-16|1996-10-01|1996-11-13|COLLECT COD|RAIL|s about the bold, close deposit| +2432|109|2|3|13|13118.30|0.07|0.06|N|O|1996-09-03|1996-10-10|1996-10-03|NONE|RAIL|arefully about the caref| +2432|13|4|4|14|12782.14|0.00|0.06|N|O|1996-08-18|1996-09-04|1996-08-27|TAKE BACK RETURN|RAIL|riously regular packages. p| +2433|87|8|1|39|38496.12|0.01|0.04|R|F|1994-11-20|1994-09-23|1994-12-10|DELIVER IN PERSON|SHIP|ly final asy| +2433|134|5|2|20|20682.60|0.05|0.06|A|F|1994-12-09|1994-10-20|1994-12-15|COLLECT COD|REG AIR|lithely blithely final ide| +2433|157|2|3|38|40171.70|0.08|0.03|A|F|1994-10-15|1994-10-23|1994-11-06|DELIVER IN PERSON|SHIP|. slyly regular requests sle| +2433|121|6|4|43|43908.16|0.01|0.05|A|F|1994-10-16|1994-10-23|1994-11-08|DELIVER IN PERSON|RAIL|ular requests. slyly even pa| +2433|108|1|5|3|3024.30|0.06|0.02|A|F|1994-11-08|1994-09-24|1994-11-17|COLLECT COD|AIR|usly pending depos| +2434|95|6|1|1|995.09|0.01|0.06|N|O|1997-08-02|1997-05-28|1997-08-19|TAKE BACK RETURN|MAIL| furiously express packages. ironic, pend| +2434|127|10|2|39|40057.68|0.09|0.05|N|O|1997-06-10|1997-06-08|1997-07-03|COLLECT COD|RAIL|r deposits sleep furiou| +2434|130|3|3|28|28843.64|0.02|0.05|N|O|1997-06-28|1997-06-26|1997-07-15|COLLECT COD|RAIL|ven theodolites around the slyly| +2434|168|9|4|49|52339.84|0.00|0.05|N|O|1997-08-08|1997-07-23|1997-08-27|DELIVER IN PERSON|FOB| after the requests haggle bold, fina| +2435|39|10|1|8|7512.24|0.08|0.03|A|F|1993-06-08|1993-04-04|1993-06-29|COLLECT COD|SHIP|e fluffily quickly final accounts. care| +2435|49|2|2|43|40808.72|0.03|0.08|A|F|1993-03-27|1993-05-20|1993-04-18|DELIVER IN PERSON|TRUCK|alongside of the s| +2435|12|9|3|24|21888.24|0.07|0.08|R|F|1993-03-14|1993-05-20|1993-03-26|DELIVER IN PERSON|SHIP|s. carefully regular d| +2435|156|4|4|22|23235.30|0.02|0.05|R|F|1993-05-23|1993-04-14|1993-06-04|NONE|SHIP|e final, final deposits. carefully regular| +2435|72|2|5|3|2916.21|0.07|0.07|R|F|1993-06-01|1993-03-25|1993-06-27|DELIVER IN PERSON|FOB| final accounts ar| +2435|46|9|6|17|16082.68|0.02|0.02|A|F|1993-06-05|1993-05-05|1993-06-14|NONE|TRUCK|cajole aft| +2435|121|10|7|8|8168.96|0.07|0.02|R|F|1993-05-03|1993-04-02|1993-05-17|COLLECT COD|SHIP|ng the fluffily special foxes nag | +2436|155|6|1|48|50647.20|0.04|0.02|N|O|1995-10-22|1995-10-22|1995-11-16|DELIVER IN PERSON|FOB|he furiously | +2436|117|7|2|18|18307.98|0.05|0.03|N|O|1995-10-14|1995-11-21|1995-11-12|TAKE BACK RETURN|TRUCK|y ironic accounts. furiously even packa| +2436|164|3|3|6|6384.96|0.06|0.08|N|O|1995-10-25|1995-11-30|1995-11-24|DELIVER IN PERSON|RAIL|odolites. ep| +2437|94|6|1|46|45728.14|0.07|0.04|A|F|1993-08-12|1993-06-16|1993-08-29|NONE|RAIL|e of the bold, dogged requests| +2437|190|1|2|26|28344.94|0.00|0.04|A|F|1993-06-25|1993-05-22|1993-07-07|DELIVER IN PERSON|REG AIR|lyly regular accounts.| +2437|2|7|3|23|20746.00|0.01|0.00|A|F|1993-08-15|1993-06-28|1993-08-23|TAKE BACK RETURN|SHIP|s deposits. pendi| +2437|116|10|4|12|12193.32|0.03|0.08|A|F|1993-04-27|1993-07-01|1993-05-18|TAKE BACK RETURN|FOB|thely regular deposits. ironic fray| +2437|17|7|5|29|26593.29|0.02|0.06|A|F|1993-05-12|1993-06-10|1993-05-25|NONE|FOB|ress dolphins. furiously fin| +2437|19|3|6|10|9190.10|0.10|0.06|A|F|1993-05-20|1993-06-23|1993-05-22|TAKE BACK RETURN|MAIL|unts. even, ironic pl| +2438|165|2|1|45|47932.20|0.01|0.00|A|F|1993-10-27|1993-09-24|1993-11-02|COLLECT COD|REG AIR|en theodolites w| +2438|13|4|2|31|28303.31|0.08|0.01|R|F|1993-10-16|1993-08-31|1993-11-10|COLLECT COD|REG AIR|t. slyly ironic sh| +2438|68|7|3|10|9680.60|0.10|0.00|R|F|1993-08-18|1993-08-28|1993-09-08|NONE|SHIP|engage car| +2438|161|8|4|27|28651.32|0.01|0.02|R|F|1993-07-27|1993-10-01|1993-08-06|TAKE BACK RETURN|FOB|inal accounts. slyly final reques| +2438|166|3|5|28|29852.48|0.07|0.06|R|F|1993-11-05|1993-08-22|1993-11-22|TAKE BACK RETURN|TRUCK|ctions. bli| +2438|149|6|6|23|24130.22|0.09|0.02|R|F|1993-10-06|1993-08-17|1993-10-16|DELIVER IN PERSON|MAIL|ely; blithely special pinto beans breach| +2438|183|4|7|46|49826.28|0.02|0.05|R|F|1993-10-27|1993-08-30|1993-11-14|COLLECT COD|SHIP| ironic requests cajole f| +2439|164|1|1|2|2128.32|0.09|0.03|N|O|1997-04-14|1997-06-11|1997-05-09|COLLECT COD|MAIL|courts boos| +2439|144|5|2|5|5220.70|0.07|0.01|N|O|1997-04-23|1997-04-26|1997-04-28|DELIVER IN PERSON|FOB|ites. furiously| +2439|195|7|3|33|36141.27|0.08|0.05|N|O|1997-06-01|1997-05-15|1997-06-07|TAKE BACK RETURN|FOB|asymptotes wake packages-- furiously| +2464|49|8|1|10|9490.40|0.05|0.03|N|O|1998-02-04|1997-12-29|1998-02-16|TAKE BACK RETURN|RAIL|slyly final pinto bean| +2464|101|6|2|20|20022.00|0.01|0.07|N|O|1997-12-26|1998-01-02|1998-01-24|DELIVER IN PERSON|FOB|sts. slyly close ideas shall h| +2465|68|5|1|27|26137.62|0.05|0.02|N|O|1995-09-05|1995-09-07|1995-09-17|DELIVER IN PERSON|FOB|posits boost carefully unusual instructio| +2465|51|3|2|34|32335.70|0.02|0.05|N|O|1995-10-02|1995-08-04|1995-10-09|COLLECT COD|RAIL|posits wake. regular package| +2465|32|3|3|8|7456.24|0.10|0.00|N|O|1995-10-16|1995-08-26|1995-11-07|TAKE BACK RETURN|FOB|s across the express deposits wak| +2465|148|7|4|45|47166.30|0.03|0.01|N|O|1995-09-27|1995-08-25|1995-10-06|NONE|TRUCK|y silent foxes. final pinto beans above | +2465|47|4|5|50|47352.00|0.01|0.04|N|O|1995-09-01|1995-09-06|1995-09-18|TAKE BACK RETURN|TRUCK|the pending th| +2465|124|5|6|20|20482.40|0.03|0.03|N|O|1995-08-16|1995-08-13|1995-09-02|COLLECT COD|FOB|uriously? furiously ironic excu| +2466|186|7|1|16|17378.88|0.00|0.02|R|F|1994-04-20|1994-04-20|1994-05-09|COLLECT COD|FOB|to beans sl| +2466|105|8|2|10|10051.00|0.00|0.00|A|F|1994-05-08|1994-04-06|1994-06-05|DELIVER IN PERSON|AIR|sly regular deposits. regular, regula| +2466|14|1|3|29|26506.29|0.10|0.07|A|F|1994-06-11|1994-04-27|1994-07-10|DELIVER IN PERSON|FOB|ckages. bold requests nag carefully.| +2466|11|8|4|29|26419.29|0.04|0.04|A|F|1994-04-01|1994-04-20|1994-04-23|DELIVER IN PERSON|MAIL|es boost fluffily ab| +2466|79|10|5|30|29372.10|0.02|0.01|A|F|1994-04-11|1994-05-02|1994-05-02|DELIVER IN PERSON|REG AIR|. fluffily even pinto beans are idly. f| +2466|173|2|6|19|20390.23|0.10|0.07|R|F|1994-06-12|1994-04-18|1994-07-12|NONE|MAIL|ccounts cajole a| +2466|155|7|7|35|36930.25|0.10|0.00|A|F|1994-06-01|1994-05-27|1994-06-21|COLLECT COD|AIR| packages detect carefully: ironically sl| +2467|133|9|1|7|7231.91|0.00|0.00|N|O|1995-07-28|1995-10-04|1995-08-27|NONE|REG AIR|gular packages cajole | +2468|94|7|1|46|45728.14|0.00|0.04|N|O|1997-07-16|1997-08-09|1997-08-07|COLLECT COD|SHIP|unusual theodolites su| +2468|21|10|2|43|39603.86|0.00|0.04|N|O|1997-08-17|1997-08-21|1997-08-30|DELIVER IN PERSON|FOB|uriously eve| +2468|195|6|3|44|48188.36|0.00|0.03|N|O|1997-10-01|1997-08-02|1997-10-09|TAKE BACK RETURN|RAIL|egular, silent sheave| +2468|82|3|4|5|4910.40|0.08|0.00|N|O|1997-06-28|1997-08-02|1997-07-22|NONE|MAIL| sleep fluffily acc| +2468|159|7|5|18|19064.70|0.07|0.00|N|O|1997-07-25|1997-08-26|1997-08-14|DELIVER IN PERSON|REG AIR|cies. fluffily r| +2469|166|1|1|11|11727.76|0.00|0.04|N|O|1997-02-09|1997-01-26|1997-02-16|NONE|TRUCK|ies wake carefully b| +2469|114|1|2|16|16225.76|0.07|0.06|N|O|1997-02-19|1997-02-04|1997-03-18|NONE|MAIL|ing asymptotes | +2469|11|5|3|48|43728.48|0.05|0.06|N|O|1997-01-11|1997-01-03|1997-01-15|TAKE BACK RETURN|AIR|riously even theodolites u| +2469|88|9|4|35|34582.80|0.06|0.06|N|O|1997-02-04|1997-02-02|1997-02-17|DELIVER IN PERSON|RAIL|ld packages haggle regular frets. fluffily | +2469|121|4|5|30|30633.60|0.09|0.01|N|O|1996-12-21|1997-01-29|1997-01-02|COLLECT COD|SHIP| accounts. regular theodolites affix fu| +2469|104|5|6|49|49200.90|0.02|0.02|N|O|1997-03-03|1996-12-26|1997-03-13|NONE|AIR| requests are car| +2469|127|10|7|8|8216.96|0.02|0.00|N|O|1997-03-15|1997-01-20|1997-04-13|NONE|TRUCK|s. regular| +2470|110|5|1|12|12121.32|0.06|0.06|N|O|1997-07-12|1997-05-24|1997-07-17|TAKE BACK RETURN|FOB|l accounts. deposits nag daringly. express,| +2470|100|4|2|50|50005.00|0.03|0.03|N|O|1997-06-02|1997-06-01|1997-06-09|COLLECT COD|AIR| packages | +2470|64|3|3|10|9640.60|0.05|0.08|N|O|1997-06-20|1997-06-19|1997-06-24|TAKE BACK RETURN|FOB| ironic requests a| +2470|162|3|4|30|31864.80|0.04|0.08|N|O|1997-08-04|1997-07-13|1997-08-14|DELIVER IN PERSON|AIR|s across the furiously fina| +2471|84|5|1|37|36410.96|0.05|0.01|N|O|1998-05-28|1998-04-17|1998-06-08|COLLECT COD|TRUCK|ounts mold blithely carefully express depo| +2496|141|8|1|38|39563.32|0.02|0.07|R|F|1994-03-26|1994-04-06|1994-04-23|COLLECT COD|RAIL| bold accounts. furi| +2496|23|4|2|39|35997.78|0.03|0.00|R|F|1994-03-23|1994-02-18|1994-04-10|TAKE BACK RETURN|FOB|arefully special dependencies abo| +2496|189|10|3|36|39210.48|0.09|0.04|R|F|1994-03-27|1994-03-15|1994-04-17|TAKE BACK RETURN|SHIP|ully ironic f| +2496|24|9|4|30|27720.60|0.04|0.01|A|F|1994-01-27|1994-03-11|1994-01-31|DELIVER IN PERSON|RAIL|ake. ironic foxes cajole quickly. fu| +2497|12|2|1|34|31008.34|0.02|0.03|R|F|1992-09-02|1992-10-19|1992-09-12|COLLECT COD|AIR|ronic accounts. p| +2497|77|7|2|15|14656.05|0.09|0.02|A|F|1992-12-23|1992-11-20|1993-01-18|DELIVER IN PERSON|SHIP|sly against the| +2497|34|5|3|28|26152.84|0.02|0.08|A|F|1992-12-02|1992-11-21|1992-12-04|DELIVER IN PERSON|REG AIR|ouches. special, regular requests| +2497|144|5|4|48|50118.72|0.06|0.05|A|F|1992-09-29|1992-11-13|1992-10-19|TAKE BACK RETURN|AIR| even, regular requests across | +2497|175|5|5|28|30104.76|0.04|0.05|A|F|1992-11-10|1992-09-30|1992-11-18|DELIVER IN PERSON|MAIL|hely bold ideas. unusual instructions ac| +2497|71|2|6|19|18450.33|0.05|0.08|A|F|1992-11-10|1992-11-20|1992-12-05|TAKE BACK RETURN|TRUCK| instructions? carefully daring accounts| +2498|143|2|1|48|50070.72|0.10|0.01|R|F|1993-11-25|1994-01-09|1993-12-24|DELIVER IN PERSON|RAIL|onic requests wake| +2499|150|3|1|15|15752.25|0.04|0.06|N|O|1995-12-21|1995-12-06|1996-01-19|DELIVER IN PERSON|FOB| slyly across the slyly| +2499|46|3|2|48|45409.92|0.09|0.03|N|O|1995-10-14|1995-12-12|1995-11-11|DELIVER IN PERSON|AIR|ronic ideas cajole quickly requests. caref| +2499|133|9|3|31|32027.03|0.09|0.05|N|O|1995-12-09|1995-10-28|1996-01-05|COLLECT COD|AIR|to beans across the carefully ironic theodo| +2499|159|7|4|39|41306.85|0.06|0.02|N|O|1995-10-26|1995-10-27|1995-11-07|TAKE BACK RETURN|SHIP|otes sublat| +2499|130|9|5|6|6180.78|0.02|0.01|N|O|1995-11-19|1995-12-14|1995-12-08|NONE|SHIP|cording to the| +2499|119|3|6|12|12229.32|0.04|0.05|N|O|1995-11-18|1995-12-13|1995-11-23|COLLECT COD|REG AIR|le furiously along the r| +2500|192|3|1|40|43687.60|0.00|0.02|A|F|1992-09-02|1992-09-30|1992-09-06|DELIVER IN PERSON|SHIP|efully unusual dolphins s| +2500|37|8|2|34|31859.02|0.06|0.02|R|F|1992-10-03|1992-11-11|1992-10-29|DELIVER IN PERSON|TRUCK| stealthy a| +2500|80|10|3|41|40183.28|0.02|0.00|R|F|1992-09-02|1992-11-11|1992-09-06|DELIVER IN PERSON|RAIL|s could have to integrate after the | +2500|69|8|4|17|16474.02|0.01|0.02|A|F|1992-09-30|1992-10-16|1992-10-05|DELIVER IN PERSON|REG AIR|encies-- ironic, even packages| +2501|84|5|1|4|3936.32|0.10|0.06|N|O|1997-07-17|1997-07-27|1997-07-22|COLLECT COD|RAIL|quests. furiously final| +2501|106|1|2|33|33201.30|0.01|0.04|N|O|1997-07-14|1997-08-09|1997-07-26|NONE|MAIL|leep furiously packages. even sauternes | +2501|72|2|3|20|19441.40|0.10|0.06|N|O|1997-09-23|1997-07-01|1997-10-03|DELIVER IN PERSON|RAIL|equests. furiou| +2501|58|10|4|26|24909.30|0.09|0.01|N|O|1997-07-15|1997-08-15|1997-07-28|DELIVER IN PERSON|SHIP|c accounts. express, iron| +2502|163|4|1|33|35084.28|0.10|0.06|R|F|1993-08-12|1993-07-22|1993-09-04|COLLECT COD|REG AIR|have to print| +2503|123|2|1|33|33762.96|0.06|0.01|R|F|1993-07-06|1993-08-14|1993-08-02|NONE|SHIP|nal courts integrate according to the| +2503|65|10|2|28|27021.68|0.06|0.01|R|F|1993-08-08|1993-08-31|1993-08-10|NONE|SHIP|s wake quickly slyly | +2503|46|7|3|50|47302.00|0.09|0.01|A|F|1993-09-22|1993-08-17|1993-09-29|DELIVER IN PERSON|TRUCK|s around the slyly | +2503|91|5|4|27|26759.43|0.09|0.00|A|F|1993-07-12|1993-07-24|1993-07-22|DELIVER IN PERSON|TRUCK|lly even p| +2503|48|5|5|3|2844.12|0.04|0.02|A|F|1993-07-10|1993-09-17|1993-07-19|TAKE BACK RETURN|TRUCK|s cajole. slyly close courts nod f| +2503|128|7|6|39|40096.68|0.05|0.05|R|F|1993-10-11|1993-09-09|1993-10-16|NONE|MAIL|d carefully fluffily| +2503|19|6|7|17|15623.17|0.09|0.08|R|F|1993-09-04|1993-07-31|1993-09-23|DELIVER IN PERSON|SHIP|c accounts haggle blithel| +2528|1|2|1|10|9010.00|0.02|0.03|R|F|1994-12-12|1994-12-29|1994-12-28|COLLECT COD|REG AIR|ely. fluffily even re| +2528|74|3|2|13|12662.91|0.00|0.03|A|F|1994-11-27|1995-01-20|1994-12-03|TAKE BACK RETURN|REG AIR|ggle furiously. slyly final asympt| +2528|175|6|3|35|37630.95|0.10|0.00|R|F|1994-12-19|1995-02-04|1995-01-15|NONE|MAIL|, even excuses. even,| +2528|65|4|4|37|35707.22|0.00|0.01|A|F|1994-12-25|1995-02-02|1994-12-31|COLLECT COD|AIR|ng the pending excuses haggle after the bl| +2529|131|7|1|4|4124.52|0.07|0.07|N|O|1996-10-19|1996-11-18|1996-10-24|DELIVER IN PERSON|SHIP|al dependencies haggle slyly alongsi| +2530|21|2|1|9|8289.18|0.09|0.03|R|F|1994-05-10|1994-04-30|1994-05-24|TAKE BACK RETURN|REG AIR|lyly ironic| +2530|93|7|2|42|41709.78|0.04|0.08|R|F|1994-03-27|1994-05-20|1994-03-29|NONE|RAIL|ng platelets wake s| +2530|108|1|3|8|8064.80|0.10|0.08|A|F|1994-05-02|1994-05-08|1994-05-24|DELIVER IN PERSON|MAIL|ial asymptotes snooze slyly regular | +2531|148|7|1|9|9433.26|0.03|0.07|N|O|1996-07-27|1996-07-03|1996-08-01|DELIVER IN PERSON|AIR|t the dogged, un| +2531|157|2|2|3|3171.45|0.07|0.06|N|O|1996-07-20|1996-06-20|1996-08-10|NONE|MAIL|he quickly ev| +2531|86|7|3|20|19721.60|0.06|0.04|N|O|1996-07-18|1996-06-25|1996-07-29|TAKE BACK RETURN|TRUCK|into beans. furious| +2531|191|5|4|36|39282.84|0.08|0.01|N|O|1996-06-11|1996-07-26|1996-06-27|NONE|MAIL|y ironic, bold packages. blithely e| +2531|56|4|5|28|26769.40|0.03|0.07|N|O|1996-07-06|1996-07-31|1996-07-19|TAKE BACK RETURN|REG AIR|its. busily| +2531|145|4|6|46|48076.44|0.10|0.08|N|O|1996-07-03|1996-06-27|1996-07-12|TAKE BACK RETURN|REG AIR|e final, bold pains. ir| +2532|53|4|1|3|2859.15|0.06|0.07|N|O|1995-12-14|1995-11-28|1995-12-15|COLLECT COD|FOB|unusual sentiments. even pinto| +2532|160|2|2|33|34985.28|0.06|0.05|N|O|1995-11-23|1996-01-04|1995-12-16|DELIVER IN PERSON|TRUCK|rve carefully slyly ironic accounts! fluf| +2532|135|1|3|1|1035.13|0.00|0.06|N|O|1996-01-27|1995-11-23|1996-01-29|DELIVER IN PERSON|REG AIR|ely final ideas cajole despite the ca| +2532|78|8|4|50|48903.50|0.02|0.02|N|O|1995-11-13|1996-01-01|1995-11-26|NONE|TRUCK|yly after the fluffily regul| +2532|114|1|5|9|9126.99|0.09|0.04|N|O|1995-11-30|1995-11-23|1995-12-12|DELIVER IN PERSON|TRUCK|cial ideas haggle slyly pending request| +2532|150|1|6|20|21003.00|0.09|0.05|N|O|1995-12-02|1995-11-26|1995-12-08|TAKE BACK RETURN|AIR|er the slyly pending| +2533|54|9|1|36|34345.80|0.06|0.04|N|O|1997-06-10|1997-04-28|1997-07-01|NONE|REG AIR|ss requests sleep neve| +2533|198|10|2|5|5490.95|0.10|0.04|N|O|1997-05-26|1997-06-02|1997-06-24|NONE|FOB|ccounts. ironic, special accounts boo| +2533|183|4|3|37|40077.66|0.00|0.08|N|O|1997-05-10|1997-04-26|1997-05-28|COLLECT COD|SHIP| haggle carefully | +2533|30|5|4|17|15810.51|0.06|0.02|N|O|1997-05-23|1997-05-10|1997-06-18|NONE|FOB|ackages. blith| +2533|126|1|5|38|38992.56|0.09|0.00|N|O|1997-05-10|1997-06-02|1997-05-28|TAKE BACK RETURN|REG AIR|of the regular accounts. even packages caj| +2533|184|5|6|20|21683.60|0.05|0.08|N|O|1997-07-04|1997-04-30|1997-07-05|COLLECT COD|FOB|thless excuses are b| +2533|94|7|7|14|13917.26|0.06|0.04|N|O|1997-07-06|1997-05-08|1997-08-03|COLLECT COD|FOB|ut the pending, special depos| +2534|139|5|1|29|30134.77|0.07|0.07|N|O|1996-08-09|1996-09-29|1996-08-11|COLLECT COD|TRUCK|ugouts haggle slyly. final| +2534|27|6|2|49|45423.98|0.08|0.08|N|O|1996-09-01|1996-08-20|1996-09-06|NONE|SHIP|sometimes regular requests. blithely unus| +2534|1|4|3|50|45050.00|0.10|0.06|N|O|1996-09-25|1996-10-07|1996-10-09|TAKE BACK RETURN|AIR|ideas. deposits use. slyly regular pa| +2534|75|3|4|43|41928.01|0.09|0.02|N|O|1996-10-25|1996-09-30|1996-11-05|TAKE BACK RETURN|REG AIR|ngly final depos| +2534|165|2|5|14|14912.24|0.05|0.02|N|O|1996-08-12|1996-09-26|1996-08-28|COLLECT COD|MAIL|eposits doze quickly final| +2534|116|10|6|12|12193.32|0.02|0.02|N|O|1996-07-29|1996-10-12|1996-08-14|TAKE BACK RETURN|AIR|sual depos| +2534|173|3|7|17|18243.89|0.02|0.07|N|O|1996-07-22|1996-09-15|1996-08-03|NONE|SHIP|riously regular | +2535|199|2|1|5|5495.95|0.06|0.01|A|F|1993-09-07|1993-07-25|1993-09-29|DELIVER IN PERSON|REG AIR|, unusual reque| +2535|39|5|2|12|11268.36|0.08|0.05|A|F|1993-07-17|1993-08-17|1993-07-31|TAKE BACK RETURN|FOB|uses sleep among the packages. excuses | +2535|54|5|3|5|4770.25|0.09|0.06|R|F|1993-07-28|1993-08-14|1993-08-11|DELIVER IN PERSON|SHIP| across the express requests. silent, eve| +2535|160|5|4|19|20143.04|0.01|0.02|A|F|1993-06-01|1993-08-01|1993-06-19|DELIVER IN PERSON|FOB|ructions. final requests| +2535|174|3|5|25|26854.25|0.07|0.04|A|F|1993-07-19|1993-08-07|1993-07-27|NONE|REG AIR|ions believe ab| +2560|169|10|1|41|43835.56|0.07|0.01|R|F|1992-10-23|1992-11-11|1992-11-22|NONE|SHIP| after the accounts. regular foxes are be| +2560|4|9|2|27|24408.00|0.00|0.01|R|F|1992-12-03|1992-11-16|1992-12-30|NONE|MAIL| against the carefully| +2560|46|5|3|31|29327.24|0.01|0.05|A|F|1992-11-14|1992-10-14|1992-12-11|DELIVER IN PERSON|AIR|to beans. blithely regular Tiresias int| +2560|72|1|4|36|34994.52|0.01|0.02|A|F|1992-10-18|1992-10-30|1992-11-05|TAKE BACK RETURN|MAIL|accounts alongside of the excuses are | +2560|42|1|5|9|8478.36|0.04|0.02|A|F|1992-10-23|1992-10-29|1992-11-02|COLLECT COD|REG AIR| deposits affix quickly. unusual, eve| +2560|108|9|6|13|13105.30|0.03|0.06|A|F|1992-09-07|1992-10-21|1992-09-24|COLLECT COD|FOB|slyly final accoun| +2561|25|4|1|32|29600.64|0.02|0.01|N|O|1998-01-05|1997-12-28|1998-01-26|DELIVER IN PERSON|REG AIR|bold packages wake slyly. slyly| +2561|98|1|2|5|4990.45|0.07|0.04|N|O|1997-12-27|1998-01-23|1998-01-13|TAKE BACK RETURN|AIR|p ironic, regular pinto beans.| +2561|173|4|3|47|50438.99|0.04|0.02|N|O|1997-11-19|1998-01-21|1997-12-03|DELIVER IN PERSON|REG AIR|larly pending t| +2561|108|9|4|39|39315.90|0.08|0.06|N|O|1998-01-20|1997-12-16|1998-02-05|TAKE BACK RETURN|MAIL|equests are furiously against the| +2561|150|3|5|2|2100.30|0.04|0.08|N|O|1998-03-14|1998-01-21|1998-03-27|DELIVER IN PERSON|TRUCK|s are. silently silent foxes sleep about| +2561|51|6|6|14|13314.70|0.02|0.03|N|O|1998-03-07|1998-02-04|1998-03-21|COLLECT COD|RAIL|ep unusual, ironic accounts| +2562|53|5|1|28|26685.40|0.04|0.03|R|F|1992-10-04|1992-09-24|1992-10-09|COLLECT COD|MAIL|ans haggle special, special packages. | +2562|148|9|2|1|1048.14|0.01|0.06|R|F|1992-10-16|1992-09-18|1992-10-17|NONE|TRUCK| slyly final ideas haggle car| +2562|66|7|3|25|24151.50|0.05|0.03|A|F|1992-11-23|1992-10-08|1992-12-19|DELIVER IN PERSON|REG AIR| accounts-- silent, unusual ideas a| +2562|148|1|4|37|38781.18|0.08|0.03|R|F|1992-10-29|1992-10-06|1992-11-09|COLLECT COD|FOB|. slyly regular ideas according to the fl| +2562|160|8|5|29|30744.64|0.05|0.08|A|F|1992-11-01|1992-09-29|1992-11-13|TAKE BACK RETURN|MAIL|eep against the furiously r| +2562|50|7|6|17|16150.85|0.01|0.06|A|F|1992-10-15|1992-10-08|1992-10-26|DELIVER IN PERSON|TRUCK|lar pinto beans. blithely ev| +2563|65|4|1|10|9650.60|0.07|0.04|A|F|1994-01-26|1993-12-19|1994-01-28|DELIVER IN PERSON|AIR|tealthily abo| +2563|167|4|2|28|29880.48|0.04|0.03|R|F|1994-03-17|1994-02-04|1994-04-13|TAKE BACK RETURN|RAIL|hely regular depe| +2563|119|9|3|39|39745.29|0.07|0.00|R|F|1994-02-10|1993-12-31|1994-02-19|COLLECT COD|FOB|lent requests should integrate; carefully e| +2563|90|1|4|50|49504.50|0.01|0.01|A|F|1994-01-26|1994-01-03|1994-02-09|DELIVER IN PERSON|SHIP|ly regular, regular excuses. bold plate| +2563|15|6|5|42|38430.42|0.06|0.08|R|F|1994-02-21|1994-02-14|1994-03-04|DELIVER IN PERSON|AIR|ymptotes nag furiously slyly even inst| +2563|121|2|6|5|5105.60|0.10|0.00|R|F|1993-12-27|1993-12-19|1994-01-02|DELIVER IN PERSON|REG AIR| the quickly final theodolite| +2564|112|3|1|4|4048.44|0.02|0.00|R|F|1994-11-12|1994-10-29|1994-12-04|NONE|MAIL|y express requests sleep furi| +2565|144|5|1|42|43853.88|0.04|0.08|N|O|1998-04-07|1998-04-02|1998-05-04|NONE|AIR|ngly silent | +2565|189|10|2|26|28318.68|0.05|0.08|N|O|1998-05-07|1998-04-09|1998-05-15|DELIVER IN PERSON|TRUCK| pinto beans about the slyly regula| +2565|115|5|3|34|34513.74|0.06|0.06|N|O|1998-03-19|1998-04-12|1998-04-17|DELIVER IN PERSON|SHIP|nstructions was carefu| +2565|17|7|4|25|22925.25|0.10|0.08|N|O|1998-06-27|1998-05-20|1998-07-13|DELIVER IN PERSON|RAIL|, express accounts. final id| +2565|76|7|5|26|25377.82|0.08|0.03|N|O|1998-03-05|1998-04-11|1998-03-11|TAKE BACK RETURN|AIR|ites wake. ironic acco| +2565|141|4|6|48|49974.72|0.08|0.07|N|O|1998-06-18|1998-05-06|1998-07-13|DELIVER IN PERSON|TRUCK|r instructions sleep qui| +2566|148|5|1|19|19914.66|0.06|0.07|R|F|1992-12-21|1992-11-24|1992-12-22|DELIVER IN PERSON|MAIL|ests. silent| +2566|181|2|2|42|45409.56|0.08|0.02|R|F|1992-12-20|1992-12-22|1992-12-29|COLLECT COD|MAIL|ously ironic accounts| +2566|23|8|3|18|16614.36|0.09|0.02|A|F|1992-11-16|1992-12-24|1992-12-16|COLLECT COD|FOB| braids according t| +2566|42|9|4|3|2826.12|0.05|0.02|A|F|1992-11-04|1992-12-30|1992-12-04|TAKE BACK RETURN|FOB|ckages are ironic Tiresias. furious| +2566|22|3|5|9|8298.18|0.04|0.03|R|F|1992-12-14|1992-12-28|1992-12-16|NONE|FOB|blithely bold accounts? quickl| +2566|128|3|6|1|1028.12|0.07|0.03|A|F|1992-10-28|1992-11-20|1992-11-22|TAKE BACK RETURN|AIR|theodolites wake pending| +2567|26|9|1|39|36114.78|0.03|0.04|N|O|1998-05-10|1998-05-10|1998-05-21|NONE|SHIP|ns. furiously final dependencies cajo| +2567|112|3|2|50|50605.50|0.06|0.05|N|O|1998-05-05|1998-04-18|1998-05-09|DELIVER IN PERSON|TRUCK|. carefully pending foxes are furi| +2567|52|10|3|6|5712.30|0.03|0.06|N|O|1998-04-21|1998-04-14|1998-05-11|NONE|RAIL|s cajole regular, final acco| +2567|158|6|4|50|52907.50|0.05|0.03|N|O|1998-03-27|1998-05-25|1998-04-23|DELIVER IN PERSON|FOB|pinto beans? r| +2567|81|2|5|46|45129.68|0.07|0.02|N|O|1998-06-02|1998-04-30|1998-06-13|COLLECT COD|AIR|efully pending epitaphs. carefully reg| +2567|100|3|6|32|32003.20|0.01|0.07|N|O|1998-05-24|1998-04-30|1998-06-14|NONE|RAIL| the even, iro| +2567|135|6|7|43|44510.59|0.06|0.02|N|O|1998-05-11|1998-04-15|1998-05-29|NONE|RAIL|requests. final courts cajole | +2592|90|1|1|7|6930.63|0.10|0.04|R|F|1993-03-13|1993-04-25|1993-04-01|NONE|REG AIR| carefully special theodolites integrate | +2592|66|1|2|2|1932.12|0.10|0.00|A|F|1993-03-24|1993-04-05|1993-04-16|DELIVER IN PERSON|RAIL|side of the b| +2593|105|2|1|37|37188.70|0.08|0.06|R|F|1993-12-14|1993-10-08|1994-01-04|NONE|SHIP|s wake bravel| +2593|90|1|2|28|27722.52|0.08|0.03|A|F|1993-10-30|1993-10-18|1993-11-06|DELIVER IN PERSON|SHIP|y even escapades shall| +2593|128|3|3|6|6168.72|0.04|0.05|A|F|1993-11-28|1993-10-04|1993-12-28|TAKE BACK RETURN|REG AIR|ular packages. re| +2593|161|10|4|44|46691.04|0.02|0.08|A|F|1993-09-05|1993-10-23|1993-09-29|NONE|RAIL|ents impress furiously; unusual theodoli| +2593|4|5|5|3|2712.00|0.03|0.00|A|F|1993-12-16|1993-11-01|1993-12-29|COLLECT COD|SHIP|the furiously | +2593|175|6|6|1|1075.17|0.08|0.08|A|F|1993-11-23|1993-10-25|1993-12-04|DELIVER IN PERSON|RAIL| accounts wake slyly | +2593|192|5|7|11|12014.09|0.00|0.07|R|F|1993-11-01|1993-11-19|1993-11-28|TAKE BACK RETURN|RAIL|express packages sleep bold re| +2594|72|3|1|7|6804.49|0.06|0.02|R|F|1993-03-26|1993-03-05|1993-04-24|DELIVER IN PERSON|FOB|arls cajole | +2594|124|9|2|13|13313.56|0.10|0.05|R|F|1993-02-06|1993-03-01|1993-02-23|TAKE BACK RETURN|TRUCK|fully special accounts use courts| +2594|126|1|3|24|24626.88|0.03|0.00|A|F|1993-01-31|1993-03-10|1993-02-04|COLLECT COD|REG AIR|lar accounts sleep fur| +2594|144|7|4|46|48030.44|0.00|0.08|R|F|1993-04-17|1993-03-06|1993-04-21|TAKE BACK RETURN|SHIP|beans. instructions across t| +2595|61|2|1|42|40364.52|0.08|0.02|N|O|1996-03-24|1996-01-28|1996-04-10|DELIVER IN PERSON|MAIL|ggle furiou| +2595|88|9|2|30|29642.40|0.05|0.01|N|O|1996-03-05|1996-02-23|1996-03-19|NONE|AIR|ctions. regula| +2595|24|3|3|19|17556.38|0.01|0.05|N|O|1995-12-23|1996-03-02|1996-01-17|COLLECT COD|MAIL|ns are neve| +2595|159|1|4|29|30715.35|0.07|0.05|N|O|1996-01-01|1996-02-13|1996-01-18|TAKE BACK RETURN|RAIL|ronic accounts haggle carefully fin| +2595|86|7|5|30|29582.40|0.09|0.07|N|O|1996-03-16|1996-01-31|1996-04-05|TAKE BACK RETURN|FOB|. final orbits cajole | +2595|82|3|6|31|30444.48|0.06|0.04|N|O|1996-02-07|1996-02-10|1996-03-05|DELIVER IN PERSON|AIR|tipliers w| +2596|170|5|1|6|6421.02|0.05|0.01|N|O|1996-12-15|1996-11-02|1996-12-29|TAKE BACK RETURN|TRUCK|ily special re| +2596|139|10|2|43|44682.59|0.07|0.03|N|O|1996-09-03|1996-10-26|1996-09-15|NONE|FOB|ial packages haggl| +2596|39|5|3|19|17841.57|0.10|0.00|N|O|1996-09-02|1996-11-03|1996-09-06|COLLECT COD|AIR|ias mold! sp| +2596|105|6|4|10|10051.00|0.06|0.05|N|O|1996-08-25|1996-11-05|1996-09-13|DELIVER IN PERSON|REG AIR| instructions shall have| +2597|84|5|1|24|23617.92|0.07|0.00|A|F|1993-05-15|1993-03-06|1993-05-25|TAKE BACK RETURN|FOB|pending packages. enticingly fi| +2598|7|4|1|12|10884.00|0.00|0.01|N|O|1996-06-17|1996-04-12|1996-06-24|COLLECT COD|TRUCK|express packages nag sly| +2598|148|7|2|40|41925.60|0.07|0.02|N|O|1996-05-11|1996-05-19|1996-06-08|TAKE BACK RETURN|AIR|the enticing| +2598|104|9|3|4|4016.40|0.03|0.03|N|O|1996-05-23|1996-05-13|1996-05-25|COLLECT COD|AIR| across the furiously fi| +2598|23|2|4|19|17537.38|0.02|0.00|N|O|1996-04-09|1996-05-30|1996-04-17|TAKE BACK RETURN|RAIL|nic packages. even accounts| +2598|106|3|5|12|12073.20|0.01|0.08|N|O|1996-04-14|1996-04-24|1996-04-21|TAKE BACK RETURN|REG AIR|eposits cajol| +2599|101|4|1|11|11012.10|0.08|0.08|N|O|1997-02-01|1996-12-14|1997-02-27|TAKE BACK RETURN|FOB| express accoun| +2599|42|5|2|26|24493.04|0.03|0.04|N|O|1996-11-08|1996-12-21|1996-11-24|TAKE BACK RETURN|AIR|nag carefully | +2599|99|10|3|29|28973.61|0.09|0.03|N|O|1997-01-10|1996-12-10|1997-02-02|COLLECT COD|RAIL|ly express dolphins. special, | +2624|63|10|1|15|14445.90|0.03|0.07|N|O|1997-02-28|1997-02-19|1997-03-21|DELIVER IN PERSON|AIR|le. quickly pending requests| +2624|189|10|2|12|13070.16|0.07|0.00|N|O|1997-02-24|1997-02-22|1997-02-27|DELIVER IN PERSON|SHIP|er the quickly unu| +2625|20|1|1|42|38640.84|0.02|0.04|R|F|1992-10-18|1992-11-17|1992-10-23|DELIVER IN PERSON|AIR| even accounts haggle furiously| +2626|22|5|1|45|41490.90|0.09|0.04|N|O|1995-11-22|1995-11-01|1995-11-23|NONE|AIR|deposits wake blithely according to | +2626|175|3|2|2|2150.34|0.05|0.07|N|O|1995-10-19|1995-11-09|1995-10-24|TAKE BACK RETURN|FOB|uffy accounts haggle furiously above| +2626|154|2|3|40|42166.00|0.05|0.07|N|O|1995-09-28|1995-12-03|1995-10-10|NONE|REG AIR|eans. ironic deposits haggle. depo| +2627|131|7|1|28|28871.64|0.09|0.02|R|F|1992-05-14|1992-05-09|1992-05-31|COLLECT COD|SHIP|ggedly final excuses nag packages. f| +2628|106|9|1|44|44268.40|0.07|0.03|R|F|1994-01-11|1994-01-14|1994-01-13|DELIVER IN PERSON|SHIP|lyly final, pending ide| +2628|106|9|2|14|14085.40|0.01|0.03|A|F|1994-01-28|1993-11-30|1994-02-20|TAKE BACK RETURN|SHIP|g the furiously unusual pi| +2628|64|9|3|42|40490.52|0.00|0.00|A|F|1993-11-20|1994-01-04|1993-12-19|DELIVER IN PERSON|TRUCK|ld notornis alongside | +2628|95|7|4|23|22887.07|0.08|0.04|A|F|1993-10-27|1994-01-08|1993-11-12|DELIVER IN PERSON|TRUCK|usual packages sleep about the fina| +2628|90|1|5|50|49504.50|0.07|0.01|A|F|1994-01-13|1993-12-11|1994-01-14|NONE|AIR|posits serve carefully toward | +2629|118|9|1|6|6108.66|0.06|0.05|N|O|1998-06-10|1998-05-29|1998-06-13|DELIVER IN PERSON|SHIP|dolites hinder bli| +2629|124|7|2|31|31747.72|0.08|0.03|N|O|1998-05-24|1998-05-26|1998-06-10|COLLECT COD|AIR|ate blithely bold, regular deposits. bold| +2629|128|9|3|29|29815.48|0.08|0.07|N|O|1998-07-09|1998-06-17|1998-07-12|TAKE BACK RETURN|AIR|eposits serve unusual, express i| +2629|70|5|4|33|32012.31|0.06|0.03|N|O|1998-05-29|1998-05-14|1998-05-30|NONE|TRUCK|es. slowly express accounts are along the| +2630|29|8|1|46|42734.92|0.05|0.03|R|F|1992-11-05|1992-12-17|1992-12-05|TAKE BACK RETURN|MAIL|uests cajole. e| +2630|57|2|2|8|7656.40|0.09|0.07|A|F|1992-11-16|1993-01-01|1992-12-07|DELIVER IN PERSON|TRUCK|indle fluffily silent, ironic pi| +2630|173|2|3|45|48292.65|0.08|0.07|A|F|1993-01-04|1993-01-11|1993-01-09|NONE|FOB|edly express ideas. carefully final | +2630|162|9|4|29|30802.64|0.08|0.07|A|F|1992-12-03|1993-01-04|1992-12-12|DELIVER IN PERSON|SHIP|efully unusual dependencies. even i| +2631|122|7|1|42|42929.04|0.00|0.03|A|F|1994-01-04|1993-12-01|1994-01-16|TAKE BACK RETURN|SHIP|ect carefully at the furiously final the| +2631|67|4|2|4|3868.24|0.07|0.06|R|F|1993-11-03|1993-12-17|1993-11-05|COLLECT COD|AIR|special theodolites. a| +2631|118|8|3|15|15271.65|0.06|0.05|A|F|1993-09-30|1993-11-06|1993-10-13|DELIVER IN PERSON|SHIP|y. furiously even pinto be| +2656|181|2|1|10|10811.80|0.02|0.06|R|F|1993-06-28|1993-07-04|1993-07-12|TAKE BACK RETURN|TRUCK|s nag regularly about the deposits. slyly| +2656|137|8|2|38|39410.94|0.07|0.02|A|F|1993-06-25|1993-06-04|1993-07-24|NONE|RAIL|structions wake along the furio| +2656|2|5|3|19|17138.00|0.03|0.02|R|F|1993-08-03|1993-07-25|1993-08-20|TAKE BACK RETURN|MAIL|ts serve deposi| +2656|110|3|4|40|40404.40|0.05|0.04|R|F|1993-06-09|1993-07-24|1993-06-21|DELIVER IN PERSON|RAIL|refully final pearls. final ideas wake. qu| +2657|115|9|1|22|22332.42|0.02|0.03|N|O|1995-12-08|1995-12-28|1995-12-21|TAKE BACK RETURN|MAIL|r ideas. furiously special dolphins| +2657|165|2|2|15|15977.40|0.08|0.05|N|O|1995-12-09|1995-12-16|1995-12-18|NONE|RAIL|ole carefully above the ironic ideas. b| +2657|79|9|3|25|24476.75|0.02|0.04|N|O|1995-10-21|1995-12-12|1995-11-09|COLLECT COD|FOB|lly pinto beans. final | +2657|55|7|4|11|10505.55|0.04|0.08|N|O|1995-11-19|1995-12-11|1995-11-24|COLLECT COD|TRUCK|ckly enticing requests. fur| +2657|78|9|5|42|41078.94|0.06|0.03|N|O|1996-01-23|1995-11-22|1996-01-25|COLLECT COD|RAIL|ckly slyly even accounts. platelets x-ray| +2657|194|7|6|31|33919.89|0.01|0.03|N|O|1995-11-10|1995-11-27|1995-12-06|COLLECT COD|RAIL|re blithely | +2658|132|3|1|41|42317.33|0.05|0.04|N|O|1995-11-07|1995-11-04|1995-12-04|NONE|MAIL|eposits. furiously final theodolite| +2658|29|4|2|22|20438.44|0.08|0.05|N|O|1995-11-12|1995-11-18|1995-11-14|DELIVER IN PERSON|TRUCK|ts cajole. pending packages affix| +2658|18|5|3|13|11934.13|0.07|0.06|N|O|1995-10-24|1995-12-12|1995-11-14|COLLECT COD|FOB|s kindle blithely regular accounts.| +2658|92|5|4|22|21825.98|0.04|0.04|N|O|1995-12-02|1995-11-03|1995-12-26|DELIVER IN PERSON|SHIP| dependencies. blithely pending foxes abou| +2658|7|8|5|45|40815.00|0.03|0.01|N|O|1995-11-02|1995-11-08|1995-11-29|DELIVER IN PERSON|MAIL|e special requests. quickly ex| +2658|147|4|6|27|28272.78|0.05|0.07|N|O|1995-09-26|1995-12-08|1995-09-30|NONE|AIR|ecial packages use abov| +2659|42|1|1|28|26377.12|0.08|0.05|A|F|1994-03-17|1994-01-24|1994-03-19|NONE|FOB|idle tithes| +2659|43|2|2|21|19803.84|0.00|0.00|A|F|1993-12-23|1994-02-10|1994-01-17|DELIVER IN PERSON|RAIL|y beyond the furiously even co| +2659|135|1|3|24|24843.12|0.04|0.03|R|F|1994-03-28|1994-02-20|1994-04-05|DELIVER IN PERSON|REG AIR| haggle carefully | +2659|119|6|4|2|2038.22|0.00|0.08|R|F|1994-02-19|1994-03-12|1994-02-21|NONE|MAIL|sts above the fluffily express fo| +2659|7|4|5|9|8163.00|0.08|0.03|A|F|1994-02-07|1994-03-17|1994-03-04|DELIVER IN PERSON|AIR|ly final packages sleep ac| +2660|48|7|1|17|16116.68|0.00|0.05|N|O|1995-08-18|1995-09-13|1995-09-17|NONE|SHIP|al pinto beans wake after the furious| +2661|178|9|1|31|33423.27|0.03|0.02|N|O|1997-04-07|1997-03-10|1997-04-23|TAKE BACK RETURN|AIR|e ironicall| +2661|103|8|2|22|22068.20|0.08|0.02|N|O|1997-03-14|1997-03-17|1997-04-08|COLLECT COD|REG AIR| foxes affix quickly ironic request| +2661|67|6|3|11|10637.66|0.00|0.08|N|O|1997-04-14|1997-02-11|1997-05-05|TAKE BACK RETURN|FOB|equests are a| +2661|137|8|4|41|42522.33|0.06|0.02|N|O|1997-03-06|1997-03-27|1997-03-15|DELIVER IN PERSON|AIR|iously ironically ironic requests. | +2662|102|5|1|43|43090.30|0.09|0.07|N|O|1996-11-24|1996-11-04|1996-12-08|NONE|RAIL|. slyly specia| +2662|128|9|2|8|8224.96|0.02|0.07|N|O|1996-09-10|1996-10-09|1996-09-21|TAKE BACK RETURN|REG AIR|ajole carefully. sp| +2662|2|5|3|6|5412.00|0.02|0.00|N|O|1996-11-30|1996-09-20|1996-12-03|DELIVER IN PERSON|REG AIR|olites cajole quickly along the b| +2662|30|1|4|34|31621.02|0.06|0.07|N|O|1996-10-04|1996-11-05|1996-10-19|NONE|SHIP|ding theodolites use carefully. p| +2663|114|4|1|35|35493.85|0.02|0.01|N|O|1995-12-11|1995-10-16|1996-01-07|TAKE BACK RETURN|REG AIR|tect. slyly fina| +2688|18|5|1|45|41310.45|0.08|0.08|R|F|1992-05-21|1992-04-14|1992-05-28|NONE|FOB|sits run carefully| +2688|15|6|2|46|42090.46|0.01|0.01|R|F|1992-05-24|1992-04-01|1992-05-26|COLLECT COD|TRUCK|elets. regular reque| +2688|89|10|3|30|29672.40|0.05|0.04|A|F|1992-04-18|1992-03-18|1992-05-18|TAKE BACK RETURN|RAIL|ithely final | +2688|25|10|4|3|2775.06|0.00|0.03|R|F|1992-02-04|1992-03-18|1992-02-24|DELIVER IN PERSON|RAIL|e fluffily | +2688|59|10|5|22|21099.10|0.02|0.05|R|F|1992-02-09|1992-04-09|1992-02-11|DELIVER IN PERSON|RAIL|press, ironic excuses wake carefully id| +2688|149|10|6|42|44063.88|0.01|0.01|R|F|1992-04-29|1992-04-04|1992-05-17|TAKE BACK RETURN|FOB|lly even account| +2689|6|1|1|45|40770.00|0.02|0.04|R|F|1992-04-29|1992-06-22|1992-04-30|COLLECT COD|SHIP|e quickly. carefully silent| +2690|140|1|1|44|45766.16|0.05|0.06|N|O|1996-05-30|1996-05-19|1996-06-26|NONE|REG AIR|ly alongside of th| +2690|51|2|2|50|47552.50|0.03|0.03|N|O|1996-06-13|1996-05-22|1996-06-14|DELIVER IN PERSON|MAIL| doubt careful| +2690|125|6|3|45|46130.40|0.02|0.07|N|O|1996-05-23|1996-06-02|1996-05-29|DELIVER IN PERSON|MAIL|ounts. slyly regular dependencies wa| +2690|195|6|4|12|13142.28|0.04|0.07|N|O|1996-07-18|1996-06-03|1996-07-25|NONE|AIR|nal, regular atta| +2690|86|7|5|30|29582.40|0.01|0.08|N|O|1996-05-20|1996-06-01|1996-06-04|TAKE BACK RETURN|SHIP|d accounts above the express req| +2690|189|10|6|3|3267.54|0.07|0.01|N|O|1996-07-04|1996-05-28|1996-07-06|TAKE BACK RETURN|RAIL|. final reques| +2690|79|7|7|35|34267.45|0.05|0.06|N|O|1996-07-25|1996-05-14|1996-08-03|COLLECT COD|FOB|y silent pinto be| +2691|91|3|1|11|10901.99|0.04|0.07|R|F|1992-06-21|1992-06-08|1992-07-09|COLLECT COD|FOB|leep alongside of the accounts. slyly ironi| +2691|48|7|2|2|1896.08|0.00|0.07|R|F|1992-05-10|1992-06-04|1992-05-11|TAKE BACK RETURN|TRUCK|s cajole at the blithely ironic warthog| +2691|162|3|3|16|16994.56|0.09|0.03|R|F|1992-06-11|1992-07-29|1992-06-29|NONE|RAIL|bove the even foxes. unusual theodoli| +2691|166|3|4|1|1066.16|0.08|0.00|A|F|1992-08-11|1992-06-07|1992-08-16|NONE|SHIP|egular instructions b| +2692|17|1|1|3|2751.03|0.10|0.04|N|O|1998-02-25|1998-01-29|1998-03-27|TAKE BACK RETURN|MAIL|equests. bold, even foxes haggle slyl| +2692|114|1|2|21|21296.31|0.03|0.05|N|O|1998-03-11|1998-02-11|1998-03-19|NONE|SHIP|posits. final, express requests nag furi| +2693|9|10|1|26|23634.00|0.04|0.00|N|O|1996-09-14|1996-10-07|1996-10-03|COLLECT COD|MAIL|cajole alo| +2693|102|3|2|43|43090.30|0.03|0.04|N|O|1996-10-24|1996-10-24|1996-11-03|TAKE BACK RETURN|TRUCK|as are according to th| +2694|153|1|1|30|31594.50|0.02|0.06|N|O|1996-06-20|1996-06-01|1996-07-15|NONE|TRUCK|oxes. never iro| +2694|157|2|2|35|37000.25|0.07|0.03|N|O|1996-05-24|1996-06-01|1996-05-25|NONE|RAIL|atelets past the furiously final deposits | +2694|19|3|3|15|13785.15|0.08|0.02|N|O|1996-06-30|1996-05-01|1996-07-25|TAKE BACK RETURN|REG AIR|e blithely even platelets. special wa| +2694|20|10|4|12|11040.24|0.00|0.05|N|O|1996-04-24|1996-04-22|1996-05-14|DELIVER IN PERSON|RAIL|foxes atop the hockey pla| +2694|108|9|5|10|10081.00|0.08|0.08|N|O|1996-06-23|1996-05-28|1996-06-27|COLLECT COD|REG AIR|fluffily fluffy accounts. even packages hi| +2695|184|5|1|21|22767.78|0.07|0.00|N|O|1996-10-04|1996-11-02|1996-10-21|NONE|MAIL|y regular pinto beans. evenly regular packa| +2695|19|9|2|44|40436.44|0.09|0.07|N|O|1996-10-05|1996-10-10|1996-11-01|NONE|MAIL|ts. busy platelets boost| +2695|144|7|3|21|21926.94|0.02|0.07|N|O|1996-09-13|1996-09-25|1996-10-13|NONE|TRUCK|s. furiously ironic platelets ar| +2695|58|6|4|16|15328.80|0.08|0.08|N|O|1996-11-16|1996-10-05|1996-11-22|NONE|TRUCK|its. theodolites sleep slyly| +2695|86|7|5|40|39443.20|0.02|0.03|N|O|1996-11-02|1996-10-26|1996-11-14|NONE|FOB|ructions. pending| +2720|45|6|1|5|4725.20|0.10|0.06|A|F|1993-06-24|1993-08-08|1993-07-08|NONE|FOB|ously ironic foxes thrash| +2720|17|8|2|42|38514.42|0.09|0.03|R|F|1993-07-25|1993-07-23|1993-08-23|COLLECT COD|REG AIR|fter the inst| +2720|120|1|3|50|51006.00|0.10|0.02|A|F|1993-08-10|1993-07-29|1993-09-06|NONE|SHIP|l requests. deposits nag furiously| +2720|109|2|4|49|49445.90|0.06|0.02|A|F|1993-07-09|1993-07-14|1993-07-13|NONE|REG AIR| accounts. fluffily bold pack| +2720|121|6|5|27|27570.24|0.04|0.00|R|F|1993-06-29|1993-08-06|1993-07-28|NONE|TRUCK|eas. carefully regular | +2721|183|4|1|49|53075.82|0.00|0.08|N|O|1996-02-14|1996-04-26|1996-03-02|DELIVER IN PERSON|AIR|ounts poach carefu| +2721|3|4|2|2|1806.00|0.02|0.05|N|O|1996-02-13|1996-03-14|1996-02-28|TAKE BACK RETURN|TRUCK| slyly final requests against | +2722|124|7|1|21|21506.52|0.09|0.01|A|F|1994-07-29|1994-06-26|1994-08-09|NONE|RAIL|e carefully around the furiously ironic pac| +2722|146|7|2|15|15692.10|0.05|0.03|R|F|1994-07-02|1994-06-01|1994-07-13|COLLECT COD|AIR|refully final asympt| +2722|34|10|3|16|14944.48|0.04|0.06|R|F|1994-05-25|1994-06-09|1994-05-26|NONE|MAIL|ts besides the fluffy,| +2723|13|7|1|47|42911.47|0.09|0.07|N|O|1995-12-05|1995-11-19|1995-12-11|TAKE BACK RETURN|AIR|furiously r| +2723|32|3|2|10|9320.30|0.06|0.08|N|O|1995-11-27|1995-11-29|1995-12-12|DELIVER IN PERSON|MAIL|al, special r| +2723|162|1|3|2|2124.32|0.10|0.01|N|O|1995-11-09|1995-11-10|1995-11-14|TAKE BACK RETURN|FOB| courts boost quickly about th| +2723|82|3|4|12|11784.96|0.01|0.05|N|O|1995-12-24|1995-11-15|1996-01-17|DELIVER IN PERSON|RAIL|bold foxes are bold packages. regular, fin| +2723|129|10|5|40|41164.80|0.09|0.05|N|O|1995-11-17|1995-11-22|1995-11-18|TAKE BACK RETURN|MAIL|unwind fluffily carefully regular realms.| +2724|92|4|1|47|46628.23|0.09|0.01|A|F|1994-11-23|1994-11-13|1994-12-03|COLLECT COD|TRUCK|unusual patterns nag. special p| +2724|147|8|2|21|21989.94|0.09|0.02|A|F|1994-11-25|1994-10-15|1994-12-07|COLLECT COD|RAIL|as. carefully regular dependencies wak| +2724|50|3|3|22|20901.10|0.04|0.06|A|F|1994-09-19|1994-11-18|1994-10-17|TAKE BACK RETURN|TRUCK|express fo| +2724|35|6|4|1|935.03|0.07|0.03|A|F|1994-12-26|1994-11-27|1995-01-07|NONE|MAIL|lyly carefully blithe theodolites-- pl| +2724|149|2|5|29|30425.06|0.05|0.06|A|F|1995-01-10|1994-11-17|1995-02-04|COLLECT COD|MAIL|l requests hagg| +2725|118|2|1|23|23416.53|0.10|0.08|R|F|1994-08-25|1994-06-22|1994-08-28|TAKE BACK RETURN|REG AIR|y regular deposits. brave foxes | +2725|5|8|2|41|37105.00|0.01|0.00|R|F|1994-07-05|1994-06-29|1994-08-02|DELIVER IN PERSON|TRUCK|ns sleep furiously c| +2725|189|10|3|15|16337.70|0.07|0.03|R|F|1994-08-06|1994-08-09|1994-08-15|TAKE BACK RETURN|AIR|? furiously regular a| +2726|1|6|1|50|45050.00|0.00|0.06|R|F|1993-03-04|1993-01-29|1993-03-28|COLLECT COD|TRUCK| furiously bold theodolites| +2727|151|6|1|3|3153.45|0.03|0.01|N|O|1998-06-18|1998-06-06|1998-06-23|NONE|RAIL| the carefully regular foxes u| +2752|31|2|1|41|38172.23|0.02|0.05|A|F|1994-03-02|1994-01-31|1994-03-06|DELIVER IN PERSON|AIR|tructions hag| +2752|7|2|2|29|26303.00|0.02|0.04|R|F|1994-01-22|1994-01-08|1994-01-28|COLLECT COD|TRUCK|gly blithely re| +2752|56|7|3|4|3824.20|0.08|0.00|A|F|1993-12-14|1994-02-13|1994-01-05|DELIVER IN PERSON|TRUCK|telets haggle. regular, final | +2752|24|7|4|40|36960.80|0.09|0.06|A|F|1994-01-24|1994-01-18|1994-02-22|DELIVER IN PERSON|MAIL|into beans are after the sly| +2752|126|5|5|22|22574.64|0.03|0.04|A|F|1994-03-20|1994-02-08|1994-04-01|TAKE BACK RETURN|TRUCK|equests nag. regular dependencies are furio| +2752|170|5|6|21|22473.57|0.09|0.05|R|F|1994-01-01|1994-01-24|1994-01-24|COLLECT COD|SHIP| along the quickly | +2752|199|10|7|38|41769.22|0.08|0.00|R|F|1994-02-23|1993-12-23|1994-03-24|DELIVER IN PERSON|SHIP|es boost. slyly silent ideas| +2753|13|3|1|6|5478.06|0.10|0.04|A|F|1993-12-30|1994-01-28|1994-01-29|COLLECT COD|TRUCK|s accounts| +2753|48|7|2|40|37921.60|0.03|0.05|A|F|1994-01-06|1994-02-13|1994-02-03|DELIVER IN PERSON|SHIP|latelets kindle slyly final depos| +2753|89|10|3|30|29672.40|0.00|0.07|A|F|1994-01-26|1994-01-29|1994-02-02|NONE|RAIL|ans wake fluffily blithely iro| +2753|31|7|4|7|6517.21|0.07|0.03|R|F|1994-02-11|1994-01-22|1994-03-10|DELIVER IN PERSON|AIR|xpress ideas detect b| +2753|137|8|5|36|37336.68|0.04|0.08|R|F|1994-03-15|1994-01-03|1994-04-03|DELIVER IN PERSON|SHIP|gle slyly final c| +2753|50|1|6|17|16150.85|0.01|0.08|A|F|1994-03-08|1994-01-17|1994-03-11|TAKE BACK RETURN|REG AIR| carefully bold deposits sublate s| +2753|148|9|7|20|20962.80|0.01|0.06|R|F|1994-02-24|1994-02-04|1994-03-23|DELIVER IN PERSON|FOB| express pack| +2754|149|6|1|4|4196.56|0.05|0.08|A|F|1994-07-13|1994-05-15|1994-08-02|NONE|REG AIR|blithely silent requests. regular depo| +2754|177|5|2|19|20466.23|0.01|0.07|A|F|1994-06-27|1994-05-06|1994-06-28|NONE|FOB|latelets hag| +2755|92|4|1|19|18849.71|0.10|0.00|R|F|1992-02-11|1992-03-15|1992-02-14|TAKE BACK RETURN|MAIL|furiously special deposits| +2755|24|3|2|11|10164.22|0.03|0.08|A|F|1992-04-12|1992-05-07|1992-04-21|COLLECT COD|RAIL|egular excuses sleep carefully.| +2755|64|3|3|21|20245.26|0.08|0.04|R|F|1992-02-13|1992-04-20|1992-03-02|NONE|AIR|furious re| +2755|131|7|4|5|5155.65|0.01|0.00|A|F|1992-02-27|1992-04-07|1992-03-09|TAKE BACK RETURN|AIR|e the furi| +2755|116|7|5|48|48773.28|0.05|0.06|R|F|1992-03-22|1992-03-10|1992-04-14|DELIVER IN PERSON|MAIL|yly even epitaphs for the | +2756|118|9|1|35|35633.85|0.03|0.02|R|F|1994-06-08|1994-06-01|1994-06-21|TAKE BACK RETURN|AIR| deposits grow bold sheaves; iro| +2756|80|9|2|47|46063.76|0.06|0.01|R|F|1994-05-10|1994-05-25|1994-05-13|NONE|AIR|e final, f| +2756|105|8|3|31|31158.10|0.01|0.07|A|F|1994-07-27|1994-07-06|1994-08-22|TAKE BACK RETURN|TRUCK|en instructions use quickly.| +2756|72|2|4|30|29162.10|0.00|0.04|A|F|1994-06-05|1994-06-30|1994-06-14|DELIVER IN PERSON|TRUCK|ular packages. regular deposi| +2757|148|5|1|26|27251.64|0.07|0.00|N|O|1995-08-19|1995-10-02|1995-09-06|DELIVER IN PERSON|MAIL|around the blithely| +2757|22|7|2|12|11064.24|0.07|0.08|N|O|1995-08-01|1995-09-04|1995-08-08|TAKE BACK RETURN|SHIP| regular, eve| +2757|73|3|3|17|16542.19|0.10|0.04|N|O|1995-09-06|1995-09-27|1995-09-22|DELIVER IN PERSON|AIR|er the furiously silent | +2757|140|1|4|25|26003.50|0.08|0.01|N|O|1995-11-09|1995-09-12|1995-11-23|NONE|AIR|uickly regular | +2757|70|7|5|14|13580.98|0.04|0.05|N|O|1995-09-01|1995-08-24|1995-09-03|TAKE BACK RETURN|SHIP|special deposits u| +2758|121|10|1|20|20422.40|0.02|0.04|N|O|1998-07-27|1998-09-10|1998-08-21|TAKE BACK RETURN|AIR|ptotes sleep furiously| +2758|23|8|2|17|15691.34|0.10|0.06|N|O|1998-09-25|1998-10-03|1998-10-25|NONE|MAIL| accounts! qui| +2758|26|5|3|1|926.02|0.06|0.02|N|O|1998-10-09|1998-09-15|1998-10-16|NONE|TRUCK|ake furious| +2759|59|1|1|10|9590.50|0.10|0.03|R|F|1993-12-14|1994-01-08|1994-01-01|COLLECT COD|FOB|s. busily ironic theodo| +2759|113|10|2|37|37485.07|0.00|0.06|R|F|1994-03-05|1994-02-22|1994-03-18|DELIVER IN PERSON|REG AIR|lar Tiresias affix ironically carefully sp| +2759|112|9|3|11|11133.21|0.03|0.08|A|F|1994-01-24|1994-01-16|1994-02-21|DELIVER IN PERSON|TRUCK|hely regular | +2759|23|2|4|31|28613.62|0.02|0.05|A|F|1994-01-11|1994-01-15|1994-01-23|NONE|SHIP|ithely aft| +2784|33|4|1|45|41986.35|0.03|0.01|N|O|1998-02-15|1998-04-07|1998-02-26|COLLECT COD|AIR|yly along the asymptotes. reque| +2784|54|5|2|23|21943.15|0.03|0.05|N|O|1998-03-28|1998-02-07|1998-04-17|DELIVER IN PERSON|AIR|uests lose after | +2784|175|4|3|40|43006.80|0.07|0.01|N|O|1998-04-28|1998-03-19|1998-05-03|DELIVER IN PERSON|TRUCK|deas nag furiously never unusual | +2784|29|10|4|3|2787.06|0.04|0.03|N|O|1998-01-19|1998-04-05|1998-02-05|TAKE BACK RETURN|AIR|n packages. foxes haggle quickly sile| +2785|100|3|1|34|34003.40|0.08|0.06|N|O|1995-08-07|1995-09-09|1995-09-05|NONE|RAIL|ly final packages haggl| +2785|110|7|2|37|37374.07|0.08|0.04|N|O|1995-07-25|1995-09-12|1995-08-06|DELIVER IN PERSON|TRUCK|tructions. furiously | +2785|65|10|3|33|31846.98|0.08|0.06|N|O|1995-10-16|1995-08-24|1995-11-02|DELIVER IN PERSON|MAIL|fter the furiously final p| +2785|48|1|4|34|32233.36|0.00|0.02|N|O|1995-09-16|1995-09-09|1995-10-11|COLLECT COD|SHIP|kages wake carefully silent | +2786|136|2|1|15|15541.95|0.03|0.04|A|F|1992-05-19|1992-05-08|1992-05-28|COLLECT COD|TRUCK|low deposits are ironic| +2786|51|3|2|42|39944.10|0.10|0.04|R|F|1992-05-15|1992-04-22|1992-05-30|DELIVER IN PERSON|AIR|unts are against the furious| +2786|156|1|3|41|43302.15|0.04|0.05|R|F|1992-07-01|1992-06-04|1992-07-13|COLLECT COD|RAIL|ix requests. bold requests a| +2786|23|4|4|24|22152.48|0.05|0.02|A|F|1992-04-04|1992-06-09|1992-05-02|DELIVER IN PERSON|MAIL|ans. slyly unusual platelets detect. unus| +2786|50|3|5|43|40852.15|0.06|0.03|R|F|1992-04-22|1992-05-13|1992-04-29|NONE|RAIL|ons. theodolites after| +2786|162|1|6|21|22305.36|0.08|0.00|A|F|1992-05-03|1992-05-01|1992-05-14|COLLECT COD|AIR|slow instructi| +2787|33|9|1|4|3732.12|0.04|0.04|N|O|1996-01-26|1995-11-26|1996-02-20|TAKE BACK RETURN|SHIP|ts. instructions nag furiously according | +2788|177|8|1|16|17234.72|0.06|0.06|A|F|1994-10-04|1994-11-25|1994-10-18|DELIVER IN PERSON|AIR| requests wake carefully. carefully si| +2789|163|8|1|16|17010.56|0.03|0.02|N|O|1998-04-18|1998-05-25|1998-05-12|DELIVER IN PERSON|REG AIR|o beans use carefully| +2789|23|4|2|41|37843.82|0.02|0.05|N|O|1998-03-20|1998-05-15|1998-03-21|COLLECT COD|MAIL|d packages-- fluffily specia| +2789|176|5|3|33|35513.61|0.06|0.02|N|O|1998-04-21|1998-05-02|1998-04-30|COLLECT COD|TRUCK|deposits. ironic | +2789|16|3|4|47|43052.47|0.02|0.04|N|O|1998-03-29|1998-05-05|1998-04-07|NONE|RAIL|usly busy packages wake against the unusual| +2789|197|1|5|23|25235.37|0.02|0.07|N|O|1998-03-25|1998-05-10|1998-04-24|COLLECT COD|RAIL|cording to the careful de| +2789|144|5|6|16|16706.24|0.07|0.03|N|O|1998-05-11|1998-05-08|1998-05-24|TAKE BACK RETURN|RAIL|d the carefully iron| +2789|133|4|7|42|43391.46|0.01|0.00|N|O|1998-04-28|1998-05-17|1998-05-24|TAKE BACK RETURN|AIR|ending packages shoul| +2790|185|6|1|27|29299.86|0.06|0.08|R|F|1994-09-04|1994-09-27|1994-09-16|TAKE BACK RETURN|MAIL|ilent packages cajole. quickly ironic requ| +2790|117|1|2|50|50855.50|0.00|0.06|A|F|1994-12-08|1994-11-17|1994-12-19|NONE|RAIL|fter the regular ideas. f| +2790|184|5|3|19|20599.42|0.06|0.00|R|F|1994-10-23|1994-10-03|1994-10-26|TAKE BACK RETURN|RAIL|uffily even excuses. furiously thin| +2790|197|8|4|24|26332.56|0.07|0.01|A|F|1994-12-04|1994-10-10|1994-12-25|NONE|MAIL|ments. slyly f| +2790|148|9|5|11|11529.54|0.08|0.03|A|F|1994-09-28|1994-11-14|1994-10-04|TAKE BACK RETURN|AIR|lar requests poach slyly foxes| +2790|73|3|6|13|12649.91|0.08|0.00|R|F|1994-09-20|1994-10-10|1994-10-20|COLLECT COD|SHIP|n deposits according to the regul| +2790|4|1|7|32|28928.00|0.08|0.02|A|F|1994-09-25|1994-10-26|1994-10-01|NONE|SHIP|ully pending| +2791|59|10|1|49|46993.45|0.10|0.04|A|F|1995-01-11|1994-11-10|1995-02-08|COLLECT COD|MAIL| accounts sleep at the bold, regular pinto | +2791|63|4|2|4|3852.24|0.10|0.08|A|F|1995-01-02|1994-12-28|1995-01-29|NONE|SHIP|slyly bold packages boost. slyly| +2791|133|9|3|44|45457.72|0.08|0.06|R|F|1994-11-17|1994-11-12|1994-12-14|NONE|FOB|heodolites use furio| +2791|156|8|4|24|25347.60|0.04|0.02|R|F|1995-01-30|1994-11-20|1995-02-08|DELIVER IN PERSON|TRUCK|ilent forges. quickly special pinto beans | +2791|105|2|5|8|8040.80|0.02|0.04|R|F|1995-01-30|1994-11-24|1995-02-13|NONE|FOB|se. close ideas alongs| +2791|75|3|6|9|8775.63|0.08|0.02|R|F|1994-11-19|1994-12-14|1994-12-10|TAKE BACK RETURN|AIR|pendencies. blithely bold patterns acr| +2791|29|2|7|26|24154.52|0.06|0.03|R|F|1995-02-06|1994-12-07|1995-02-23|DELIVER IN PERSON|AIR|uriously special instructio| +2816|59|10|1|33|31648.65|0.00|0.07|R|F|1994-10-19|1994-11-10|1994-11-09|NONE|REG AIR|s; slyly even theodo| +2816|142|3|2|4|4168.56|0.05|0.04|R|F|1994-12-11|1994-12-07|1995-01-03|NONE|FOB|. blithely pending id| +2816|121|6|3|4|4084.48|0.02|0.06|R|F|1994-12-12|1994-12-05|1994-12-30|NONE|RAIL| requests print above the final deposits| +2817|60|8|1|25|24001.50|0.07|0.01|R|F|1994-04-21|1994-06-20|1994-05-07|DELIVER IN PERSON|FOB|doze blithely.| +2817|32|8|2|5|4660.15|0.03|0.04|A|F|1994-05-07|1994-05-31|1994-05-12|TAKE BACK RETURN|AIR|furiously unusual theodolites use furiou| +2817|172|10|3|35|37525.95|0.01|0.07|A|F|1994-05-20|1994-06-03|1994-05-22|COLLECT COD|FOB|gular foxes| +2817|161|2|4|4|4244.64|0.00|0.05|R|F|1994-06-04|1994-06-11|1994-06-10|NONE|TRUCK|n accounts wake across the fluf| +2818|121|4|1|12|12253.44|0.10|0.03|A|F|1995-02-01|1995-03-10|1995-02-16|NONE|AIR|lms. quickly bold asymp| +2818|199|2|2|22|24182.18|0.06|0.07|R|F|1995-02-28|1995-03-10|1995-03-06|TAKE BACK RETURN|RAIL|egrate toward the carefully iron| +2818|45|6|3|11|10395.44|0.01|0.06|R|F|1995-02-18|1995-02-11|1995-03-19|TAKE BACK RETURN|TRUCK|ggle across the carefully blithe| +2818|40|6|4|32|30081.28|0.08|0.08|R|F|1995-02-04|1995-03-05|1995-02-18|COLLECT COD|REG AIR|arefully! ac| +2818|18|8|5|42|38556.42|0.08|0.04|A|F|1995-02-12|1995-02-19|1995-03-13|COLLECT COD|MAIL|ar accounts wake carefully a| +2818|91|5|6|7|6937.63|0.06|0.03|R|F|1995-03-24|1995-03-09|1995-04-06|TAKE BACK RETURN|TRUCK|ly according to the r| +2819|70|1|1|17|16491.19|0.08|0.08|A|F|1994-07-16|1994-07-15|1994-07-17|TAKE BACK RETURN|RAIL|en deposits above the f| +2819|67|2|2|12|11604.72|0.03|0.08|R|F|1994-07-18|1994-06-24|1994-07-28|NONE|MAIL| regular, regular a| +2819|5|2|3|28|25340.00|0.03|0.08|R|F|1994-05-09|1994-07-02|1994-05-15|NONE|RAIL|ckages sublate carefully closely regular | +2819|153|4|4|5|5265.75|0.00|0.02|R|F|1994-05-29|1994-06-12|1994-06-28|NONE|TRUCK| fluffily unusual foxes sleep caref| +2819|200|3|5|6|6601.20|0.03|0.01|A|F|1994-07-22|1994-08-02|1994-07-29|NONE|REG AIR|eas after the carefully express pack| +2820|174|2|1|23|24705.91|0.04|0.08|R|F|1994-07-10|1994-08-08|1994-07-21|NONE|MAIL| was furiously. deposits among the ironic| +2820|126|9|2|33|33861.96|0.08|0.06|A|F|1994-07-07|1994-08-17|1994-08-02|DELIVER IN PERSON|AIR|carefully even pinto beans. | +2820|141|10|3|38|39563.32|0.03|0.08|A|F|1994-09-10|1994-08-07|1994-10-07|TAKE BACK RETURN|MAIL|ests despite the carefully unusual a| +2820|197|9|4|40|43887.60|0.06|0.06|A|F|1994-08-08|1994-07-30|1994-08-21|TAKE BACK RETURN|REG AIR|g multipliers. final c| +2821|181|2|1|4|4324.72|0.00|0.00|A|F|1993-09-15|1993-10-02|1993-09-17|TAKE BACK RETURN|TRUCK|nding foxes.| +2821|72|1|2|4|3888.28|0.09|0.00|A|F|1993-11-19|1993-09-20|1993-11-27|TAKE BACK RETURN|TRUCK|ual multipliers. final deposits cajol| +2821|164|1|3|27|28732.32|0.01|0.01|A|F|1993-11-27|1993-10-11|1993-12-08|COLLECT COD|TRUCK|requests. blit| +2822|151|9|1|39|40994.85|0.04|0.02|R|F|1993-09-11|1993-08-29|1993-09-18|NONE|MAIL|kly about the sly| +2823|86|7|1|45|44373.60|0.03|0.04|N|O|1995-12-28|1995-11-27|1996-01-02|DELIVER IN PERSON|SHIP|furiously special idea| +2823|160|5|2|18|19082.88|0.00|0.03|N|O|1995-11-11|1995-10-30|1995-12-08|TAKE BACK RETURN|TRUCK| final deposits. furiously regular foxes u| +2823|186|7|3|11|11947.98|0.07|0.02|N|O|1995-12-10|1995-11-24|1995-12-21|DELIVER IN PERSON|SHIP|bold requests nag blithely s| +2823|139|10|4|48|49878.24|0.09|0.03|N|O|1995-11-21|1995-10-30|1995-11-27|NONE|SHIP|ously busily slow excus| +2823|99|2|5|18|17983.62|0.04|0.06|N|O|1995-11-09|1995-10-30|1995-11-19|NONE|AIR|eas. decoys cajole deposi| +2823|123|2|6|20|20462.40|0.07|0.00|N|O|1995-11-13|1995-12-06|1995-12-07|NONE|MAIL|its sleep between the unusual, ironic pac| +2823|86|7|7|12|11832.96|0.02|0.04|N|O|1995-12-22|1995-11-20|1996-01-13|NONE|REG AIR|the slyly ironic dolphins; fin| +2848|65|4|1|44|42462.64|0.01|0.05|R|F|1992-04-14|1992-05-09|1992-04-19|DELIVER IN PERSON|MAIL|ions. slyly express instructions n| +2848|165|6|2|8|8521.28|0.07|0.01|A|F|1992-03-21|1992-05-18|1992-04-07|DELIVER IN PERSON|TRUCK|. silent, final ideas sublate packages. ir| +2848|138|4|3|8|8305.04|0.07|0.08|A|F|1992-06-20|1992-04-12|1992-07-09|NONE|SHIP|sly regular foxes. | +2848|125|6|4|34|34854.08|0.02|0.08|A|F|1992-03-15|1992-04-24|1992-04-12|TAKE BACK RETURN|RAIL|ts along the blithely regu| +2848|195|7|5|18|19713.42|0.07|0.03|R|F|1992-04-10|1992-06-01|1992-05-05|DELIVER IN PERSON|TRUCK|osits haggle. stealthily ironic packa| +2849|154|2|1|16|16866.40|0.09|0.08|N|O|1996-05-20|1996-07-23|1996-06-18|NONE|TRUCK|. furiously regular requ| +2849|187|8|2|39|42400.02|0.10|0.03|N|O|1996-05-22|1996-07-18|1996-06-05|TAKE BACK RETURN|SHIP|s sleep furiously silently regul| +2849|60|1|3|24|23041.44|0.01|0.05|N|O|1996-06-12|1996-07-10|1996-06-27|TAKE BACK RETURN|AIR|e slyly even asymptotes. slo| +2849|55|7|4|48|45842.40|0.05|0.02|N|O|1996-05-03|1996-06-05|1996-05-28|NONE|AIR|mong the carefully regular theodol| +2849|28|7|5|30|27840.60|0.10|0.06|N|O|1996-08-24|1996-07-08|1996-09-03|TAKE BACK RETURN|SHIP|ly. carefully silent| +2849|69|4|6|30|29071.80|0.06|0.07|N|O|1996-06-20|1996-07-23|1996-07-06|NONE|FOB|yly furiously even id| +2850|97|1|1|43|42874.87|0.02|0.05|N|O|1997-01-11|1996-11-03|1997-02-01|COLLECT COD|REG AIR|unusual accounts| +2850|110|7|2|30|30303.30|0.09|0.01|N|O|1996-12-14|1996-11-29|1997-01-03|COLLECT COD|AIR|even ideas. busy pinto beans sleep above t| +2850|105|6|3|49|49249.90|0.09|0.04|N|O|1996-10-07|1996-12-12|1996-10-12|TAKE BACK RETURN|MAIL| slyly unusual req| +2850|199|3|4|4|4396.76|0.04|0.04|N|O|1996-10-28|1996-12-26|1996-11-07|COLLECT COD|RAIL|al deposits cajole carefully quickly | +2851|148|5|1|8|8385.12|0.09|0.03|N|O|1997-11-12|1997-11-22|1997-12-11|NONE|REG AIR|y special theodolites. carefully| +2852|177|6|1|6|6463.02|0.01|0.01|R|F|1993-03-02|1993-04-11|1993-03-11|TAKE BACK RETURN|RAIL| accounts above the furiously un| +2852|41|10|2|24|22584.96|0.05|0.07|R|F|1993-01-18|1993-03-13|1993-02-14|DELIVER IN PERSON|MAIL| the blithe| +2852|164|9|3|29|30860.64|0.09|0.05|R|F|1993-04-21|1993-03-22|1993-05-02|COLLECT COD|SHIP|lyly ironi| +2852|100|3|4|12|12001.20|0.08|0.02|A|F|1993-02-25|1993-03-24|1993-03-07|TAKE BACK RETURN|TRUCK|le. request| +2852|154|2|5|28|29516.20|0.05|0.03|R|F|1993-02-08|1993-03-30|1993-02-11|NONE|MAIL|e accounts. caref| +2853|139|5|1|14|14547.82|0.07|0.05|R|F|1994-05-16|1994-07-01|1994-05-27|NONE|TRUCK|oach slyly along t| +2853|134|10|2|26|26887.38|0.06|0.01|R|F|1994-06-26|1994-06-05|1994-07-02|TAKE BACK RETURN|MAIL|dolphins wake slyly. blith| +2853|173|3|3|40|42926.80|0.06|0.04|A|F|1994-08-06|1994-06-24|1994-08-29|NONE|RAIL|lyly. pearls cajole. final accounts ca| +2853|132|8|4|20|20642.60|0.02|0.04|A|F|1994-08-30|1994-06-16|1994-09-06|TAKE BACK RETURN|TRUCK|e slyly silent foxes. express deposits sno| +2853|36|7|5|1|936.03|0.08|0.05|R|F|1994-09-01|1994-06-27|1994-09-12|TAKE BACK RETURN|FOB|refully slyly quick packages. final c| +2854|181|2|1|46|49734.28|0.00|0.04|A|F|1994-09-22|1994-08-02|1994-09-30|COLLECT COD|AIR|. furiously regular deposits across th| +2854|88|9|2|29|28654.32|0.09|0.07|R|F|1994-07-06|1994-08-26|1994-07-09|COLLECT COD|SHIP|y slyly ironic accounts. foxes haggle slyl| +2854|160|8|3|20|21203.20|0.08|0.01|R|F|1994-09-18|1994-08-03|1994-10-12|COLLECT COD|AIR|rs impress after the deposits. | +2854|170|1|4|34|36385.78|0.06|0.03|A|F|1994-09-06|1994-08-07|1994-09-22|NONE|REG AIR|age carefully| +2854|102|3|5|7|7014.70|0.03|0.06|A|F|1994-09-23|1994-08-14|1994-10-10|DELIVER IN PERSON|REG AIR| the pending| +2854|18|2|6|13|11934.13|0.04|0.03|R|F|1994-09-15|1994-08-18|1994-09-19|DELIVER IN PERSON|SHIP| excuses wak| +2855|33|4|1|50|46651.50|0.03|0.07|A|F|1993-05-20|1993-06-28|1993-06-16|TAKE BACK RETURN|TRUCK|beans. deposits | +2880|35|6|1|40|37401.20|0.09|0.00|A|F|1992-05-26|1992-06-01|1992-05-31|COLLECT COD|TRUCK|even requests. quick| +2880|139|5|2|26|27017.38|0.07|0.07|R|F|1992-04-12|1992-04-15|1992-04-28|NONE|RAIL|ully among the regular warthogs| +2880|115|9|3|42|42634.62|0.01|0.01|R|F|1992-06-17|1992-05-29|1992-07-11|NONE|REG AIR|ions. carefully final accounts are unusual,| +2880|18|2|4|46|42228.46|0.02|0.02|A|F|1992-04-21|1992-06-05|1992-05-16|COLLECT COD|RAIL|eep quickly according to t| +2881|180|10|1|16|17282.88|0.02|0.06|A|F|1992-06-21|1992-06-27|1992-07-03|TAKE BACK RETURN|TRUCK|usly bold | +2881|10|1|2|1|910.01|0.09|0.03|A|F|1992-05-13|1992-07-21|1992-05-18|COLLECT COD|MAIL|final theodolites. quickly| +2881|93|6|3|21|20854.89|0.07|0.03|A|F|1992-05-28|1992-07-03|1992-06-02|TAKE BACK RETURN|SHIP|hely express Tiresias. final dependencies | +2881|140|6|4|7|7280.98|0.06|0.01|R|F|1992-08-03|1992-07-10|1992-08-27|NONE|REG AIR|ironic packages are carefully final ac| +2882|4|7|1|14|12656.00|0.09|0.02|N|O|1995-09-28|1995-11-11|1995-10-18|TAKE BACK RETURN|MAIL|kly. even requests w| +2882|42|1|2|30|28261.20|0.00|0.00|N|O|1995-10-15|1995-10-13|1995-10-25|NONE|REG AIR|among the furiously even theodolites. regu| +2882|197|9|3|29|31818.51|0.10|0.08|N|O|1995-09-10|1995-11-01|1995-10-02|NONE|TRUCK|kages. furiously ironic| +2882|78|6|4|27|26407.89|0.06|0.02|N|O|1995-09-04|1995-11-11|1995-09-12|DELIVER IN PERSON|MAIL|rding to the regu| +2882|134|5|5|32|33092.16|0.07|0.03|N|O|1995-10-21|1995-11-10|1995-11-01|COLLECT COD|RAIL|sts. quickly regular e| +2882|87|8|6|47|46392.76|0.06|0.03|N|O|1995-09-13|1995-09-21|1995-09-14|NONE|REG AIR|l, special| +2883|1|4|1|33|29733.00|0.08|0.07|R|F|1995-02-26|1995-03-04|1995-03-01|NONE|RAIL|s. final i| +2883|125|6|2|27|27678.24|0.00|0.02|A|F|1995-03-12|1995-03-10|1995-04-04|TAKE BACK RETURN|REG AIR|s. brave pinto beans nag furiously| +2883|189|10|3|47|51191.46|0.05|0.04|R|F|1995-01-29|1995-04-19|1995-02-05|DELIVER IN PERSON|SHIP|ep carefully ironic| +2883|98|2|4|23|22956.07|0.00|0.02|R|F|1995-02-03|1995-03-17|1995-02-19|TAKE BACK RETURN|AIR| even requests cajole. special, regular | +2883|195|8|5|36|39426.84|0.07|0.06|A|F|1995-05-02|1995-03-14|1995-05-30|COLLECT COD|MAIL|ests detect slyly special packages| +2884|71|2|1|41|39813.87|0.03|0.00|N|O|1998-01-02|1997-12-17|1998-01-20|DELIVER IN PERSON|TRUCK|ep. slyly even accounts a| +2884|146|5|2|25|26153.50|0.09|0.08|N|O|1998-01-18|1997-12-06|1998-02-16|TAKE BACK RETURN|MAIL|onic theodolites with the instructi| +2884|26|7|3|8|7408.16|0.08|0.08|N|O|1997-11-30|1997-11-28|1997-12-14|COLLECT COD|TRUCK|pending accounts about | +2885|4|9|1|6|5424.00|0.10|0.01|A|F|1993-01-05|1992-12-12|1993-01-19|COLLECT COD|FOB|ctions solve. slyly regular requests n| +2885|112|3|2|4|4048.44|0.07|0.00|A|F|1992-10-09|1992-12-17|1992-11-04|TAKE BACK RETURN|SHIP| pending packages wake. | +2885|1|6|3|45|40545.00|0.10|0.04|A|F|1992-12-24|1992-10-30|1993-01-04|NONE|SHIP|ess ideas. regular, silen| +2885|32|3|4|15|13980.45|0.03|0.04|R|F|1992-10-31|1992-11-24|1992-11-21|DELIVER IN PERSON|MAIL|odolites. boldly pending packages han| +2885|175|5|5|43|46232.31|0.06|0.00|R|F|1992-11-17|1992-10-30|1992-12-04|DELIVER IN PERSON|SHIP|cial deposits use bold| +2885|190|1|6|5|5450.95|0.01|0.02|R|F|1993-01-06|1992-11-13|1993-02-05|TAKE BACK RETURN|TRUCK|s. slyly express th| +2885|50|9|7|40|38002.00|0.05|0.03|A|F|1992-09-23|1992-11-15|1992-10-07|TAKE BACK RETURN|AIR| express depos| +2886|60|1|1|1|960.06|0.09|0.05|A|F|1995-02-01|1994-12-18|1995-02-28|COLLECT COD|REG AIR|eposits fr| +2886|184|5|2|38|41198.84|0.02|0.04|A|F|1995-01-21|1995-01-08|1995-01-30|NONE|SHIP|old requests along the fur| +2886|63|8|3|2|1926.12|0.04|0.07|A|F|1994-11-18|1995-01-31|1994-12-05|COLLECT COD|REG AIR|ar theodolites. e| +2886|130|3|4|46|47385.98|0.03|0.08|A|F|1995-02-02|1995-01-26|1995-02-15|TAKE BACK RETURN|SHIP|ously final packages sleep blithely regular| +2887|66|3|1|11|10626.66|0.06|0.00|N|O|1997-07-08|1997-07-17|1997-07-15|COLLECT COD|SHIP|ackages. unusual, speci| +2887|112|6|2|17|17205.87|0.00|0.08|N|O|1997-08-31|1997-07-04|1997-09-17|DELIVER IN PERSON|SHIP|fily final packages. regula| +2912|122|1|1|8|8176.96|0.06|0.04|A|F|1992-04-09|1992-04-19|1992-04-26|NONE|RAIL|hs cajole over the slyl| +2912|115|9|2|18|18271.98|0.00|0.08|R|F|1992-03-13|1992-04-19|1992-03-30|TAKE BACK RETURN|RAIL|unts cajole reg| +2913|123|6|1|39|39901.68|0.06|0.04|N|O|1997-08-28|1997-09-27|1997-09-02|TAKE BACK RETURN|AIR|. final packages a| +2913|22|5|2|22|20284.44|0.10|0.07|N|O|1997-09-18|1997-08-11|1997-10-02|COLLECT COD|MAIL|riously pending realms. blithely even pac| +2913|166|1|3|17|18124.72|0.07|0.04|N|O|1997-10-21|1997-09-25|1997-11-20|NONE|FOB|requests doze quickly. furious| +2913|143|4|4|5|5215.70|0.10|0.07|N|O|1997-10-07|1997-08-25|1997-10-09|TAKE BACK RETURN|RAIL|haggle. even, bold instructi| +2913|15|9|5|13|11895.13|0.03|0.01|N|O|1997-10-02|1997-08-20|1997-10-26|COLLECT COD|MAIL|inos are carefully alongside of the bol| +2913|168|5|6|35|37385.60|0.06|0.08|N|O|1997-08-30|1997-08-21|1997-09-03|COLLECT COD|MAIL|es. quickly even braids against| +2914|66|7|1|22|21253.32|0.05|0.06|R|F|1993-05-11|1993-04-09|1993-05-22|DELIVER IN PERSON|FOB| carefully about the fluffily ironic gifts| +2914|163|10|2|25|26579.00|0.03|0.04|A|F|1993-05-14|1993-04-04|1993-05-22|NONE|SHIP|cross the carefully even accounts.| +2914|35|1|3|4|3740.12|0.00|0.05|R|F|1993-06-11|1993-04-09|1993-06-14|TAKE BACK RETURN|SHIP|s integrate. bold deposits sleep req| +2914|121|2|4|9|9190.08|0.06|0.01|R|F|1993-06-17|1993-05-26|1993-06-19|NONE|REG AIR|s. carefully final foxes ar| +2915|175|5|1|28|30104.76|0.10|0.02|R|F|1994-04-17|1994-06-09|1994-05-10|NONE|MAIL|yly special | +2915|94|7|2|12|11929.08|0.00|0.03|A|F|1994-07-18|1994-06-11|1994-07-27|TAKE BACK RETURN|RAIL|accounts. slyly final| +2915|136|2|3|15|15541.95|0.07|0.00|A|F|1994-05-01|1994-06-12|1994-05-15|DELIVER IN PERSON|TRUCK|al requests haggle furiousl| +2915|81|2|4|43|42186.44|0.06|0.05|R|F|1994-06-02|1994-05-24|1994-06-06|DELIVER IN PERSON|SHIP|into beans dazzle alongside of| +2916|83|4|1|21|20644.68|0.06|0.04|N|O|1996-03-11|1996-02-21|1996-03-30|NONE|REG AIR|uickly express ideas over the slyly even | +2917|93|4|1|36|35751.24|0.10|0.01|N|O|1998-04-07|1998-02-23|1998-05-01|DELIVER IN PERSON|RAIL|usly ironic d| +2917|21|2|2|20|18420.40|0.06|0.03|N|O|1997-12-31|1998-01-22|1998-01-12|NONE|MAIL|slyly even ideas wa| +2917|90|1|3|4|3960.36|0.02|0.07|N|O|1998-01-10|1998-01-18|1998-02-08|TAKE BACK RETURN|REG AIR|s. unusual instruct| +2917|167|2|4|5|5335.80|0.05|0.01|N|O|1997-12-16|1998-01-26|1998-01-07|NONE|RAIL|bove the furiously silent packages. pend| +2917|41|10|5|37|34818.48|0.04|0.01|N|O|1997-12-12|1998-02-03|1997-12-23|COLLECT COD|RAIL|dependencies. express | +2917|194|8|6|7|7659.33|0.05|0.01|N|O|1998-03-21|1998-03-03|1998-03-25|NONE|REG AIR|ly about the regular accounts. carefully pe| +2918|78|7|1|24|23473.68|0.10|0.03|N|O|1996-12-20|1996-10-28|1996-12-26|DELIVER IN PERSON|FOB| quickly. express requests haggle careful| +2919|102|5|1|2|2004.20|0.03|0.05|R|F|1993-12-28|1994-02-23|1994-01-18|COLLECT COD|TRUCK|re slyly. regular ideas detect furiousl| +2919|121|4|2|49|50034.88|0.07|0.02|R|F|1993-12-16|1994-02-28|1993-12-19|COLLECT COD|FOB|hely final inst| +2919|46|5|3|44|41625.76|0.07|0.07|A|F|1994-04-01|1994-01-12|1994-04-07|TAKE BACK RETURN|TRUCK|final ideas haggle carefully fluff| +2919|102|5|4|44|44092.40|0.00|0.05|R|F|1994-02-04|1994-02-03|1994-03-02|TAKE BACK RETURN|AIR|es doze around the furiously | +2944|120|1|1|44|44885.28|0.08|0.05|N|O|1997-12-25|1997-10-28|1998-01-21|COLLECT COD|AIR|ickly special theodolit| +2944|42|9|2|44|41449.76|0.06|0.02|N|O|1997-10-28|1997-11-22|1997-11-10|NONE|SHIP|ickly. regular requests haggle. idea| +2944|170|5|3|2|2140.34|0.06|0.07|N|O|1997-12-13|1997-12-01|1998-01-08|DELIVER IN PERSON|REG AIR|luffily expr| +2944|17|7|4|23|21091.23|0.02|0.03|N|O|1998-01-12|1997-12-03|1998-01-17|TAKE BACK RETURN|MAIL| excuses? regular platelets e| +2944|75|4|5|18|17551.26|0.10|0.01|N|O|1998-01-07|1997-10-26|1998-01-27|TAKE BACK RETURN|FOB| furiously slyl| +2944|60|2|6|17|16321.02|0.00|0.03|N|O|1997-10-18|1997-11-27|1997-10-29|TAKE BACK RETURN|SHIP|slyly final dolphins sleep silent the| +2944|90|1|7|7|6930.63|0.01|0.06|N|O|1997-10-30|1997-11-03|1997-11-03|DELIVER IN PERSON|FOB|fluffily blithely express pea| +2945|59|10|1|37|35484.85|0.00|0.02|N|O|1996-02-10|1996-03-20|1996-02-12|COLLECT COD|SHIP|l instructions. regular, regular | +2945|72|2|2|30|29162.10|0.05|0.01|N|O|1996-01-19|1996-02-11|1996-01-26|NONE|TRUCK|ular instructions| +2945|127|8|3|28|28759.36|0.06|0.02|N|O|1996-03-17|1996-03-13|1996-04-15|COLLECT COD|FOB|le slyly along the eve| +2945|188|9|4|34|36998.12|0.08|0.06|N|O|1996-02-03|1996-03-17|1996-02-29|COLLECT COD|REG AIR|at the unusual theodolite| +2945|173|1|5|10|10731.70|0.09|0.05|N|O|1996-03-13|1996-03-10|1996-04-06|COLLECT COD|FOB|thely. final courts could hang qu| +2945|97|9|6|45|44869.05|0.07|0.00|N|O|1996-03-01|1996-03-25|1996-03-08|TAKE BACK RETURN|MAIL|ainst the final packages| +2945|52|10|7|47|44746.35|0.07|0.05|N|O|1996-01-05|1996-02-11|1996-01-12|DELIVER IN PERSON|MAIL|quests use| +2946|10|5|1|25|22750.25|0.05|0.02|N|O|1996-05-06|1996-04-23|1996-05-16|DELIVER IN PERSON|SHIP|ic deposits. furiously| +2946|94|5|2|48|47716.32|0.03|0.07|N|O|1996-06-02|1996-03-31|1996-06-16|COLLECT COD|TRUCK|oss the platelets. furi| +2946|3|6|3|35|31605.00|0.03|0.00|N|O|1996-03-15|1996-04-02|1996-03-26|NONE|REG AIR| sublate along the fluffily iron| +2947|10|1|1|37|33670.37|0.09|0.07|N|O|1995-08-09|1995-07-05|1995-08-20|DELIVER IN PERSON|RAIL|e accounts: expres| +2947|186|7|2|10|10861.80|0.09|0.07|A|F|1995-06-07|1995-06-26|1995-06-08|NONE|MAIL|lly special | +2948|118|9|1|48|48869.28|0.00|0.04|R|F|1994-08-29|1994-10-23|1994-09-23|NONE|TRUCK|unusual excuses use about the | +2948|92|3|2|49|48612.41|0.04|0.07|R|F|1994-12-16|1994-11-08|1995-01-07|DELIVER IN PERSON|MAIL|ress requests. furiously blithe foxes | +2949|21|6|1|4|3684.08|0.06|0.06|A|F|1994-06-07|1994-06-17|1994-07-04|TAKE BACK RETURN|REG AIR|gular pinto beans wake alongside of the reg| +2949|70|5|2|50|48503.50|0.05|0.04|A|F|1994-08-04|1994-06-23|1994-08-17|TAKE BACK RETURN|FOB|gular courts cajole across t| +2949|180|9|3|38|41046.84|0.02|0.06|R|F|1994-05-22|1994-05-25|1994-05-27|COLLECT COD|REG AIR|se slyly requests. carefull| +2950|130|1|1|32|32964.16|0.01|0.05|N|O|1997-09-21|1997-08-25|1997-10-08|DELIVER IN PERSON|REG AIR|its wake carefully slyly final ideas.| +2950|66|7|2|18|17389.08|0.10|0.01|N|O|1997-07-19|1997-08-29|1997-08-17|COLLECT COD|TRUCK|uests cajole furio| +2950|53|4|3|14|13342.70|0.01|0.02|N|O|1997-07-29|1997-08-05|1997-07-31|TAKE BACK RETURN|MAIL|ccounts haggle carefully according | +2950|187|8|4|45|48923.10|0.08|0.00|N|O|1997-09-05|1997-09-23|1997-09-11|NONE|FOB|ides the b| +2950|61|2|5|46|44208.76|0.02|0.05|N|O|1997-07-15|1997-09-30|1997-07-25|COLLECT COD|RAIL|to the regular accounts are slyly carefu| +2950|174|5|6|27|29002.59|0.01|0.03|N|O|1997-10-01|1997-09-13|1997-10-08|NONE|TRUCK|are alongside of the carefully silent | +2951|3|8|1|5|4515.00|0.03|0.03|N|O|1996-03-27|1996-04-16|1996-03-30|NONE|REG AIR|to beans wake ac| +2951|136|2|2|24|24867.12|0.07|0.03|N|O|1996-03-24|1996-04-16|1996-04-08|NONE|SHIP| ironic multipliers. express, regular| +2951|187|8|3|40|43487.20|0.02|0.07|N|O|1996-05-03|1996-04-20|1996-05-22|COLLECT COD|REG AIR|ial deposits wake fluffily about th| +2951|73|3|4|21|20434.47|0.06|0.08|N|O|1996-04-12|1996-04-27|1996-04-14|DELIVER IN PERSON|REG AIR|nt instructions toward the f| +2951|51|6|5|15|14265.75|0.07|0.00|N|O|1996-03-25|1996-04-23|1996-03-27|COLLECT COD|REG AIR|inal account| +2951|138|4|6|18|18686.34|0.06|0.00|N|O|1996-04-04|1996-04-27|1996-04-06|COLLECT COD|FOB|ep about the final, even package| +2976|9|4|1|32|29088.00|0.06|0.00|A|F|1994-01-26|1994-02-13|1994-02-10|NONE|MAIL|nding, ironic deposits sleep f| +2976|4|5|2|24|21696.00|0.00|0.03|A|F|1994-03-19|1994-01-26|1994-04-18|COLLECT COD|TRUCK|ronic pinto beans. slyly bol| +2976|10|5|3|35|31850.35|0.10|0.07|R|F|1993-12-19|1994-02-14|1994-01-11|NONE|RAIL|boost slyly about the regular, regular re| +2976|82|3|4|22|21605.76|0.00|0.04|A|F|1994-02-08|1994-03-03|1994-02-12|TAKE BACK RETURN|FOB|ncies kindle furiously. carefull| +2976|134|5|5|13|13443.69|0.00|0.06|A|F|1994-02-06|1994-02-02|1994-02-19|NONE|FOB| furiously final courts boost | +2976|109|2|6|30|30273.00|0.08|0.03|R|F|1994-03-27|1994-02-01|1994-04-26|TAKE BACK RETURN|RAIL|c ideas! unusual| +2977|70|5|1|25|24251.75|0.03|0.07|N|O|1996-09-21|1996-10-06|1996-10-13|TAKE BACK RETURN|RAIL|furiously pe| +2978|90|1|1|29|28712.61|0.00|0.08|A|F|1995-06-03|1995-07-25|1995-06-06|NONE|SHIP|ecial ideas promise slyly| +2978|127|2|2|42|43139.04|0.01|0.06|N|O|1995-08-19|1995-07-18|1995-09-07|DELIVER IN PERSON|MAIL|ial requests nag blithely alongside of th| +2978|43|2|3|26|24519.04|0.07|0.05|N|O|1995-07-29|1995-07-22|1995-08-20|COLLECT COD|REG AIR|as haggle against the carefully express dep| +2978|28|1|4|7|6496.14|0.00|0.00|N|O|1995-07-18|1995-07-03|1995-07-23|NONE|FOB|. final ideas are blithe| +2978|29|2|5|33|30657.66|0.09|0.03|R|F|1995-05-06|1995-07-23|1995-05-16|COLLECT COD|FOB|s. blithely unusual pack| +2978|168|7|6|4|4272.64|0.08|0.04|N|O|1995-07-06|1995-07-31|1995-07-19|COLLECT COD|AIR|ffily unusual | +2979|9|6|1|8|7272.00|0.00|0.08|N|O|1996-06-18|1996-05-21|1996-07-06|COLLECT COD|REG AIR|st blithely; blithely regular gifts dazz| +2979|11|2|2|47|42817.47|0.05|0.00|N|O|1996-03-25|1996-05-13|1996-04-04|TAKE BACK RETURN|SHIP|iously unusual dependencies wake across| +2979|188|9|3|35|38086.30|0.04|0.03|N|O|1996-05-25|1996-06-11|1996-06-24|DELIVER IN PERSON|MAIL|old ideas beneath the blit| +2979|165|4|4|28|29824.48|0.05|0.08|N|O|1996-06-04|1996-04-23|1996-06-24|DELIVER IN PERSON|FOB|ing, regular pinto beans. blithel| +2980|37|3|1|2|1874.06|0.09|0.03|N|O|1996-11-18|1996-10-22|1996-11-27|TAKE BACK RETURN|SHIP|enly across the special, pending packag| +2980|10|7|2|48|43680.48|0.04|0.05|N|O|1996-09-25|1996-12-09|1996-10-12|NONE|REG AIR|totes. regular pinto | +2980|133|9|3|27|27894.51|0.08|0.08|N|O|1996-12-08|1996-12-03|1996-12-14|NONE|REG AIR| theodolites cajole blithely sl| +2980|25|10|4|49|45325.98|0.03|0.02|N|O|1996-10-04|1996-12-04|1996-10-06|NONE|RAIL|hy packages sleep quic| +2980|187|8|5|24|26092.32|0.05|0.04|N|O|1997-01-12|1996-10-27|1997-01-14|NONE|MAIL|elets. fluffily regular in| +2980|109|4|6|43|43391.30|0.01|0.01|N|O|1996-12-07|1996-11-10|1997-01-02|COLLECT COD|AIR|sts. slyly regu| +2981|14|4|1|17|15538.17|0.03|0.05|N|O|1998-10-17|1998-10-02|1998-10-21|DELIVER IN PERSON|RAIL|, unusual packages x-ray. furious| +2981|176|4|2|8|8609.36|0.06|0.03|N|O|1998-08-21|1998-09-28|1998-09-05|DELIVER IN PERSON|MAIL|ng to the f| +2981|37|3|3|14|13118.42|0.03|0.07|N|O|1998-08-30|1998-10-04|1998-09-04|DELIVER IN PERSON|MAIL|kages detect furiously express requests.| +2982|112|6|1|21|21254.31|0.00|0.01|A|F|1995-04-03|1995-06-08|1995-04-18|DELIVER IN PERSON|AIR|ironic deposits. furiously ex| +2982|99|2|2|13|12988.17|0.02|0.08|R|F|1995-03-31|1995-05-07|1995-04-18|TAKE BACK RETURN|RAIL|regular deposits unwind alongside | +2982|70|5|3|21|20371.47|0.01|0.01|R|F|1995-04-19|1995-06-03|1995-04-28|COLLECT COD|SHIP|egular ideas use furiously? bl| +2983|163|4|1|44|46779.04|0.03|0.06|R|F|1992-02-09|1992-03-07|1992-03-09|TAKE BACK RETURN|AIR|ly regular instruct| +2983|49|8|2|11|10439.44|0.09|0.06|A|F|1992-04-29|1992-02-27|1992-05-26|NONE|MAIL|aids integrate s| +3008|132|3|1|8|8257.04|0.10|0.04|N|O|1995-12-06|1996-01-12|1995-12-22|TAKE BACK RETURN|FOB|yly ironic foxes. regular requests h| +3008|200|3|2|31|34106.20|0.05|0.06|N|O|1995-12-14|1995-12-11|1995-12-31|TAKE BACK RETURN|AIR| bold packages. quic| +3008|24|5|3|40|36960.80|0.01|0.03|N|O|1995-12-18|1996-01-06|1996-01-11|COLLECT COD|AIR|esias. theodolites detect blithely | +3008|60|1|4|48|46082.88|0.07|0.06|N|O|1996-01-23|1996-01-07|1996-02-09|COLLECT COD|SHIP|ld theodolites. fluffily bold theodolit| +3008|105|10|5|31|31158.10|0.03|0.02|N|O|1995-12-01|1996-01-20|1995-12-28|COLLECT COD|RAIL|nts use thinly around the carefully iro| +3009|45|8|1|48|45361.92|0.10|0.02|N|O|1997-03-19|1997-05-13|1997-04-11|TAKE BACK RETURN|TRUCK| dependencies sleep quickly a| +3009|185|6|2|38|41236.84|0.00|0.01|N|O|1997-05-01|1997-04-10|1997-05-17|TAKE BACK RETURN|AIR|nal packages should haggle slyly. quickl| +3009|130|3|3|26|26783.38|0.08|0.02|N|O|1997-05-15|1997-05-10|1997-06-13|TAKE BACK RETURN|SHIP|uriously specia| +3010|138|4|1|23|23876.99|0.04|0.00|N|O|1996-03-08|1996-02-29|1996-03-27|NONE|TRUCK|ounts. pendin| +3010|174|4|2|22|23631.74|0.09|0.06|N|O|1996-03-06|1996-04-06|1996-03-18|COLLECT COD|REG AIR| final deposit| +3010|58|6|3|24|22993.20|0.04|0.07|N|O|1996-05-09|1996-03-14|1996-05-15|DELIVER IN PERSON|RAIL|ar, even reques| +3010|24|7|4|28|25872.56|0.09|0.06|N|O|1996-03-05|1996-03-28|1996-04-03|DELIVER IN PERSON|FOB|ake carefully carefully even request| +3010|104|5|5|9|9036.90|0.02|0.02|N|O|1996-04-28|1996-03-17|1996-05-18|NONE|SHIP|inal packages. quickly even pinto| +3010|92|3|6|38|37699.42|0.05|0.07|N|O|1996-04-15|1996-03-16|1996-04-21|DELIVER IN PERSON|RAIL|accounts ar| +3011|198|10|1|5|5490.95|0.02|0.04|R|F|1992-04-21|1992-02-23|1992-05-15|NONE|TRUCK|nusual sentiments. carefully bold idea| +3011|123|4|2|42|42971.04|0.05|0.00|A|F|1992-02-01|1992-03-18|1992-02-29|NONE|TRUCK|osits haggle quickly pending, | +3012|195|7|1|49|53664.31|0.00|0.00|A|F|1993-08-07|1993-07-01|1993-08-08|NONE|MAIL| quickly furious packages. silently unusua| +3012|161|2|2|37|39262.92|0.06|0.03|A|F|1993-08-16|1993-06-07|1993-08-24|TAKE BACK RETURN|REG AIR|uickly permanent packages sleep caref| +3013|94|6|1|31|30816.79|0.08|0.08|N|O|1997-05-03|1997-04-05|1997-05-25|NONE|AIR|y furious depen| +3013|139|5|2|30|31173.90|0.05|0.06|N|O|1997-05-02|1997-03-09|1997-05-12|TAKE BACK RETURN|MAIL|ronic packages. slyly even| +3013|120|10|3|35|35704.20|0.00|0.03|N|O|1997-04-02|1997-05-04|1997-04-16|COLLECT COD|MAIL|ely accord| +3013|181|2|4|17|18380.06|0.01|0.07|N|O|1997-02-26|1997-05-02|1997-03-27|DELIVER IN PERSON|SHIP|fully unusual account| +3013|60|5|5|20|19201.20|0.00|0.04|N|O|1997-05-06|1997-03-18|1997-05-12|COLLECT COD|RAIL|unts boost regular ideas. slyly pe| +3013|72|2|6|19|18469.33|0.08|0.07|N|O|1997-05-11|1997-04-18|1997-05-15|COLLECT COD|REG AIR|fluffily pending packages nag furiously al| +3014|163|4|1|36|38273.76|0.05|0.03|A|F|1992-11-16|1993-01-20|1992-11-28|TAKE BACK RETURN|FOB|ding accounts boost fu| +3014|106|1|2|36|36219.60|0.00|0.08|R|F|1992-12-28|1992-12-29|1993-01-24|COLLECT COD|MAIL|iously ironic r| +3014|151|9|3|48|50455.20|0.06|0.02|A|F|1992-12-19|1993-01-08|1992-12-25|DELIVER IN PERSON|REG AIR|y pending theodolites wake. reg| +3014|114|1|4|14|14197.54|0.10|0.02|R|F|1992-11-19|1993-01-01|1992-12-17|DELIVER IN PERSON|SHIP|. slyly brave platelets nag. careful,| +3014|75|5|5|28|27301.96|0.02|0.08|R|F|1993-01-09|1992-12-18|1993-01-10|TAKE BACK RETURN|FOB|es are. final braids nag slyly. fluff| +3014|38|4|6|30|28140.90|0.04|0.01|R|F|1993-02-28|1993-01-02|1993-03-20|TAKE BACK RETURN|AIR| final foxes.| +3015|3|8|1|5|4515.00|0.09|0.00|A|F|1993-01-10|1992-12-02|1993-01-19|TAKE BACK RETURN|RAIL| the furiously pendi| +3015|18|2|2|17|15606.17|0.03|0.01|R|F|1992-10-16|1992-11-20|1992-10-28|COLLECT COD|AIR|s above the fluffily final t| +3015|91|4|3|23|22795.07|0.03|0.05|A|F|1992-12-03|1992-11-19|1992-12-23|DELIVER IN PERSON|FOB|s are slyly carefully special pinto bea| +3015|156|7|4|7|7393.05|0.10|0.03|A|F|1992-12-07|1992-12-17|1992-12-30|DELIVER IN PERSON|REG AIR| after the evenly special packages ca| +3015|165|4|5|42|44736.72|0.04|0.02|R|F|1993-01-21|1992-11-07|1993-02-11|DELIVER IN PERSON|AIR|encies haggle furious| +3015|66|7|6|18|17389.08|0.02|0.03|R|F|1992-10-10|1992-11-19|1992-10-18|TAKE BACK RETURN|MAIL|equests wake fluffil| +3040|16|6|1|18|16488.18|0.08|0.04|R|F|1993-06-25|1993-07-06|1993-07-19|TAKE BACK RETURN|SHIP|ly thin accou| +3040|133|9|2|9|9298.17|0.00|0.01|A|F|1993-06-12|1993-05-16|1993-06-14|NONE|RAIL|ges. pending packages wake. requests| +3040|126|5|3|30|30783.60|0.01|0.01|A|F|1993-08-06|1993-05-18|1993-08-19|NONE|MAIL|x furiously bold packages. expres| +3040|83|4|4|14|13763.12|0.05|0.04|A|F|1993-05-13|1993-05-18|1993-05-19|TAKE BACK RETURN|REG AIR| haggle carefully. express hocke| +3040|52|3|5|43|40938.15|0.04|0.04|R|F|1993-05-21|1993-05-25|1993-05-26|NONE|MAIL|sts nag slyly alongside of the depos| +3040|18|5|6|10|9180.10|0.08|0.04|R|F|1993-05-16|1993-06-24|1993-06-11|DELIVER IN PERSON|MAIL|ely regular foxes haggle dari| +3041|181|2|1|5|5405.90|0.07|0.04|N|O|1997-07-20|1997-07-15|1997-08-17|COLLECT COD|FOB|posits dazzle special p| +3041|146|9|2|9|9415.26|0.03|0.03|N|O|1997-06-29|1997-08-14|1997-07-19|COLLECT COD|AIR|iously across the silent pinto beans. furi| +3041|68|5|3|9|8712.54|0.09|0.06|N|O|1997-08-28|1997-07-23|1997-09-16|TAKE BACK RETURN|FOB|scapades after the special| +3042|105|2|1|30|30153.00|0.08|0.06|A|F|1995-01-12|1995-02-15|1995-01-24|DELIVER IN PERSON|SHIP|the requests detect fu| +3042|102|3|2|28|28058.80|0.05|0.03|A|F|1994-11-24|1995-01-02|1994-12-06|TAKE BACK RETURN|MAIL|ng the furiously r| +3042|14|8|3|34|31076.34|0.04|0.00|R|F|1994-12-11|1995-02-03|1994-12-21|TAKE BACK RETURN|TRUCK|can wake after the enticingly stealthy i| +3042|48|1|4|19|18012.76|0.02|0.01|A|F|1995-03-05|1995-01-24|1995-03-17|COLLECT COD|TRUCK|e carefully. regul| +3043|46|9|1|23|21758.92|0.07|0.04|R|F|1992-05-08|1992-07-22|1992-05-18|COLLECT COD|TRUCK|uickly above the pending,| +3043|6|3|2|15|13590.00|0.03|0.05|A|F|1992-05-27|1992-06-03|1992-06-09|COLLECT COD|FOB|usly furiously| +3043|60|1|3|42|40322.52|0.10|0.07|R|F|1992-07-15|1992-06-19|1992-07-23|NONE|MAIL|ide of the un| +3043|91|2|4|5|4955.45|0.10|0.01|A|F|1992-05-22|1992-07-02|1992-06-20|TAKE BACK RETURN|TRUCK|ake blithely re| +3044|101|2|1|10|10011.00|0.07|0.08|N|O|1996-07-13|1996-05-06|1996-07-21|TAKE BACK RETURN|REG AIR| slyly ironic requests. s| +3044|168|7|2|3|3204.48|0.06|0.02|N|O|1996-07-27|1996-05-26|1996-08-15|TAKE BACK RETURN|AIR|ecoys haggle furiously pending requests.| +3044|19|3|3|47|43193.47|0.09|0.00|N|O|1996-05-24|1996-06-22|1996-05-30|NONE|REG AIR|ly around the car| +3045|88|9|1|41|40511.28|0.05|0.01|N|O|1995-09-30|1995-11-24|1995-10-03|TAKE BACK RETURN|MAIL|ely final foxes. carefully ironic pinto b| +3045|69|6|2|48|46514.88|0.02|0.03|N|O|1995-10-01|1995-12-16|1995-10-10|TAKE BACK RETURN|MAIL|ole quickly outside th| +3046|74|5|1|44|42859.08|0.03|0.03|N|O|1996-03-03|1996-02-25|1996-04-01|NONE|AIR| are quickly. blithe| +3046|54|5|2|46|43886.30|0.03|0.08|N|O|1996-03-22|1996-02-28|1996-04-07|TAKE BACK RETURN|AIR|sits sleep furious| +3046|2|9|3|31|27962.00|0.03|0.07|N|O|1996-03-24|1996-01-30|1996-03-26|NONE|RAIL|y pending somas alongside of the slyly iro| +3047|104|5|1|17|17069.70|0.08|0.02|N|O|1997-06-14|1997-04-20|1997-06-23|COLLECT COD|FOB|onic instruction| +3047|14|1|2|23|21022.23|0.00|0.04|N|O|1997-05-20|1997-06-14|1997-05-28|TAKE BACK RETURN|REG AIR| slyly ironi| +3072|57|9|1|6|5742.30|0.09|0.05|R|F|1994-02-09|1994-03-24|1994-02-28|DELIVER IN PERSON|REG AIR|gular requests abov| +3072|108|3|2|36|36291.60|0.07|0.02|R|F|1994-04-14|1994-04-22|1994-05-06|COLLECT COD|AIR| theodolites. blithely e| +3072|97|8|3|7|6979.63|0.04|0.07|R|F|1994-05-09|1994-03-31|1994-05-19|COLLECT COD|TRUCK|uests. ironic, ironic depos| +3072|83|4|4|39|38340.12|0.05|0.08|A|F|1994-05-27|1994-04-20|1994-06-14|COLLECT COD|MAIL|es; slyly spe| +3072|88|9|5|1|988.08|0.01|0.08|R|F|1994-02-26|1994-03-14|1994-03-19|NONE|AIR| slyly ironic attainments. car| +3073|194|7|1|16|17507.04|0.07|0.01|R|F|1994-03-02|1994-03-23|1994-03-31|DELIVER IN PERSON|AIR|n requests. ironi| +3073|22|5|2|47|43334.94|0.09|0.00|R|F|1994-03-26|1994-02-12|1994-04-21|NONE|REG AIR|eposits. fluffily| +3073|87|8|3|10|9870.80|0.03|0.00|R|F|1994-02-11|1994-03-24|1994-02-26|COLLECT COD|FOB| furiously caref| +3073|29|4|4|14|13006.28|0.09|0.07|R|F|1994-03-24|1994-04-01|1994-04-07|NONE|RAIL|ilently quiet epitaphs.| +3073|41|10|5|25|23526.00|0.00|0.07|R|F|1994-04-14|1994-03-07|1994-04-22|NONE|TRUCK|nag asymptotes. pinto beans sleep | +3073|147|8|6|39|40838.46|0.09|0.02|R|F|1994-05-01|1994-02-16|1994-05-12|DELIVER IN PERSON|AIR|lar excuses across the furiously even | +3073|44|5|7|11|10384.44|0.08|0.07|A|F|1994-05-01|1994-03-06|1994-05-08|COLLECT COD|SHIP|instructions sleep according to the | +3074|37|8|1|50|46851.50|0.08|0.08|A|F|1993-01-31|1992-12-15|1993-02-20|NONE|AIR|furiously pending requests haggle s| +3074|139|5|2|39|40526.07|0.03|0.00|R|F|1992-12-08|1993-01-28|1992-12-09|DELIVER IN PERSON|TRUCK|iously throu| +3075|9|6|1|39|35451.00|0.02|0.03|A|F|1994-06-10|1994-06-21|1994-06-20|NONE|FOB|ing deposits nag | +3075|52|10|2|2|1904.10|0.07|0.08|R|F|1994-06-14|1994-06-10|1994-06-25|TAKE BACK RETURN|AIR|. unusual, unusual accounts haggle furious| +3076|85|6|1|44|43343.52|0.00|0.05|A|F|1993-09-14|1993-10-04|1993-09-17|TAKE BACK RETURN|FOB| instructions h| +3076|106|1|2|22|22134.20|0.08|0.00|A|F|1993-09-05|1993-09-10|1993-09-27|NONE|REG AIR|packages wake furiou| +3076|5|8|3|31|28055.00|0.06|0.06|A|F|1993-08-10|1993-09-17|1993-08-17|TAKE BACK RETURN|SHIP|regular depos| +3077|72|2|1|25|24301.75|0.06|0.01|N|O|1997-09-14|1997-10-16|1997-10-06|NONE|TRUCK|lent account| +3077|91|3|2|40|39643.60|0.05|0.06|N|O|1997-10-22|1997-09-19|1997-11-19|DELIVER IN PERSON|AIR|to the enticing packag| +3077|78|7|3|13|12714.91|0.03|0.07|N|O|1997-09-09|1997-10-15|1997-09-19|NONE|TRUCK|luffily close depende| +3077|115|5|4|23|23347.53|0.03|0.02|N|O|1997-11-05|1997-09-16|1997-11-20|NONE|MAIL|lly. fluffily pending dinos across| +3078|132|3|1|25|25803.25|0.01|0.03|A|F|1993-04-22|1993-05-01|1993-04-28|TAKE BACK RETURN|AIR|express dinos. carefully ironic| +3078|78|8|2|21|20539.47|0.09|0.07|A|F|1993-03-20|1993-03-21|1993-04-01|COLLECT COD|AIR|e fluffily. | +3079|70|5|1|20|19401.40|0.05|0.00|N|O|1997-10-18|1997-10-26|1997-11-14|NONE|RAIL|ets are according to the quickly dari| +3079|117|1|2|38|38650.18|0.08|0.07|N|O|1997-11-07|1997-11-25|1997-12-06|NONE|RAIL|e carefully regular realms| +3079|17|8|3|40|36680.40|0.02|0.08|N|O|1997-09-26|1997-12-11|1997-10-09|NONE|RAIL|ide of the pending, special deposi| +3079|24|5|4|2|1848.04|0.00|0.08|N|O|1998-01-05|1997-11-17|1998-01-28|NONE|FOB|ly busy requests believ| +3079|188|9|5|2|2176.36|0.10|0.00|N|O|1997-12-27|1997-10-25|1998-01-08|COLLECT COD|SHIP|y regular asymptotes doz| +3079|166|1|6|46|49043.36|0.00|0.00|N|O|1997-11-19|1997-11-04|1997-11-25|DELIVER IN PERSON|REG AIR|es. final, regula| +3104|51|6|1|20|19021.00|0.01|0.08|A|F|1993-12-31|1993-11-24|1994-01-12|DELIVER IN PERSON|REG AIR|s are. furiously s| +3104|48|1|2|47|44557.88|0.02|0.05|A|F|1993-12-25|1993-11-02|1994-01-12|COLLECT COD|RAIL|ily daring acc| +3104|63|4|3|11|10593.66|0.02|0.03|A|F|1993-10-05|1993-11-30|1993-10-27|NONE|TRUCK| special deposits u| +3104|38|9|4|26|24388.78|0.02|0.08|R|F|1994-01-02|1993-12-05|1994-01-31|TAKE BACK RETURN|TRUCK|es boost carefully. slyly | +3105|184|5|1|11|11925.98|0.01|0.06|N|O|1997-02-07|1997-02-09|1997-03-01|NONE|FOB|kly bold depths caj| +3105|45|6|2|9|8505.36|0.08|0.08|N|O|1996-12-25|1997-02-04|1997-01-09|COLLECT COD|SHIP|es wake among t| +3105|25|4|3|48|44400.96|0.02|0.05|N|O|1997-02-28|1997-01-31|1997-03-18|DELIVER IN PERSON|REG AIR|ending platelets wake carefully ironic inst| +3105|91|5|4|23|22795.07|0.04|0.07|N|O|1997-03-08|1996-12-14|1997-03-18|COLLECT COD|REG AIR| detect slyly. blithely unusual requests ar| +3105|90|1|5|8|7920.72|0.07|0.07|N|O|1996-12-28|1996-12-28|1997-01-25|NONE|FOB|s. blithely unusual ideas was after| +3105|47|6|6|30|28411.20|0.08|0.05|N|O|1997-03-03|1997-02-03|1997-03-05|NONE|FOB|ess accounts boost among t| +3106|86|7|1|22|21693.76|0.03|0.02|N|O|1997-02-28|1997-02-12|1997-03-03|DELIVER IN PERSON|FOB|structions atop the blithely| +3106|136|2|2|49|50770.37|0.06|0.06|N|O|1997-02-27|1997-03-11|1997-03-12|NONE|TRUCK|lets. quietly regular courts | +3106|52|7|3|42|39986.10|0.09|0.07|N|O|1997-04-05|1997-03-17|1997-04-22|COLLECT COD|REG AIR|nstructions wake. furiously | +3106|196|10|4|6|6577.14|0.10|0.07|N|O|1997-02-02|1997-04-11|1997-02-27|COLLECT COD|REG AIR|symptotes. slyly bold platelets cajol| +3106|65|2|5|16|15440.96|0.09|0.08|N|O|1997-02-25|1997-04-10|1997-03-16|NONE|AIR|sits wake slyl| +3107|149|6|1|16|16786.24|0.05|0.04|N|O|1997-08-30|1997-10-20|1997-09-20|TAKE BACK RETURN|REG AIR|regular pinto beans. ironic ideas haggle| +3107|142|3|2|35|36474.90|0.05|0.06|N|O|1997-08-27|1997-11-19|1997-09-14|COLLECT COD|TRUCK|ets doubt furiously final ideas. final| +3107|170|9|3|23|24613.91|0.03|0.06|N|O|1997-12-10|1997-11-11|1997-12-14|TAKE BACK RETURN|SHIP|atelets must ha| +3107|87|8|4|27|26651.16|0.00|0.08|N|O|1997-11-15|1997-10-31|1997-11-28|DELIVER IN PERSON|FOB|furiously final | +3108|109|2|1|37|37336.70|0.06|0.04|A|F|1993-10-16|1993-10-01|1993-11-09|DELIVER IN PERSON|RAIL| final requests. | +3108|166|1|2|26|27720.16|0.08|0.05|A|F|1993-11-12|1993-10-05|1993-12-09|COLLECT COD|TRUCK| slyly slow foxes wake furious| +3109|18|2|1|32|29376.32|0.08|0.03|A|F|1993-09-05|1993-10-06|1993-09-18|DELIVER IN PERSON|FOB|ecial orbits are furiou| +3109|145|4|2|49|51211.86|0.08|0.06|R|F|1993-10-24|1993-09-30|1993-11-21|TAKE BACK RETURN|AIR| even pearls. furiously pending | +3109|176|4|3|43|46275.31|0.04|0.07|R|F|1993-09-29|1993-09-06|1993-10-13|COLLECT COD|MAIL|ding to the foxes. | +3109|79|10|4|26|25455.82|0.01|0.05|R|F|1993-11-16|1993-10-18|1993-12-06|TAKE BACK RETURN|TRUCK| sleep slyly according to t| +3109|143|2|5|50|52157.00|0.01|0.08|A|F|1993-09-17|1993-10-16|1993-10-11|NONE|FOB| regular packages boost blithely even, re| +3109|15|9|6|10|9150.10|0.10|0.04|A|F|1993-10-26|1993-10-03|1993-11-09|NONE|TRUCK|sits haggle carefully. regular, unusual ac| +3110|89|10|1|1|989.08|0.02|0.07|A|F|1995-01-15|1995-01-20|1995-01-30|DELIVER IN PERSON|REG AIR|c theodolites a| +3110|57|2|2|31|29668.55|0.01|0.06|R|F|1995-03-31|1995-03-07|1995-04-21|TAKE BACK RETURN|REG AIR|en deposits. ironic| +3110|3|10|3|34|30702.00|0.02|0.02|A|F|1995-02-23|1995-01-27|1995-03-09|TAKE BACK RETURN|FOB|ly pending requests ha| +3110|40|1|4|16|15040.64|0.04|0.04|A|F|1995-01-10|1995-02-06|1995-01-26|NONE|MAIL|across the regular acco| +3110|140|6|5|39|40565.46|0.09|0.01|A|F|1995-02-09|1995-01-21|1995-02-21|NONE|MAIL|side of the blithely unusual courts. slyly | +3111|137|8|1|22|22816.86|0.06|0.05|N|O|1995-09-21|1995-11-09|1995-10-17|COLLECT COD|REG AIR|quests. regular dolphins against the | +3111|58|10|2|30|28741.50|0.06|0.05|N|O|1995-10-05|1995-11-15|1995-11-01|TAKE BACK RETURN|TRUCK|eas are furiously slyly special deposits.| +3111|52|3|3|10|9520.50|0.02|0.02|N|O|1995-11-10|1995-11-02|1995-12-04|NONE|FOB|ng the slyly ironic inst| +3111|132|3|4|31|31996.03|0.00|0.08|N|O|1995-10-26|1995-09-26|1995-11-02|TAKE BACK RETURN|MAIL|kages detect express attainments| +3111|54|6|5|14|13356.70|0.05|0.04|N|O|1995-10-17|1995-10-19|1995-10-19|TAKE BACK RETURN|SHIP|re. pinto | +3111|86|7|6|5|4930.40|0.03|0.08|N|O|1995-08-30|1995-10-16|1995-09-04|DELIVER IN PERSON|TRUCK|. carefully even ideas| +3111|148|9|7|41|42973.74|0.09|0.05|N|O|1995-11-22|1995-11-01|1995-12-01|TAKE BACK RETURN|FOB|fily slow ideas. | +3136|142|5|1|30|31264.20|0.02|0.08|R|F|1994-08-13|1994-10-02|1994-09-02|TAKE BACK RETURN|RAIL|leep blithel| +3136|103|4|2|7|7021.70|0.05|0.07|A|F|1994-10-08|1994-09-14|1994-10-11|TAKE BACK RETURN|SHIP|ic pinto beans are slyly. f| +3136|158|3|3|43|45500.45|0.00|0.07|A|F|1994-09-05|1994-09-25|1994-09-11|NONE|RAIL|. special theodolites ha| +3136|116|6|4|26|26418.86|0.04|0.05|A|F|1994-10-13|1994-11-07|1994-11-05|TAKE BACK RETURN|AIR|eep fluffily. daringly silent attainments d| +3136|67|8|5|2|1934.12|0.08|0.07|R|F|1994-11-21|1994-11-03|1994-11-26|DELIVER IN PERSON|TRUCK|? special, silent | +3136|80|1|6|29|28422.32|0.08|0.07|A|F|1994-11-16|1994-10-03|1994-12-14|NONE|FOB|latelets. final | +3137|3|4|1|6|5418.00|0.02|0.02|N|O|1995-09-19|1995-10-23|1995-10-16|NONE|SHIP|ly express as| +3137|6|3|2|4|3624.00|0.06|0.04|N|O|1995-10-01|1995-09-11|1995-10-30|COLLECT COD|RAIL|posits wake. silent excuses boost about| +3138|93|5|1|7|6951.63|0.05|0.05|R|F|1994-03-04|1994-03-14|1994-03-20|NONE|AIR|lithely quickly even packages. packages| +3138|44|5|2|27|25489.08|0.09|0.01|R|F|1994-03-24|1994-03-23|1994-04-18|DELIVER IN PERSON|FOB|counts cajole fluffily carefully special i| +3138|197|8|3|32|35110.08|0.00|0.01|R|F|1994-02-24|1994-05-07|1994-02-28|TAKE BACK RETURN|MAIL|inal foxes affix slyly. fluffily regul| +3138|172|3|4|38|40742.46|0.07|0.04|R|F|1994-02-21|1994-03-21|1994-03-13|COLLECT COD|FOB|lithely fluffily un| +3138|10|1|5|12|10920.12|0.09|0.02|A|F|1994-03-04|1994-04-11|1994-03-21|COLLECT COD|FOB|. bold pinto beans haggl| +3138|44|7|6|25|23601.00|0.05|0.08|A|F|1994-05-19|1994-04-07|1994-06-17|TAKE BACK RETURN|AIR|dolites around the carefully busy the| +3139|40|6|1|46|43241.84|0.08|0.03|R|F|1992-04-28|1992-03-04|1992-05-19|TAKE BACK RETURN|FOB|of the unusual, unusual re| +3140|7|4|1|21|19047.00|0.08|0.02|R|F|1992-04-12|1992-05-31|1992-04-21|NONE|REG AIR| furiously sly excuses according to the| +3140|89|10|2|10|9890.80|0.07|0.01|A|F|1992-05-30|1992-05-09|1992-06-09|COLLECT COD|RAIL|accounts. expres| +3140|133|4|3|28|28927.64|0.06|0.00|R|F|1992-06-08|1992-07-07|1992-07-08|TAKE BACK RETURN|SHIP|lar ideas. slyly ironic d| +3141|177|6|1|32|34469.44|0.06|0.00|N|O|1995-11-21|1995-12-18|1995-11-26|DELIVER IN PERSON|FOB|oxes are quickly about t| +3141|10|7|2|37|33670.37|0.10|0.05|N|O|1996-01-24|1995-12-16|1996-01-27|DELIVER IN PERSON|AIR|press pinto beans. bold accounts boost b| +3141|79|7|3|9|8811.63|0.09|0.02|N|O|1995-11-11|1995-12-10|1995-12-02|DELIVER IN PERSON|MAIL|uickly ironic, pendi| +3141|46|9|4|47|44463.88|0.03|0.01|N|O|1995-11-29|1996-01-13|1995-12-10|TAKE BACK RETURN|TRUCK| are slyly pi| +3142|120|7|1|15|15301.80|0.03|0.08|R|F|1992-08-15|1992-08-18|1992-08-22|DELIVER IN PERSON|AIR|instructions are. ironic packages doz| +3143|90|1|1|22|21781.98|0.02|0.00|A|F|1993-05-11|1993-03-26|1993-05-20|TAKE BACK RETURN|MAIL|l, special instructions nag | +3143|183|4|2|40|43327.20|0.03|0.08|A|F|1993-05-07|1993-03-29|1993-05-17|COLLECT COD|FOB|sly unusual theodolites. slyly ev| +3143|183|4|3|22|23829.96|0.05|0.03|A|F|1993-03-18|1993-05-09|1993-04-14|DELIVER IN PERSON|MAIL|beans. fluf| +3143|66|7|4|46|44438.76|0.05|0.08|R|F|1993-04-19|1993-03-21|1993-05-05|COLLECT COD|REG AIR|low forges haggle. even packages use bli| +3168|60|8|1|46|44162.76|0.08|0.08|R|F|1992-02-14|1992-03-02|1992-03-02|TAKE BACK RETURN|SHIP|y across the express accounts. fluff| +3168|154|5|2|1|1054.15|0.06|0.08|A|F|1992-05-27|1992-03-12|1992-06-09|TAKE BACK RETURN|SHIP|pinto beans. slyly regular courts haggle | +3168|128|3|3|13|13365.56|0.09|0.02|A|F|1992-03-05|1992-04-29|1992-03-15|NONE|SHIP|ironic somas haggle quick| +3168|165|10|4|11|11716.76|0.02|0.05|R|F|1992-04-12|1992-03-17|1992-05-12|COLLECT COD|SHIP|ously furious dependenc| +3169|192|4|1|12|13106.28|0.01|0.04|R|F|1994-01-05|1994-03-18|1994-01-21|COLLECT COD|REG AIR| regular d| +3169|200|3|2|17|18703.40|0.05|0.04|R|F|1994-03-02|1994-01-21|1994-03-03|DELIVER IN PERSON|TRUCK|usly regular packages. ironi| +3169|188|9|3|12|13058.16|0.08|0.07|A|F|1994-04-18|1994-03-12|1994-05-08|TAKE BACK RETURN|FOB|atelets. pac| +3169|105|6|4|26|26132.60|0.10|0.04|R|F|1994-04-08|1994-03-21|1994-04-29|NONE|TRUCK|ter the regular ideas. slyly iro| +3169|108|9|5|6|6048.60|0.09|0.01|A|F|1994-03-24|1994-02-22|1994-04-04|TAKE BACK RETURN|AIR|ular instructions. ca| +3169|177|8|6|46|49549.82|0.02|0.07|A|F|1994-02-01|1994-01-22|1994-02-24|DELIVER IN PERSON|RAIL|thely bold theodolites are fl| +3170|40|6|1|12|11280.48|0.03|0.03|N|O|1998-02-12|1998-01-17|1998-02-24|NONE|TRUCK|ing accounts along the speci| +3170|100|2|2|21|21002.10|0.01|0.00|N|O|1997-12-09|1998-01-31|1997-12-21|DELIVER IN PERSON|MAIL|o beans. carefully final requests dou| +3170|89|10|3|27|26705.16|0.00|0.05|N|O|1998-02-25|1998-01-29|1998-02-27|COLLECT COD|AIR|efully bold foxes. regular, ev| +3170|41|2|4|34|31995.36|0.05|0.04|N|O|1998-02-01|1998-01-11|1998-02-20|TAKE BACK RETURN|TRUCK|s about the fluffily final de| +3170|90|1|5|32|31682.88|0.02|0.04|N|O|1997-11-24|1997-12-12|1997-12-15|COLLECT COD|SHIP|ggle about the furiously r| +3170|110|5|6|43|43434.73|0.08|0.05|N|O|1998-01-05|1998-01-04|1998-01-14|NONE|REG AIR|. express dolphins use sly| +3170|84|5|7|26|25586.08|0.10|0.05|N|O|1998-02-12|1997-12-22|1998-02-28|COLLECT COD|TRUCK|s engage furiously. | +3171|47|4|1|34|32199.36|0.04|0.00|A|F|1993-05-30|1993-05-27|1993-06-06|DELIVER IN PERSON|REG AIR|r the final, even packages. quickly| +3171|139|10|2|50|51956.50|0.01|0.04|A|F|1993-07-19|1993-05-15|1993-07-31|TAKE BACK RETURN|REG AIR|riously final foxes about the ca| +3172|96|9|1|4|3984.36|0.06|0.07|A|F|1992-09-26|1992-08-15|1992-10-20|DELIVER IN PERSON|TRUCK|s are slyly thin package| +3172|148|7|2|43|45070.02|0.05|0.07|R|F|1992-08-22|1992-07-07|1992-08-26|COLLECT COD|MAIL| final packages. | +3172|132|3|3|13|13417.69|0.03|0.01|R|F|1992-07-06|1992-08-06|1992-08-05|DELIVER IN PERSON|MAIL|inal deposits haggle along the| +3172|135|6|4|28|28983.64|0.08|0.04|R|F|1992-07-09|1992-07-14|1992-07-16|NONE|MAIL|regular ideas. packages are furi| +3172|64|5|5|31|29885.86|0.05|0.08|A|F|1992-09-01|1992-08-27|1992-09-23|NONE|SHIP|. slyly regular dependencies haggle quiet| +3173|195|6|1|35|38331.65|0.01|0.08|N|O|1996-09-09|1996-10-15|1996-10-04|TAKE BACK RETURN|RAIL| across the slyly even requests.| +3173|178|7|2|5|5390.85|0.09|0.07|N|O|1996-12-06|1996-09-17|1996-12-07|DELIVER IN PERSON|REG AIR|express depo| +3173|46|9|3|16|15136.64|0.06|0.01|N|O|1996-08-12|1996-09-21|1996-08-22|NONE|SHIP|e special,| +3173|94|5|4|2|1988.18|0.00|0.00|N|O|1996-10-15|1996-11-06|1996-10-18|COLLECT COD|MAIL|ular pearls| +3173|185|6|5|2|2170.36|0.00|0.06|N|O|1996-08-18|1996-09-21|1996-09-07|DELIVER IN PERSON|MAIL|fluffily above t| +3174|186|7|1|6|6517.08|0.04|0.08|N|O|1996-03-13|1996-02-09|1996-03-22|DELIVER IN PERSON|AIR| furiously ironic| +3174|194|7|2|4|4376.76|0.01|0.05|N|O|1995-11-17|1996-01-08|1995-11-27|DELIVER IN PERSON|RAIL|deas sleep thi| +3174|92|4|3|21|20833.89|0.08|0.05|N|O|1996-02-20|1995-12-28|1996-03-17|NONE|MAIL|iously. idly bold theodolites a| +3174|192|6|4|13|14198.47|0.08|0.06|N|O|1996-01-11|1996-01-26|1996-02-01|DELIVER IN PERSON|SHIP|leep quickly? slyly special platelets| +3174|72|2|5|39|37910.73|0.02|0.06|N|O|1995-12-02|1996-02-08|1995-12-12|TAKE BACK RETURN|TRUCK| wake slyly foxes. bold requests p| +3174|120|7|6|8|8160.96|0.07|0.08|N|O|1995-12-07|1996-01-08|1995-12-29|DELIVER IN PERSON|TRUCK|nic deposits among t| +3175|120|10|1|28|28563.36|0.10|0.01|R|F|1994-09-27|1994-10-05|1994-10-04|NONE|FOB|ore the even, silent foxes. b| +3175|1|4|2|38|34238.00|0.01|0.07|R|F|1994-10-10|1994-08-25|1994-10-28|NONE|MAIL|the quickly even dolph| +3175|129|4|3|12|12349.44|0.09|0.07|R|F|1994-10-16|1994-09-15|1994-10-18|NONE|AIR|ter the pending deposits. slyly e| +3175|85|6|4|14|13791.12|0.02|0.05|R|F|1994-10-21|1994-09-05|1994-11-15|NONE|MAIL|nt dependencies are quietly even | +3175|18|8|5|47|43146.47|0.08|0.03|R|F|1994-08-08|1994-09-10|1994-08-21|COLLECT COD|REG AIR| final requests x-r| +3175|175|6|6|44|47307.48|0.01|0.00|R|F|1994-09-26|1994-08-30|1994-10-24|TAKE BACK RETURN|MAIL|are carefully furiously ironic accounts. e| +3175|1|4|7|32|28832.00|0.01|0.02|R|F|1994-09-29|1994-09-20|1994-10-10|TAKE BACK RETURN|SHIP|lites sleep| +3200|116|6|1|17|17273.87|0.10|0.00|N|O|1996-06-06|1996-04-21|1996-06-14|DELIVER IN PERSON|AIR|side of the furiously pendin| +3200|166|1|2|27|28786.32|0.03|0.00|N|O|1996-05-07|1996-05-01|1996-05-09|TAKE BACK RETURN|REG AIR|as haggle furiously against the fluff| +3200|131|2|3|36|37120.68|0.01|0.01|N|O|1996-03-22|1996-03-19|1996-03-30|DELIVER IN PERSON|FOB|f the carefu| +3200|30|9|4|11|10230.33|0.10|0.02|N|O|1996-03-18|1996-03-21|1996-04-14|COLLECT COD|RAIL|osits sleep fur| +3200|198|9|5|16|17571.04|0.05|0.00|N|O|1996-02-28|1996-03-13|1996-03-11|NONE|RAIL|ly against the quiet packages. blith| +3200|175|3|6|25|26879.25|0.10|0.01|N|O|1996-02-08|1996-04-11|1996-03-06|COLLECT COD|FOB| slyly regular hockey players! pinto beans | +3201|46|7|1|11|10406.44|0.10|0.06|A|F|1993-09-27|1993-08-29|1993-10-18|NONE|TRUCK|ing to the furiously expr| +3201|118|5|2|27|27488.97|0.08|0.02|R|F|1993-08-31|1993-08-24|1993-09-08|TAKE BACK RETURN|FOB|deposits are slyly along| +3201|119|6|3|50|50955.50|0.00|0.08|R|F|1993-10-27|1993-09-30|1993-11-16|COLLECT COD|TRUCK| deposits. express, ir| +3202|183|4|1|30|32495.40|0.09|0.02|A|F|1993-03-18|1993-03-10|1993-03-23|COLLECT COD|SHIP|ven platelets. furiously final| +3202|20|4|2|22|20240.44|0.01|0.02|R|F|1993-02-16|1993-02-16|1993-03-16|TAKE BACK RETURN|MAIL|the express packages. fu| +3203|144|5|1|23|24015.22|0.01|0.07|N|O|1998-01-04|1998-01-12|1998-01-24|COLLECT COD|SHIP|uses. fluffily ironic pinto bea| +3203|188|9|2|22|23939.96|0.03|0.03|N|O|1998-02-12|1998-01-01|1998-02-18|TAKE BACK RETURN|REG AIR|e the blithely regular accounts boost f| +3204|12|2|1|10|9120.10|0.10|0.07|R|F|1993-01-27|1993-03-08|1993-01-29|COLLECT COD|SHIP|counts. bold | +3204|7|10|2|39|35373.00|0.10|0.03|R|F|1993-02-11|1993-03-19|1993-02-28|TAKE BACK RETURN|MAIL|sits sleep theodolites. slyly bo| +3205|68|5|1|7|6776.42|0.09|0.00|R|F|1992-07-05|1992-06-17|1992-07-07|NONE|SHIP|ly alongsi| +3205|29|10|2|32|29728.64|0.08|0.03|A|F|1992-06-01|1992-07-10|1992-06-06|TAKE BACK RETURN|RAIL|lar accoun| +3205|103|6|3|38|38117.80|0.10|0.08|A|F|1992-07-31|1992-06-03|1992-08-20|DELIVER IN PERSON|AIR|usly quiet accounts. slyly pending pinto | +3205|56|7|4|10|9560.50|0.01|0.07|A|F|1992-06-18|1992-07-04|1992-07-16|COLLECT COD|RAIL| deposits cajole careful| +3205|70|9|5|18|17461.26|0.03|0.03|A|F|1992-07-04|1992-06-14|1992-08-03|TAKE BACK RETURN|RAIL|symptotes. slyly even deposits ar| +3205|195|8|6|19|20808.61|0.07|0.08|R|F|1992-05-28|1992-05-30|1992-06-05|COLLECT COD|AIR|yly pending packages snooz| +3205|69|8|7|36|34886.16|0.06|0.03|A|F|1992-05-31|1992-06-19|1992-06-03|TAKE BACK RETURN|SHIP|s. ironic platelets above the s| +3206|176|4|1|1|1076.17|0.07|0.05|N|O|1996-11-22|1996-10-16|1996-12-07|TAKE BACK RETURN|FOB|y unusual foxes cajole ab| +3206|111|5|2|37|37411.07|0.07|0.01|N|O|1996-09-06|1996-10-31|1996-09-25|COLLECT COD|SHIP| quick theodolites hagg| +3206|186|7|3|24|26068.32|0.00|0.08|N|O|1996-08-25|1996-10-01|1996-09-04|COLLECT COD|TRUCK|encies sleep deposits--| +3207|113|3|1|2|2026.22|0.10|0.03|N|O|1998-06-15|1998-04-20|1998-06-21|COLLECT COD|MAIL|among the ironic, even packages | +3207|71|9|2|42|40784.94|0.00|0.00|N|O|1998-05-02|1998-05-10|1998-06-01|NONE|SHIP|to the quickly special accounts? ironically| +3207|152|7|3|17|17886.55|0.03|0.04|N|O|1998-03-27|1998-04-06|1998-03-28|COLLECT COD|RAIL|eep against the instructions. gifts hag| +3207|19|6|4|32|29408.32|0.00|0.03|N|O|1998-06-17|1998-04-26|1998-07-07|TAKE BACK RETURN|SHIP|y across the slyly express foxes. bl| +3207|83|4|5|8|7864.64|0.00|0.06|N|O|1998-06-13|1998-04-26|1998-07-11|COLLECT COD|SHIP|y. final pint| +3207|134|5|6|32|33092.16|0.03|0.05|N|O|1998-04-19|1998-05-01|1998-05-08|COLLECT COD|FOB|l deposits wake beyond the carefully| +3232|14|5|1|22|20108.22|0.10|0.01|A|F|1992-11-30|1992-12-09|1992-12-04|NONE|RAIL|thely. furio| +3232|135|1|2|34|35194.42|0.07|0.04|R|F|1993-01-09|1992-11-14|1993-02-03|NONE|SHIP|old packages integrate quickly | +3232|181|2|3|3|3243.54|0.04|0.06|R|F|1992-12-14|1992-12-11|1992-12-29|DELIVER IN PERSON|FOB|ily blithely ironic acco| +3233|51|2|1|23|21874.15|0.04|0.05|A|F|1994-12-07|1995-01-11|1994-12-26|NONE|AIR|pending instructions use after the carefu| +3233|154|6|2|6|6324.90|0.02|0.08|A|F|1994-12-06|1994-12-05|1994-12-07|TAKE BACK RETURN|REG AIR|requests are quickly above the slyly p| +3233|100|4|3|2|2000.20|0.04|0.06|R|F|1995-01-03|1995-01-02|1995-01-21|TAKE BACK RETURN|AIR| across the bold packages| +3233|9|2|4|25|22725.00|0.04|0.07|A|F|1994-11-24|1995-01-07|1994-12-11|NONE|RAIL|oss the pl| +3234|79|10|1|45|44058.15|0.01|0.04|N|O|1996-05-15|1996-05-09|1996-06-02|DELIVER IN PERSON|TRUCK| express packages are carefully. f| +3234|84|5|2|23|22633.84|0.03|0.00|N|O|1996-05-29|1996-05-15|1996-06-17|DELIVER IN PERSON|AIR|d-- fluffily special packag| +3234|75|4|3|16|15601.12|0.06|0.05|N|O|1996-06-10|1996-05-30|1996-06-18|COLLECT COD|RAIL|ithely ironic accounts wake along t| +3234|122|1|4|50|51106.00|0.09|0.05|N|O|1996-06-11|1996-05-19|1996-06-18|NONE|MAIL|ly regular ideas according to the regula| +3234|165|2|5|14|14912.24|0.01|0.07|N|O|1996-04-06|1996-05-30|1996-04-13|NONE|REG AIR|lithely regular f| +3235|109|2|1|9|9081.90|0.07|0.00|N|O|1995-11-17|1995-12-24|1995-11-30|COLLECT COD|AIR|l courts sleep quickly slyly | +3235|95|6|2|43|42788.87|0.10|0.07|N|O|1995-12-25|1996-01-23|1996-01-09|COLLECT COD|MAIL|ckly final instru| +3235|138|9|3|29|30105.77|0.06|0.06|N|O|1996-01-28|1995-12-26|1996-02-12|DELIVER IN PERSON|RAIL|e fluffy pinto bea| +3235|178|9|4|23|24797.91|0.00|0.01|N|O|1996-02-16|1996-01-05|1996-03-07|DELIVER IN PERSON|SHIP|ldly ironic pinto beans| +3236|117|4|1|10|10171.10|0.06|0.05|N|O|1996-11-15|1996-12-14|1996-11-29|TAKE BACK RETURN|AIR|arefully. fluffily reg| +3236|122|7|2|21|21464.52|0.01|0.07|N|O|1996-12-23|1996-12-12|1997-01-21|NONE|AIR| final pinto | +3236|118|2|3|7|7126.77|0.07|0.01|N|O|1996-12-27|1996-12-18|1997-01-24|DELIVER IN PERSON|SHIP|dolites. slyly unus| +3237|11|5|1|11|10021.11|0.02|0.07|A|F|1992-08-03|1992-07-31|1992-08-13|TAKE BACK RETURN|AIR|es. permanently express platelets besid| +3238|72|3|1|12|11664.84|0.06|0.01|R|F|1993-03-06|1993-05-08|1993-04-01|DELIVER IN PERSON|AIR|ackages affix furiously. furiously bol| +3238|173|2|2|26|27902.42|0.01|0.06|A|F|1993-02-25|1993-04-04|1993-03-20|TAKE BACK RETURN|REG AIR|g accounts sleep furiously ironic attai| +3238|81|2|3|1|981.08|0.00|0.04|R|F|1993-05-17|1993-04-18|1993-05-27|NONE|SHIP|wake alongs| +3239|45|8|1|50|47252.00|0.05|0.01|N|O|1998-02-09|1998-04-02|1998-02-22|NONE|FOB|d blithely stea| +3239|45|8|2|43|40636.72|0.01|0.06|N|O|1998-01-15|1998-03-12|1998-01-29|COLLECT COD|REG AIR|y. bold pinto beans use | +3239|13|7|3|13|11869.13|0.01|0.05|N|O|1998-02-10|1998-02-19|1998-02-25|DELIVER IN PERSON|MAIL|r deposits solve fluf| +3239|195|6|4|26|28474.94|0.03|0.05|N|O|1998-01-21|1998-03-21|1998-02-08|DELIVER IN PERSON|SHIP|ngly pending platelets are fluff| +3239|12|9|5|31|28272.31|0.10|0.08|N|O|1998-04-14|1998-03-24|1998-04-17|DELIVER IN PERSON|FOB|foxes. pendin| +3264|200|1|1|39|42907.80|0.06|0.06|N|O|1996-11-07|1996-12-12|1996-11-20|TAKE BACK RETURN|REG AIR|sleep carefully after the slyly final| +3264|131|2|2|34|35058.42|0.00|0.01|N|O|1997-01-03|1997-01-06|1997-01-29|TAKE BACK RETURN|REG AIR|rns haggle carefully. blit| +3264|125|8|3|11|11276.32|0.09|0.03|N|O|1996-12-11|1996-12-19|1996-12-15|DELIVER IN PERSON|SHIP|regular packages| +3264|109|10|4|24|24218.40|0.09|0.07|N|O|1997-01-07|1996-12-13|1997-01-11|TAKE BACK RETURN|RAIL|ctions. quick| +3264|63|4|5|6|5778.36|0.04|0.03|N|O|1996-11-10|1996-12-05|1996-11-22|TAKE BACK RETURN|SHIP|press packages. ironical| +3264|141|2|6|43|44769.02|0.06|0.06|N|O|1997-01-17|1997-01-24|1997-02-01|TAKE BACK RETURN|TRUCK|leep at the blithely bold| +3265|25|4|1|8|7400.16|0.06|0.02|A|F|1992-09-01|1992-09-12|1992-09-27|DELIVER IN PERSON|TRUCK|thely ironic requests sleep slyly-- i| +3265|72|2|2|7|6804.49|0.09|0.00|R|F|1992-09-16|1992-09-04|1992-10-14|DELIVER IN PERSON|MAIL|he forges. fluffily regular asym| +3265|191|4|3|28|30553.32|0.09|0.08|A|F|1992-10-22|1992-08-23|1992-10-25|NONE|RAIL|n requests. quickly final dinos| +3266|64|1|1|31|29885.86|0.09|0.02|N|O|1995-06-19|1995-05-04|1995-07-06|COLLECT COD|MAIL|grate among the quickly express deposits| +3266|38|4|2|43|40335.29|0.06|0.07|R|F|1995-05-04|1995-05-30|1995-05-11|COLLECT COD|AIR|ular asymptotes use careful| +3267|185|6|1|33|35810.94|0.06|0.01|N|O|1997-03-30|1997-03-25|1997-04-23|TAKE BACK RETURN|AIR|es boost. | +3268|96|7|1|1|996.09|0.06|0.08|A|F|1994-09-12|1994-08-31|1994-09-16|NONE|TRUCK|. ironic, bold requests use carefull| +3268|42|9|2|40|37681.60|0.08|0.01|R|F|1994-06-30|1994-08-22|1994-07-25|COLLECT COD|FOB|ly. bold, eve| +3269|161|10|1|40|42446.40|0.02|0.07|N|O|1996-06-11|1996-05-06|1996-06-15|DELIVER IN PERSON|TRUCK|es. pending d| +3269|38|4|2|46|43149.38|0.00|0.02|N|O|1996-04-21|1996-04-12|1996-05-10|DELIVER IN PERSON|MAIL|final asymptotes nag| +3269|44|3|3|39|36817.56|0.02|0.03|N|O|1996-03-13|1996-05-26|1996-03-19|COLLECT COD|MAIL|he express packages?| +3269|83|4|4|37|36373.96|0.07|0.05|N|O|1996-06-14|1996-04-27|1996-07-07|NONE|MAIL|egular requests. carefully un| +3269|93|7|5|42|41709.78|0.09|0.05|N|O|1996-03-19|1996-04-24|1996-04-18|COLLECT COD|TRUCK| the special packages. | +3269|131|7|6|16|16498.08|0.01|0.08|N|O|1996-03-03|1996-04-06|1996-03-06|NONE|RAIL|s cajole. silent deposits are f| +3270|35|1|1|11|10285.33|0.07|0.06|N|O|1997-07-29|1997-08-11|1997-08-05|TAKE BACK RETURN|AIR| solve at the regular deposits. | +3270|38|4|2|44|41273.32|0.10|0.05|N|O|1997-07-20|1997-08-15|1997-08-04|DELIVER IN PERSON|SHIP| accounts. carefully even | +3270|65|4|3|20|19301.20|0.01|0.02|N|O|1997-08-26|1997-07-31|1997-08-30|DELIVER IN PERSON|FOB|en accounts among the c| +3270|189|10|4|29|31586.22|0.06|0.05|N|O|1997-07-01|1997-07-23|1997-07-10|TAKE BACK RETURN|MAIL|sly regular asymptotes. slyly dog| +3270|34|10|5|32|29888.96|0.03|0.00|N|O|1997-09-23|1997-08-17|1997-09-27|NONE|REG AIR|promise carefully.| +3270|57|5|6|29|27754.45|0.01|0.04|N|O|1997-08-22|1997-08-17|1997-09-06|COLLECT COD|RAIL|ptotes nag above the quickly bold deposits| +3270|117|1|7|9|9153.99|0.06|0.08|N|O|1997-08-14|1997-08-11|1997-09-09|DELIVER IN PERSON|SHIP|ual packages| +3271|57|9|1|30|28711.50|0.01|0.04|A|F|1992-01-16|1992-03-20|1992-01-17|DELIVER IN PERSON|AIR|r the unusual Tiresia| +3271|54|5|2|18|17172.90|0.09|0.06|R|F|1992-05-01|1992-03-28|1992-05-29|DELIVER IN PERSON|FOB| packages eat around the furiously regul| +3271|95|6|3|14|13931.26|0.05|0.01|A|F|1992-02-24|1992-02-14|1992-03-23|NONE|AIR|ending, even packa| +3271|64|1|4|29|27957.74|0.07|0.04|A|F|1992-03-10|1992-02-05|1992-03-14|COLLECT COD|MAIL|lar instructions. carefully regular| +3296|84|5|1|12|11808.96|0.06|0.07|R|F|1994-12-08|1994-12-14|1994-12-24|COLLECT COD|AIR|y about the slyly bold pinto bea| +3296|149|8|2|31|32523.34|0.08|0.00|R|F|1995-01-26|1994-12-25|1995-02-16|NONE|REG AIR|ainst the furi| +3296|185|6|3|29|31470.22|0.02|0.04|A|F|1995-01-12|1994-11-26|1995-02-06|DELIVER IN PERSON|SHIP|ss ideas are reg| +3296|140|1|4|47|48886.58|0.06|0.00|A|F|1994-11-08|1994-12-20|1994-11-30|NONE|FOB|egular deposits. quic| +3296|177|6|5|16|17234.72|0.06|0.02|R|F|1995-01-11|1994-12-27|1995-01-12|DELIVER IN PERSON|SHIP|kages cajole carefully | +3296|197|1|6|40|43887.60|0.00|0.04|A|F|1994-12-28|1994-12-08|1995-01-13|COLLECT COD|REG AIR|ronic ideas across the| +3296|36|2|7|6|5616.18|0.02|0.01|R|F|1995-01-03|1994-12-23|1995-01-27|TAKE BACK RETURN|AIR|carefully fur| +3297|134|10|1|10|10341.30|0.10|0.04|A|F|1992-12-14|1993-01-21|1992-12-26|NONE|SHIP|ironic idea| +3298|149|6|1|9|9442.26|0.01|0.06|N|O|1996-08-15|1996-05-24|1996-09-12|COLLECT COD|REG AIR|ly final accou| +3298|186|7|2|27|29326.86|0.06|0.06|N|O|1996-07-10|1996-05-21|1996-07-15|DELIVER IN PERSON|FOB|lar packages. regular deposit| +3298|29|2|3|25|23225.50|0.10|0.08|N|O|1996-06-30|1996-05-31|1996-07-23|COLLECT COD|SHIP|ly express f| +3298|191|5|4|1|1091.19|0.10|0.03|N|O|1996-07-31|1996-05-23|1996-08-24|TAKE BACK RETURN|FOB|refully regular requ| +3299|183|4|1|40|43327.20|0.03|0.02|A|F|1994-03-21|1994-03-23|1994-04-12|COLLECT COD|AIR|lyly even request| +3300|129|4|1|3|3087.36|0.07|0.02|N|O|1995-11-01|1995-10-02|1995-11-20|NONE|REG AIR|g according to the dugouts. caref| +3300|149|10|2|23|24130.22|0.02|0.02|N|O|1995-08-17|1995-09-03|1995-09-04|COLLECT COD|TRUCK|he fluffily final a| +3301|169|8|1|45|48112.20|0.04|0.05|A|F|1994-11-19|1994-10-27|1994-11-24|TAKE BACK RETURN|FOB|nusual, final excuses after the entici| +3302|36|2|1|45|42121.35|0.09|0.00|N|O|1996-01-24|1995-12-16|1996-02-13|COLLECT COD|FOB|counts use quickl| +3303|184|5|1|25|27104.50|0.06|0.01|N|O|1998-03-25|1998-01-31|1998-04-12|NONE|SHIP|lly regular pi| +3303|21|2|2|15|13815.30|0.04|0.06|N|O|1998-01-29|1998-01-22|1998-02-21|COLLECT COD|SHIP| detect sly| +3303|99|10|3|37|36966.33|0.05|0.02|N|O|1998-02-16|1998-03-07|1998-02-18|TAKE BACK RETURN|TRUCK| carefully ironic asympt| +3303|36|2|4|26|24336.78|0.09|0.00|N|O|1998-01-18|1998-03-11|1998-02-11|DELIVER IN PERSON|REG AIR|ickly permanent requests w| +3328|113|7|1|6|6078.66|0.03|0.08|A|F|1993-03-07|1993-01-25|1993-03-29|COLLECT COD|TRUCK|ffily even instructions detect b| +3328|5|2|2|23|20815.00|0.01|0.06|R|F|1993-01-12|1993-02-07|1993-01-30|TAKE BACK RETURN|MAIL|y. careful| +3328|139|10|3|44|45721.72|0.05|0.00|R|F|1992-12-03|1992-12-19|1992-12-09|TAKE BACK RETURN|FOB|dly quickly final foxes? re| +3328|95|9|4|42|41793.78|0.01|0.05|R|F|1992-11-24|1992-12-20|1992-12-06|DELIVER IN PERSON|AIR|ronic requests| +3328|131|7|5|25|25778.25|0.05|0.00|R|F|1993-01-28|1993-01-04|1993-01-31|NONE|RAIL|e unusual, r| +3329|138|4|1|36|37372.68|0.09|0.08|N|O|1995-08-06|1995-08-03|1995-08-14|DELIVER IN PERSON|TRUCK|ts at the re| +3329|6|3|2|9|8154.00|0.00|0.02|N|O|1995-07-24|1995-08-02|1995-08-01|COLLECT COD|MAIL|lly final depo| +3329|123|4|3|1|1023.12|0.04|0.08|N|O|1995-08-22|1995-09-28|1995-09-09|COLLECT COD|REG AIR|regular packages are carefull| +3330|20|7|1|49|45080.98|0.05|0.01|R|F|1995-03-02|1995-03-03|1995-03-16|DELIVER IN PERSON|TRUCK|haggle carefully alongside of the bold r| +3331|64|9|1|9|8676.54|0.08|0.07|A|F|1993-07-18|1993-07-03|1993-08-16|TAKE BACK RETURN|AIR|odolites. bold accounts| +3331|21|2|2|38|34998.76|0.06|0.04|R|F|1993-07-24|1993-06-22|1993-08-23|NONE|AIR|ymptotes haggle across the ca| +3331|3|10|3|26|23478.00|0.09|0.05|A|F|1993-08-05|1993-07-17|1993-08-29|DELIVER IN PERSON|MAIL|p asymptotes. carefully unusual in| +3332|84|5|1|28|27554.24|0.10|0.02|R|F|1994-12-30|1995-01-16|1995-01-16|COLLECT COD|FOB|s against the carefully special multipl| +3332|136|2|2|21|21758.73|0.08|0.04|R|F|1995-02-04|1995-01-08|1995-02-06|COLLECT COD|MAIL| quick packages sle| +3332|134|5|3|27|27921.51|0.03|0.02|A|F|1994-12-10|1995-01-14|1994-12-11|TAKE BACK RETURN|FOB|ording to the slyly regula| +3333|150|9|1|27|28354.05|0.06|0.08|A|F|1992-12-06|1992-10-26|1992-12-07|COLLECT COD|SHIP|s dazzle fluffil| +3333|199|3|2|36|39570.84|0.08|0.07|R|F|1992-11-20|1992-11-06|1992-12-16|TAKE BACK RETURN|FOB|foxes sleep neve| +3333|108|1|3|38|38307.80|0.05|0.05|A|F|1992-10-30|1992-11-03|1992-11-04|NONE|MAIL|ccounts promise bl| +3333|113|4|4|49|49642.39|0.07|0.07|R|F|1992-10-02|1992-11-30|1992-10-12|DELIVER IN PERSON|MAIL|riously ironic r| +3333|43|2|5|45|42436.80|0.07|0.08|A|F|1992-10-04|1992-11-08|1992-10-27|COLLECT COD|SHIP|dolites. quickly r| +3334|187|8|1|20|21743.60|0.04|0.03|N|O|1996-05-21|1996-04-08|1996-05-26|TAKE BACK RETURN|AIR|uses nag furiously. instructions are ca| +3334|190|1|2|7|7631.33|0.09|0.07|N|O|1996-04-28|1996-04-08|1996-05-25|NONE|SHIP|nts sublate slyly express pack| +3335|105|10|1|13|13066.30|0.06|0.07|N|O|1996-01-20|1995-12-20|1996-02-09|COLLECT COD|REG AIR|out the special asymptotes| +3335|31|2|2|44|40965.32|0.07|0.02|N|O|1996-01-05|1995-12-25|1996-01-18|DELIVER IN PERSON|SHIP|r packages cajole ac| +3335|140|6|3|16|16642.24|0.01|0.06|N|O|1995-10-18|1995-12-08|1995-11-03|DELIVER IN PERSON|SHIP|g packages. carefully regular reque| +3335|90|1|4|47|46534.23|0.10|0.03|N|O|1995-12-02|1995-11-19|1995-12-27|NONE|MAIL| quickly special ideas.| +3360|174|4|1|31|33299.27|0.08|0.04|N|O|1998-04-24|1998-04-12|1998-05-23|COLLECT COD|REG AIR|quests. carefully even deposits wake acros| +3360|91|3|2|29|28741.61|0.00|0.06|N|O|1998-04-15|1998-02-25|1998-05-13|TAKE BACK RETURN|FOB|press asymptotes. furiously final | +3360|82|3|3|39|38301.12|0.08|0.03|N|O|1998-04-09|1998-04-20|1998-05-05|DELIVER IN PERSON|REG AIR|s. blithely express pinto bean| +3360|117|7|4|29|29496.19|0.10|0.01|N|O|1998-05-19|1998-03-03|1998-06-09|TAKE BACK RETURN|FOB|hely gifts. spe| +3360|58|6|5|4|3832.20|0.08|0.07|N|O|1998-02-27|1998-03-23|1998-03-28|COLLECT COD|SHIP|ly busy inst| +3360|71|1|6|42|40784.94|0.04|0.01|N|O|1998-05-07|1998-04-18|1998-06-04|DELIVER IN PERSON|FOB|ages cajole. pending, | +3361|144|5|1|6|6264.84|0.02|0.02|R|F|1992-10-02|1992-10-25|1992-10-05|DELIVER IN PERSON|FOB| packages sleep. furiously unus| +3361|171|10|2|33|35348.61|0.01|0.02|R|F|1992-11-09|1992-10-15|1992-11-11|TAKE BACK RETURN|MAIL|uriously ironic accounts. ironic, ir| +3361|191|5|3|31|33826.89|0.06|0.04|R|F|1992-08-29|1992-10-13|1992-09-08|NONE|FOB|ts. pending, regular accounts sleep fur| +3362|22|5|1|14|12908.28|0.06|0.05|N|O|1995-08-01|1995-09-06|1995-08-22|NONE|FOB|even Tires| +3362|195|6|2|41|44902.79|0.05|0.03|N|O|1995-10-31|1995-09-04|1995-11-17|COLLECT COD|REG AIR|ake alongside of the | +3362|115|9|3|40|40604.40|0.05|0.06|N|O|1995-08-19|1995-10-17|1995-09-05|TAKE BACK RETURN|FOB|packages haggle furi| +3362|2|7|4|3|2706.00|0.03|0.01|N|O|1995-08-26|1995-09-02|1995-09-17|NONE|SHIP|its cajole blithely excuses. de| +3362|138|9|5|36|37372.68|0.06|0.00|N|O|1995-10-05|1995-08-28|1995-11-03|TAKE BACK RETURN|RAIL|es against the quickly permanent pint| +3362|188|9|6|46|50056.28|0.09|0.05|N|O|1995-08-02|1995-10-12|1995-08-28|COLLECT COD|REG AIR|ly bold packages. regular deposits cajol| +3363|10|3|1|42|38220.42|0.00|0.08|N|O|1995-11-09|1995-11-25|1995-11-15|TAKE BACK RETURN|RAIL| blithely final ideas nag after| +3363|191|4|2|21|22914.99|0.08|0.08|N|O|1995-12-10|1995-10-28|1995-12-28|COLLECT COD|RAIL|he regular, brave deposits. f| +3363|159|7|3|2|2118.30|0.01|0.07|N|O|1996-01-22|1995-12-01|1996-02-18|TAKE BACK RETURN|SHIP|uickly bold ide| +3363|113|3|4|20|20262.20|0.07|0.06|N|O|1995-12-11|1995-11-15|1995-12-21|COLLECT COD|MAIL|carefully quiet excuses wake. sl| +3363|200|4|5|4|4400.80|0.00|0.08|N|O|1995-10-30|1995-11-17|1995-11-22|COLLECT COD|FOB| ironic dependencie| +3364|90|1|1|49|48514.41|0.03|0.05|N|O|1997-09-17|1997-08-23|1997-10-06|NONE|SHIP|d accounts? caref| +3364|111|2|2|38|38422.18|0.02|0.02|N|O|1997-08-30|1997-09-12|1997-09-27|COLLECT COD|REG AIR| slyly express| +3364|156|4|3|10|10561.50|0.00|0.01|N|O|1997-08-10|1997-08-24|1997-08-15|TAKE BACK RETURN|SHIP|g the accounts. final, busy accounts wi| +3364|160|5|4|7|7421.12|0.10|0.05|N|O|1997-07-09|1997-08-01|1997-07-16|NONE|TRUCK|furiously regular ideas haggle furiously b| +3364|81|2|5|3|2943.24|0.01|0.00|N|O|1997-10-19|1997-08-15|1997-10-28|TAKE BACK RETURN|TRUCK|c theodolites. blithely ir| +3365|151|6|1|37|38892.55|0.02|0.08|R|F|1994-12-22|1995-02-07|1995-01-20|TAKE BACK RETURN|SHIP|requests. quickly pending instructions a| +3365|167|2|2|37|39484.92|0.07|0.08|A|F|1994-11-24|1995-01-09|1994-11-27|NONE|REG AIR|oze blithely. furiously ironic theodolit| +3365|115|6|3|13|13196.43|0.09|0.02|R|F|1995-02-25|1995-01-31|1995-03-16|NONE|RAIL|pths wake r| +3365|176|4|4|49|52732.33|0.02|0.07|R|F|1995-01-03|1995-01-01|1995-01-18|COLLECT COD|MAIL|lyly unusual asymptotes. final| +3365|16|3|5|2|1832.02|0.00|0.03|R|F|1995-02-04|1994-12-30|1995-03-06|TAKE BACK RETURN|FOB|es cajole fluffily pe| +3365|126|5|6|24|24626.88|0.01|0.00|R|F|1995-02-27|1995-01-09|1995-03-27|DELIVER IN PERSON|REG AIR|into beans? carefully regula| +3366|40|1|1|4|3760.16|0.07|0.01|N|O|1997-05-20|1997-06-25|1997-06-03|DELIVER IN PERSON|AIR| carefully about | +3366|136|2|2|9|9325.17|0.00|0.08|N|O|1997-06-02|1997-07-05|1997-06-26|COLLECT COD|REG AIR|ackages sleep carefully across the bli| +3367|41|10|1|27|25408.08|0.01|0.03|A|F|1993-04-13|1993-03-16|1993-04-26|NONE|RAIL|kly even instructions caj| +3367|141|10|2|34|35398.76|0.04|0.08|A|F|1993-03-30|1993-02-23|1993-04-11|COLLECT COD|MAIL| accounts wake slyly | +3367|120|7|3|38|38764.56|0.03|0.03|R|F|1993-03-13|1993-02-12|1993-03-31|NONE|RAIL|even packages sleep blithely slyly expr| +3392|171|10|1|40|42846.80|0.01|0.01|N|O|1996-02-18|1995-12-16|1996-02-26|COLLECT COD|MAIL|ress instructions affix carefully. fur| +3392|123|2|2|13|13300.56|0.09|0.02|N|O|1995-11-26|1996-01-17|1995-12-01|NONE|MAIL|across the fluffily bold deposits.| +3392|127|10|3|34|34922.08|0.10|0.08|N|O|1996-01-20|1996-01-21|1996-01-24|DELIVER IN PERSON|MAIL|e carefully even braids. | +3392|124|3|4|7|7168.84|0.08|0.05|N|O|1995-12-07|1996-01-09|1995-12-29|TAKE BACK RETURN|RAIL|as. express, final accounts dou| +3393|117|7|1|16|16273.76|0.01|0.00|N|O|1995-07-17|1995-08-19|1995-08-04|COLLECT COD|TRUCK|uses. instructions after the blithely | +3393|125|4|2|44|45105.28|0.08|0.04|N|O|1995-10-16|1995-08-05|1995-11-01|NONE|AIR|ld requests hag| +3393|97|1|3|25|24927.25|0.07|0.02|N|O|1995-10-17|1995-08-12|1995-11-11|DELIVER IN PERSON|MAIL|ng excuses| +3393|72|2|4|48|46659.36|0.06|0.06|N|O|1995-07-12|1995-09-15|1995-08-02|NONE|FOB| blithely final reques| +3393|178|7|5|37|39892.29|0.07|0.02|N|O|1995-10-16|1995-08-19|1995-10-19|COLLECT COD|AIR|ss the slyly ironic pinto beans. ironic,| +3393|62|7|6|17|16355.02|0.04|0.01|N|O|1995-08-15|1995-09-07|1995-09-10|COLLECT COD|MAIL|kly ironic deposits could| +3394|155|6|1|33|34819.95|0.07|0.08|N|O|1996-08-07|1996-07-17|1996-09-02|TAKE BACK RETURN|SHIP|ideas alongside of th| +3394|146|3|2|43|44984.02|0.08|0.03|N|O|1996-08-23|1996-07-20|1996-08-25|COLLECT COD|RAIL|hockey players. slyly regular requests afte| +3394|88|9|3|26|25690.08|0.01|0.00|N|O|1996-08-08|1996-06-12|1996-09-05|TAKE BACK RETURN|RAIL|its use furiously. even, even account| +3394|81|2|4|14|13735.12|0.08|0.00|N|O|1996-06-02|1996-07-02|1996-06-19|COLLECT COD|MAIL|e furiously final theodolites. furio| +3394|127|8|5|30|30813.60|0.04|0.06|N|O|1996-05-12|1996-07-24|1996-05-19|COLLECT COD|REG AIR|t ideas according to the fluffily iro| +3394|184|5|6|14|15178.52|0.05|0.05|N|O|1996-06-18|1996-06-24|1996-07-17|NONE|REG AIR|arefully regular do| +3395|142|3|1|21|21884.94|0.03|0.06|R|F|1994-12-19|1995-01-13|1994-12-25|TAKE BACK RETURN|SHIP| careful dep| +3395|36|2|2|38|35569.14|0.01|0.07|R|F|1995-01-13|1995-01-13|1995-01-25|COLLECT COD|SHIP| silent accounts are blithely| +3395|43|4|3|43|40550.72|0.06|0.07|A|F|1994-12-13|1995-01-07|1994-12-14|COLLECT COD|AIR|ckages above the furiously regu| +3395|122|1|4|39|39862.68|0.05|0.07|R|F|1994-12-03|1995-01-17|1994-12-10|NONE|AIR|riously unusual theodolites. fur| +3396|128|7|1|34|34956.08|0.00|0.06|A|F|1994-05-30|1994-08-16|1994-06-11|NONE|AIR|. slyly unusual packages wak| +3396|49|6|2|43|40808.72|0.03|0.08|A|F|1994-07-03|1994-08-09|1994-07-14|TAKE BACK RETURN|MAIL|cial packages cajole blithely around the | +3396|138|4|3|9|9343.17|0.01|0.06|R|F|1994-07-01|1994-08-18|1994-07-21|DELIVER IN PERSON|AIR|usly special foxes. accounts wake careful| +3396|75|3|4|32|31202.24|0.06|0.02|R|F|1994-08-07|1994-08-10|1994-09-05|COLLECT COD|TRUCK|osits are slyly. final, bold foxes s| +3396|126|5|5|27|27705.24|0.02|0.01|A|F|1994-09-14|1994-07-26|1994-09-28|DELIVER IN PERSON|FOB| theodolites | +3396|39|10|6|18|16902.54|0.10|0.00|A|F|1994-07-27|1994-06-26|1994-08-25|TAKE BACK RETURN|REG AIR|l requests haggle furiously along the fur| +3396|198|2|7|31|34043.89|0.05|0.06|A|F|1994-06-07|1994-06-23|1994-06-19|TAKE BACK RETURN|REG AIR|l, express pinto beans. quic| +3397|195|8|1|8|8761.52|0.07|0.01|A|F|1994-08-05|1994-08-11|1994-08-08|DELIVER IN PERSON|RAIL|y final foxes| +3397|13|3|2|11|10043.11|0.00|0.07|A|F|1994-07-29|1994-09-18|1994-08-12|DELIVER IN PERSON|REG AIR|iously careful packages. s| +3397|184|5|3|1|1084.18|0.07|0.05|R|F|1994-08-03|1994-07-30|1994-08-28|NONE|RAIL| regular packag| +3397|86|7|4|33|32540.64|0.05|0.01|R|F|1994-09-04|1994-08-06|1994-09-22|COLLECT COD|RAIL|gular accounts. blithely re| +3397|132|3|5|28|28899.64|0.05|0.05|R|F|1994-07-13|1994-08-26|1994-07-17|NONE|TRUCK|counts around the final reques| +3398|173|4|1|1|1073.17|0.01|0.08|N|O|1996-11-22|1996-11-16|1996-12-09|COLLECT COD|MAIL| blithely final deposits.| +3399|134|5|1|28|28955.64|0.09|0.05|N|O|1995-06-29|1995-05-19|1995-07-12|COLLECT COD|AIR|oggedly final theodolites grow. fi| +3399|55|6|2|8|7640.40|0.01|0.05|A|F|1995-05-15|1995-04-19|1995-06-05|COLLECT COD|TRUCK|s use carefully carefully ir| +3399|67|4|3|3|2901.18|0.03|0.00|N|F|1995-06-16|1995-04-04|1995-06-23|NONE|SHIP|hely pending dugouts | +3399|14|5|4|21|19194.21|0.09|0.06|A|F|1995-03-12|1995-05-18|1995-03-28|TAKE BACK RETURN|MAIL|se final courts. exc| +3424|181|2|1|39|42166.02|0.06|0.07|N|O|1996-11-03|1996-11-08|1996-11-23|DELIVER IN PERSON|MAIL|bits boost closely slyly p| +3425|120|1|1|11|11221.32|0.03|0.08|N|O|1996-04-24|1996-05-29|1996-05-23|DELIVER IN PERSON|FOB|ckly final deposits use quickly?| +3425|79|7|2|37|36225.59|0.06|0.03|N|O|1996-06-04|1996-05-09|1996-06-12|NONE|SHIP|as sleep carefully into the caref| +3425|14|4|3|8|7312.08|0.06|0.08|N|O|1996-07-22|1996-06-07|1996-07-26|TAKE BACK RETURN|AIR|iously regular theodolites wake. s| +3425|19|10|4|37|34003.37|0.04|0.01|N|O|1996-07-10|1996-05-10|1996-08-02|NONE|SHIP|ngside of the furiously thin dol| +3425|79|9|5|48|46995.36|0.08|0.04|N|O|1996-04-14|1996-05-25|1996-04-23|TAKE BACK RETURN|AIR|uctions wake fluffily. care| +3425|148|9|6|24|25155.36|0.05|0.04|N|O|1996-04-22|1996-06-24|1996-04-25|TAKE BACK RETURN|AIR|ajole blithely sl| +3426|110|5|1|20|20202.20|0.05|0.04|N|O|1996-11-10|1996-12-24|1996-12-01|COLLECT COD|FOB|sits cajole blit| +3426|14|4|2|19|17366.19|0.10|0.08|N|O|1996-11-02|1997-01-13|1996-11-15|DELIVER IN PERSON|RAIL|slyly special packages oug| +3426|67|6|3|19|18374.14|0.08|0.05|N|O|1996-12-07|1996-12-15|1996-12-14|DELIVER IN PERSON|FOB|c accounts cajole carefu| +3426|6|7|4|9|8154.00|0.09|0.05|N|O|1996-12-24|1997-01-14|1997-01-13|NONE|FOB|pecial theodolites haggle fluf| +3426|49|6|5|31|29420.24|0.07|0.08|N|O|1996-11-11|1996-12-10|1996-12-10|DELIVER IN PERSON|SHIP| even sentiment| +3427|54|5|1|41|39116.05|0.10|0.01|N|O|1997-09-11|1997-07-03|1997-10-04|COLLECT COD|RAIL|s the carefully| +3427|189|10|2|24|26140.32|0.02|0.04|N|O|1997-07-01|1997-07-28|1997-07-30|NONE|SHIP|y bold, sly deposits. pendi| +3427|139|5|3|40|41565.20|0.06|0.05|N|O|1997-06-12|1997-08-19|1997-06-23|COLLECT COD|MAIL|patterns cajole ca| +3427|119|6|4|31|31592.41|0.08|0.04|N|O|1997-08-12|1997-07-26|1997-08-25|COLLECT COD|RAIL|s are carefull| +3428|198|9|1|4|4392.76|0.00|0.03|N|O|1996-05-09|1996-06-13|1996-06-02|NONE|REG AIR|sly pending requests int| +3428|118|9|2|35|35633.85|0.02|0.03|N|O|1996-05-01|1996-06-07|1996-05-20|COLLECT COD|TRUCK|ly regular pinto beans sleep| +3428|136|7|3|47|48698.11|0.07|0.05|N|O|1996-04-16|1996-06-08|1996-05-05|NONE|REG AIR|y final pinto | +3429|137|8|1|48|49782.24|0.06|0.02|N|O|1997-04-08|1997-03-09|1997-04-25|TAKE BACK RETURN|SHIP| haggle furiously ir| +3429|59|7|2|15|14385.75|0.03|0.04|N|O|1997-02-04|1997-03-09|1997-03-01|TAKE BACK RETURN|TRUCK|beans are fu| +3429|69|4|3|10|9690.60|0.05|0.07|N|O|1997-01-19|1997-02-22|1997-01-25|TAKE BACK RETURN|REG AIR|ackages. quickly e| +3429|89|10|4|28|27694.24|0.10|0.07|N|O|1997-01-30|1997-03-18|1997-02-17|TAKE BACK RETURN|AIR|nstructions boost. thin| +3429|165|6|5|45|47932.20|0.10|0.00|N|O|1997-04-21|1997-03-08|1997-05-05|COLLECT COD|REG AIR|ites poach a| +3430|189|10|1|2|2178.36|0.07|0.06|R|F|1995-03-07|1995-01-28|1995-03-30|TAKE BACK RETURN|MAIL|sh furiously according to the evenly e| +3430|81|2|2|32|31394.56|0.08|0.00|R|F|1995-01-17|1995-01-28|1995-02-06|NONE|TRUCK|egular instruction| +3430|97|8|3|41|40880.69|0.06|0.04|R|F|1995-02-18|1995-02-21|1995-03-11|TAKE BACK RETURN|AIR|cuses. silent excuses h| +3430|65|2|4|50|48253.00|0.01|0.00|R|F|1994-12-15|1995-03-03|1994-12-24|COLLECT COD|REG AIR|ironic theodolites. carefully regular pac| +3430|95|9|5|5|4975.45|0.05|0.05|A|F|1995-04-02|1995-02-12|1995-04-08|DELIVER IN PERSON|FOB|even accounts haggle slyly bol| +3430|171|10|6|15|16067.55|0.08|0.07|A|F|1995-02-01|1995-03-12|1995-02-04|COLLECT COD|SHIP|cajole around the accounts. qui| +3430|52|7|7|23|21897.15|0.09|0.08|A|F|1995-03-06|1995-03-01|1995-03-10|COLLECT COD|MAIL|eas according to the| +3431|180|8|1|41|44287.38|0.03|0.06|A|F|1993-09-26|1993-10-13|1993-10-22|NONE|AIR| sleep carefully ironically special| +3456|111|8|1|34|34377.74|0.10|0.06|A|F|1993-08-29|1993-08-26|1993-09-07|TAKE BACK RETURN|SHIP|usy pinto beans b| +3457|182|3|1|29|31383.22|0.03|0.02|R|F|1995-05-12|1995-07-13|1995-06-05|NONE|TRUCK|refully final excuses wake| +3457|106|7|2|22|22134.20|0.06|0.01|N|O|1995-06-23|1995-06-16|1995-06-29|NONE|SHIP|packages nag furiously against| +3457|109|2|3|7|7063.70|0.07|0.08|N|O|1995-08-14|1995-07-06|1995-08-18|COLLECT COD|SHIP| pending accounts along the| +3457|1|2|4|24|21624.00|0.07|0.07|N|O|1995-08-03|1995-05-30|1995-08-14|TAKE BACK RETURN|REG AIR|tructions haggle alongsid| +3457|109|4|5|42|42382.20|0.05|0.01|A|F|1995-06-12|1995-06-14|1995-06-14|COLLECT COD|MAIL|riously final instruc| +3457|144|1|6|45|46986.30|0.08|0.01|N|O|1995-08-12|1995-07-18|1995-08-23|TAKE BACK RETURN|SHIP| packages. care| +3457|167|4|7|9|9604.44|0.04|0.00|R|F|1995-05-29|1995-06-30|1995-06-12|DELIVER IN PERSON|FOB|quests. foxes sleep quickly| +3458|133|4|1|48|49590.24|0.06|0.04|R|F|1995-03-17|1995-01-25|1995-03-28|TAKE BACK RETURN|AIR|iously pending dep| +3458|50|3|2|46|43702.30|0.06|0.06|R|F|1995-03-08|1995-01-21|1995-03-10|TAKE BACK RETURN|SHIP|nod across the boldly even instruct| +3458|143|4|3|36|37553.04|0.01|0.06|R|F|1995-04-20|1995-02-14|1995-05-09|TAKE BACK RETURN|REG AIR|s lose. blithely ironic requests boost| +3458|16|10|4|16|14656.16|0.09|0.03|R|F|1995-03-01|1995-02-25|1995-03-06|TAKE BACK RETURN|AIR|s grow carefully. express, final grouc| +3458|157|5|5|2|2114.30|0.09|0.03|A|F|1995-02-05|1995-02-01|1995-03-07|COLLECT COD|FOB|ironic packages haggle past the furiously | +3458|142|1|6|6|6252.84|0.09|0.04|A|F|1995-03-10|1995-02-02|1995-03-23|TAKE BACK RETURN|AIR|dolites; regular theodolites cajole | +3459|179|7|1|31|33454.27|0.06|0.01|A|F|1994-09-05|1994-10-20|1994-10-03|NONE|REG AIR|y regular pain| +3459|130|9|2|30|30903.90|0.04|0.08|R|F|1994-11-22|1994-09-12|1994-12-11|NONE|REG AIR|nic theodolites; evenly i| +3459|41|8|3|45|42346.80|0.04|0.05|A|F|1994-07-31|1994-09-09|1994-08-02|TAKE BACK RETURN|REG AIR|ntly speci| +3459|69|10|4|10|9690.60|0.05|0.06|A|F|1994-10-06|1994-09-16|1994-11-03|TAKE BACK RETURN|REG AIR| furiously silent dolphi| +3459|189|10|5|10|10891.80|0.02|0.02|R|F|1994-08-01|1994-10-17|1994-08-11|TAKE BACK RETURN|FOB|. blithely ironic pinto beans above| +3460|11|1|1|40|36440.40|0.10|0.06|N|O|1995-12-28|1995-12-14|1996-01-02|NONE|REG AIR|odolites are slyly bold deposits| +3460|74|4|2|3|2922.21|0.06|0.00|N|O|1996-01-19|1995-12-28|1996-01-31|COLLECT COD|AIR|er quickly | +3460|35|1|3|40|37401.20|0.08|0.07|N|O|1995-10-29|1995-11-10|1995-11-24|TAKE BACK RETURN|REG AIR|o the even deposits| +3460|95|8|4|50|49754.50|0.02|0.07|N|O|1996-01-30|1995-12-10|1996-02-06|DELIVER IN PERSON|SHIP|e slyly about the sly| +3460|130|1|5|47|48416.11|0.08|0.05|N|O|1995-12-09|1995-11-12|1995-12-22|TAKE BACK RETURN|SHIP|es haggle slyly regular accounts. fi| +3460|63|10|6|46|44300.76|0.03|0.07|N|O|1996-01-27|1996-01-01|1996-02-01|NONE|TRUCK|uses run among the carefully even deposits| +3460|45|2|7|28|26461.12|0.00|0.01|N|O|1995-10-28|1995-11-13|1995-11-17|COLLECT COD|SHIP|inal, ironic instructions. carefully| +3461|100|4|1|49|49004.90|0.06|0.06|A|F|1993-03-09|1993-04-16|1993-03-13|DELIVER IN PERSON|RAIL|ual request| +3461|63|4|2|27|26002.62|0.06|0.06|A|F|1993-02-10|1993-03-02|1993-03-04|COLLECT COD|SHIP|ely unusual deposits. quickly ir| +3461|39|5|3|44|41317.32|0.09|0.06|A|F|1993-05-20|1993-04-03|1993-05-27|COLLECT COD|RAIL| haggle quickly even ideas. fin| +3461|95|7|4|41|40798.69|0.09|0.02|R|F|1993-02-19|1993-04-20|1993-02-21|NONE|TRUCK|heodolites. blithely ironi| +3461|90|1|5|16|15841.44|0.08|0.06|A|F|1993-05-09|1993-04-29|1993-05-26|TAKE BACK RETURN|TRUCK| pending deposi| +3461|167|2|6|24|25611.84|0.10|0.00|A|F|1993-06-01|1993-03-12|1993-06-20|TAKE BACK RETURN|MAIL|thely. carefully re| +3462|151|3|1|4|4204.60|0.09|0.04|N|O|1997-06-12|1997-07-31|1997-06-16|COLLECT COD|RAIL|ackages. fu| +3462|40|1|2|43|40421.72|0.08|0.03|N|O|1997-08-01|1997-07-18|1997-08-29|NONE|RAIL| carefully. final, final ideas sleep slyly| +3462|129|4|3|6|6174.72|0.05|0.04|N|O|1997-06-02|1997-08-09|1997-06-30|NONE|RAIL|iously regular fo| +3462|99|3|4|2|1998.18|0.09|0.07|N|O|1997-09-10|1997-08-08|1997-09-19|NONE|AIR|nic packages. even accounts alongside | +3462|38|4|5|14|13132.42|0.01|0.02|N|O|1997-05-31|1997-07-05|1997-06-24|COLLECT COD|MAIL|yly. blithely bold theodolites wa| +3463|61|10|1|45|43247.70|0.02|0.02|A|F|1993-10-30|1993-11-04|1993-11-08|DELIVER IN PERSON|FOB|nts are slyly | +3463|98|1|2|43|42917.87|0.04|0.02|A|F|1993-10-28|1993-09-24|1993-11-03|DELIVER IN PERSON|FOB| across the | +3488|160|5|1|1|1060.16|0.04|0.01|A|F|1995-03-06|1995-02-16|1995-03-23|DELIVER IN PERSON|FOB| final excuses. carefully even waters hagg| +3488|104|9|2|48|48196.80|0.00|0.03|A|F|1995-03-29|1995-03-26|1995-04-28|COLLECT COD|SHIP|sly? final requests | +3488|160|1|3|11|11661.76|0.03|0.08|R|F|1995-03-25|1995-02-08|1995-04-16|COLLECT COD|TRUCK|unusual re| +3488|42|9|4|12|11304.48|0.05|0.07|R|F|1995-04-27|1995-02-16|1995-05-09|DELIVER IN PERSON|RAIL|e slyly; furiously final packages wak| +3488|156|1|5|18|19010.70|0.09|0.06|A|F|1995-03-18|1995-03-19|1995-03-29|DELIVER IN PERSON|FOB|s the carefully r| +3489|186|7|1|19|20637.42|0.09|0.05|A|F|1993-07-31|1993-10-26|1993-08-15|NONE|SHIP|c deposits alongside of the pending, fu| +3489|29|4|2|46|42734.92|0.00|0.00|A|F|1993-08-02|1993-10-09|1993-08-10|TAKE BACK RETURN|TRUCK|xcuses? quickly stealthy dependenci| +3490|92|6|1|43|42659.87|0.05|0.05|N|O|1997-08-04|1997-08-06|1997-08-14|TAKE BACK RETURN|SHIP|. even requests cajol| +3490|86|7|2|50|49304.00|0.05|0.07|N|O|1997-06-27|1997-08-15|1997-06-28|NONE|RAIL| haggle carefu| +3490|93|7|3|8|7944.72|0.10|0.04|N|O|1997-08-11|1997-07-25|1997-08-28|COLLECT COD|MAIL|inal deposits use furiousl| +3491|154|2|1|28|29516.20|0.04|0.03|N|O|1998-09-29|1998-09-08|1998-10-23|COLLECT COD|FOB|ccounts. sly| +3491|122|3|2|22|22486.64|0.08|0.02|N|O|1998-08-19|1998-08-22|1998-09-03|TAKE BACK RETURN|REG AIR| grow against the boldly pending pinto bea| +3492|156|7|1|3|3168.45|0.02|0.08|R|F|1994-11-26|1994-12-28|1994-12-19|COLLECT COD|REG AIR|the deposits. carefully | +3492|126|9|2|7|7182.84|0.04|0.00|R|F|1995-03-10|1995-01-03|1995-03-16|COLLECT COD|FOB|thely regular dolphi| +3492|109|10|3|34|34309.40|0.05|0.06|A|F|1994-12-07|1994-12-29|1994-12-24|COLLECT COD|AIR| unusual requests. ir| +3492|147|6|4|30|31414.20|0.02|0.06|A|F|1995-01-29|1995-01-02|1995-02-13|DELIVER IN PERSON|MAIL| detect furiously permanent, unusual accou| +3492|122|1|5|47|48039.64|0.09|0.07|R|F|1995-03-24|1994-12-28|1995-03-29|NONE|REG AIR|deposits. quickly express | +3492|22|7|6|47|43334.94|0.04|0.07|R|F|1994-12-12|1995-01-18|1994-12-26|COLLECT COD|RAIL|ronic instructions u| +3493|93|6|1|31|30785.79|0.06|0.07|R|F|1993-10-22|1993-10-12|1993-11-07|DELIVER IN PERSON|REG AIR|ructions. slyly regular accounts across the| +3493|132|3|2|10|10321.30|0.02|0.06|R|F|1993-08-27|1993-10-07|1993-09-23|COLLECT COD|TRUCK|hall have to integ| +3494|117|1|1|40|40684.40|0.05|0.04|R|F|1993-07-10|1993-06-01|1993-07-25|TAKE BACK RETURN|TRUCK|lites haggle furiously about the fin| +3494|75|6|2|23|22426.61|0.10|0.01|A|F|1993-06-19|1993-06-04|1993-07-14|NONE|FOB|osits nag | +3494|198|2|3|40|43927.60|0.02|0.08|A|F|1993-05-30|1993-07-02|1993-06-20|TAKE BACK RETURN|MAIL|uests cajole blithely| +3494|77|8|4|30|29312.10|0.04|0.03|R|F|1993-07-01|1993-06-08|1993-07-15|TAKE BACK RETURN|TRUCK|ns are quickly regular, | +3495|28|3|1|20|18560.40|0.10|0.03|N|O|1996-04-24|1996-05-18|1996-05-01|TAKE BACK RETURN|RAIL|posits are carefully; forges cajole qui| +3495|173|1|2|24|25756.08|0.05|0.02|N|O|1996-03-22|1996-04-10|1996-04-07|DELIVER IN PERSON|RAIL|ic, final pains along the even request| +3495|199|10|3|16|17587.04|0.08|0.02|N|O|1996-03-30|1996-04-02|1996-04-12|TAKE BACK RETURN|AIR|y bold dependencies; blithely idle sautern| +3520|28|1|1|30|27840.60|0.04|0.02|N|O|1997-11-11|1997-10-02|1997-12-06|COLLECT COD|SHIP|deas should solve blithely among the ironi| +3520|167|4|2|38|40552.08|0.00|0.04|N|O|1997-08-14|1997-10-26|1997-09-09|NONE|RAIL|yly final packages according to the quickl| +3520|106|9|3|5|5030.50|0.01|0.02|N|O|1997-11-13|1997-09-22|1997-12-09|NONE|MAIL|ly even ideas haggle | +3520|64|5|4|41|39526.46|0.01|0.01|N|O|1997-08-06|1997-09-20|1997-08-20|TAKE BACK RETURN|AIR| carefully pendi| +3520|163|10|5|35|37210.60|0.02|0.02|N|O|1997-09-16|1997-09-03|1997-09-24|DELIVER IN PERSON|FOB|s nag carefully. sometimes unusual account| +3521|59|4|1|48|46034.40|0.09|0.03|A|F|1993-01-03|1992-12-31|1993-01-22|NONE|AIR|ses use. furiously express ideas wake f| +3521|131|2|2|2|2062.26|0.05|0.06|R|F|1993-01-29|1992-12-20|1993-02-23|NONE|MAIL|refully duri| +3521|178|8|3|38|40970.46|0.00|0.08|A|F|1993-02-15|1992-12-10|1993-03-10|COLLECT COD|FOB|ges hang q| +3521|144|7|4|26|27147.64|0.02|0.08|R|F|1993-01-04|1993-01-20|1993-01-17|DELIVER IN PERSON|AIR|onic dependencies haggle. fur| +3521|36|7|5|28|26208.84|0.10|0.01|A|F|1993-01-06|1993-01-22|1993-02-02|TAKE BACK RETURN|FOB|e slyly above the slyly final| +3522|4|9|1|6|5424.00|0.08|0.03|A|F|1995-01-21|1994-12-09|1995-01-23|NONE|SHIP|tes snooze | +3522|87|8|2|48|47379.84|0.00|0.03|R|F|1994-12-05|1994-10-30|1994-12-26|TAKE BACK RETURN|SHIP|ve the quickly special packages| +3522|157|2|3|46|48628.90|0.09|0.02|A|F|1994-11-12|1994-11-30|1994-11-20|NONE|AIR|d the express, silent foxes. blit| +3522|130|9|4|7|7210.91|0.10|0.02|A|F|1994-10-31|1994-11-19|1994-11-28|NONE|TRUCK|e stealthil| +3522|50|9|5|27|25651.35|0.02|0.05|R|F|1994-11-29|1994-12-15|1994-12-08|COLLECT COD|REG AIR|ic tithes. car| +3522|158|10|6|18|19046.70|0.01|0.03|A|F|1994-11-16|1994-10-29|1994-11-29|COLLECT COD|RAIL|sits wake carefully pen| +3523|25|6|1|15|13875.30|0.06|0.02|N|O|1998-06-26|1998-05-22|1998-07-24|COLLECT COD|REG AIR|se slyly pending, sp| +3523|133|9|2|4|4132.52|0.03|0.06|N|O|1998-05-08|1998-05-18|1998-05-25|TAKE BACK RETURN|MAIL|ts. final accounts detect furiously along | +3523|50|7|3|24|22801.20|0.07|0.04|N|O|1998-08-02|1998-06-22|1998-08-27|COLLECT COD|FOB|ke according to the doggedly re| +3523|192|4|4|36|39318.84|0.06|0.08|N|O|1998-05-26|1998-06-04|1998-06-25|DELIVER IN PERSON|SHIP|accounts. fluffily regu| +3523|134|5|5|48|49638.24|0.00|0.01|N|O|1998-07-22|1998-06-25|1998-08-19|DELIVER IN PERSON|AIR| regular requests| +3524|137|8|1|5|5185.65|0.01|0.04|R|F|1992-05-23|1992-07-25|1992-06-19|DELIVER IN PERSON|RAIL|ts whithout the bold depende| +3524|143|6|2|17|17733.38|0.09|0.08|A|F|1992-09-01|1992-07-17|1992-09-05|DELIVER IN PERSON|FOB|g, final epitaphs about the pinto | +3525|46|7|1|12|11352.48|0.01|0.03|N|O|1996-03-08|1996-03-18|1996-03-16|NONE|TRUCK|lar excuses wake carefull| +3525|138|9|2|27|28029.51|0.03|0.03|N|O|1995-12-30|1996-01-23|1996-01-02|DELIVER IN PERSON|SHIP|y slyly special asymptotes| +3525|75|5|3|31|30227.17|0.00|0.03|N|O|1996-03-08|1996-02-27|1996-03-13|COLLECT COD|TRUCK|he careful| +3525|184|5|4|28|30357.04|0.03|0.02|N|O|1996-01-22|1996-02-08|1996-01-27|COLLECT COD|FOB| nag according | +3526|98|9|1|11|10978.99|0.02|0.03|R|F|1995-05-23|1995-05-28|1995-05-24|NONE|TRUCK|ges. furiously regular d| +3526|117|7|2|23|23393.53|0.03|0.04|A|F|1995-05-01|1995-05-31|1995-05-25|DELIVER IN PERSON|FOB|special, regular packages cajole. | +3526|33|9|3|20|18660.60|0.05|0.08|N|F|1995-06-16|1995-04-26|1995-06-22|DELIVER IN PERSON|REG AIR|kages. bold, special requests detect sl| +3527|102|7|1|47|47098.70|0.07|0.02|N|O|1997-07-14|1997-07-29|1997-07-21|DELIVER IN PERSON|RAIL|unts. express re| +3527|26|9|2|33|30558.66|0.01|0.02|N|O|1997-09-25|1997-09-17|1997-10-12|NONE|FOB|kly alongside of | +3527|162|7|3|50|53108.00|0.09|0.07|N|O|1997-07-17|1997-08-03|1997-07-29|DELIVER IN PERSON|SHIP|e even accounts was about th| +3527|128|3|4|17|17478.04|0.02|0.05|N|O|1997-07-30|1997-09-01|1997-08-17|COLLECT COD|MAIL|ular instruction| +3552|197|8|1|18|19749.42|0.01|0.07|N|O|1997-08-11|1997-07-14|1997-08-15|DELIVER IN PERSON|TRUCK|s deposits against the blithely unusual pin| +3552|90|1|2|44|43563.96|0.01|0.00|N|O|1997-08-08|1997-06-15|1997-08-29|COLLECT COD|FOB|ns after the blithely reg| +3552|161|6|3|36|38201.76|0.04|0.08|N|O|1997-06-29|1997-06-24|1997-07-21|COLLECT COD|TRUCK|ly regular theodolites. fin| +3553|143|10|1|4|4172.56|0.05|0.01|R|F|1994-06-13|1994-07-10|1994-07-03|COLLECT COD|RAIL|olites boost bli| +3553|65|4|2|26|25091.56|0.05|0.08|A|F|1994-08-06|1994-07-30|1994-08-23|DELIVER IN PERSON|MAIL|fily special p| +3553|22|5|3|18|16596.36|0.04|0.03|A|F|1994-07-03|1994-06-30|1994-07-07|COLLECT COD|RAIL|. quickly ironic| +3553|32|8|4|40|37281.20|0.06|0.00|A|F|1994-09-14|1994-06-26|1994-09-25|NONE|RAIL| slyly pending asymptotes against the furi| +3553|157|2|5|36|38057.40|0.06|0.08|R|F|1994-08-12|1994-06-25|1994-09-06|DELIVER IN PERSON|TRUCK| realms. pending, bold theodolites | +3554|175|5|1|32|34405.44|0.01|0.05|N|O|1995-09-28|1995-09-01|1995-10-07|NONE|RAIL|. blithely ironic t| +3554|145|6|2|18|18812.52|0.03|0.00|N|O|1995-09-11|1995-08-12|1995-10-04|DELIVER IN PERSON|REG AIR| haggle. furiously fluffy requests ac| +3554|192|3|3|41|44779.79|0.02|0.01|N|O|1995-07-13|1995-08-28|1995-07-27|DELIVER IN PERSON|MAIL|ent dependencies. sly| +3555|166|3|1|11|11727.76|0.05|0.02|N|O|1996-09-25|1996-10-01|1996-10-03|NONE|FOB|oost caref| +3555|79|10|2|15|14686.05|0.03|0.08|N|O|1996-07-13|1996-09-01|1996-08-02|TAKE BACK RETURN|RAIL|y across the pending a| +3555|43|2|3|25|23576.00|0.09|0.07|N|O|1996-10-01|1996-08-23|1996-10-24|TAKE BACK RETURN|MAIL|sual packages. quickly | +3555|5|6|4|19|17195.00|0.00|0.05|N|O|1996-09-08|1996-09-14|1996-10-01|COLLECT COD|REG AIR|leep special theodolit| +3555|33|4|5|29|27057.87|0.07|0.04|N|O|1996-08-02|1996-09-04|1996-08-08|DELIVER IN PERSON|TRUCK|deas. carefully s| +3555|28|3|6|33|30624.66|0.04|0.08|N|O|1996-09-20|1996-09-23|1996-10-05|TAKE BACK RETURN|AIR|fluffily regular a| +3555|126|5|7|9|9235.08|0.07|0.02|N|O|1996-10-13|1996-10-02|1996-10-22|NONE|SHIP|are. slyly final foxes acro| +3556|142|9|1|45|46896.30|0.05|0.06|A|F|1992-10-14|1992-12-21|1992-10-16|NONE|TRUCK|ckages boost quickl| +3556|31|2|2|43|40034.29|0.02|0.06|R|F|1993-01-18|1992-11-09|1993-02-04|NONE|FOB|wake carefull| +3556|87|8|3|28|27638.24|0.10|0.04|A|F|1993-01-06|1992-11-27|1993-01-16|NONE|MAIL|refully final instructions? ironic packa| +3557|175|3|1|41|44081.97|0.01|0.07|R|F|1993-01-30|1992-12-31|1993-02-18|COLLECT COD|FOB|ideas breach c| +3557|129|10|2|37|38077.44|0.03|0.05|R|F|1993-02-16|1993-01-05|1993-03-15|DELIVER IN PERSON|RAIL|gside of the ca| +3558|87|8|1|8|7896.64|0.01|0.03|N|O|1996-05-31|1996-05-26|1996-06-25|COLLECT COD|AIR|? even requests sle| +3558|10|7|2|28|25480.28|0.02|0.08|N|O|1996-06-02|1996-04-18|1996-06-24|COLLECT COD|TRUCK|l deposits | +3558|187|8|3|3|3261.54|0.03|0.06|N|O|1996-05-19|1996-04-28|1996-05-26|DELIVER IN PERSON|RAIL|l, final deposits haggle. fina| +3558|91|5|4|22|21803.98|0.06|0.03|N|O|1996-04-27|1996-04-19|1996-04-30|DELIVER IN PERSON|SHIP|refully ironic theodolites are fu| +3558|29|8|5|38|35302.76|0.03|0.08|N|O|1996-05-29|1996-05-02|1996-06-09|COLLECT COD|RAIL|refully permanently iron| +3558|72|1|6|17|16525.19|0.07|0.07|N|O|1996-03-14|1996-05-04|1996-04-05|NONE|RAIL|ithely unusual packa| +3559|90|1|1|29|28712.61|0.00|0.07|R|F|1992-12-10|1992-12-03|1992-12-20|COLLECT COD|REG AIR|l, regular accounts wake flu| +3584|11|8|1|4|3644.04|0.04|0.08|N|O|1997-08-16|1997-10-31|1997-08-28|DELIVER IN PERSON|TRUCK|nal packag| +3584|160|8|2|23|24383.68|0.00|0.03|N|O|1997-09-10|1997-10-15|1997-09-30|COLLECT COD|TRUCK|l platelets until the asymptotes | +3584|24|5|3|6|5544.12|0.03|0.06|N|O|1997-10-28|1997-11-09|1997-11-24|TAKE BACK RETURN|MAIL|deposits across the| +3584|146|5|4|11|11507.54|0.06|0.02|N|O|1997-11-27|1997-10-15|1997-12-08|NONE|REG AIR|lithely slyly | +3584|18|5|5|39|35802.39|0.09|0.07|N|O|1997-09-20|1997-10-31|1997-10-06|COLLECT COD|AIR|eposits. carefu| +3585|122|1|1|21|21464.52|0.05|0.04|A|F|1994-12-04|1994-12-25|1995-01-01|TAKE BACK RETURN|TRUCK|ounts use. express, final platelets us| +3585|19|10|2|40|36760.40|0.03|0.00|R|F|1995-01-22|1995-01-17|1995-02-07|TAKE BACK RETURN|RAIL|elets affix. even asymptotes play care| +3585|112|2|3|11|11133.21|0.01|0.04|R|F|1995-01-04|1995-02-14|1995-01-15|NONE|MAIL|even packages| +3585|48|1|4|33|31285.32|0.08|0.08|A|F|1994-12-14|1995-01-19|1994-12-22|NONE|RAIL|ironic dependencies serve furi| +3585|25|8|5|13|12025.26|0.06|0.07|R|F|1995-03-15|1995-01-22|1995-03-17|DELIVER IN PERSON|AIR|ccording to the foxes. slyly iro| +3585|94|7|6|7|6958.63|0.10|0.02|A|F|1994-12-13|1995-01-20|1995-01-05|TAKE BACK RETURN|TRUCK|dependencies sleep un| +3585|42|1|7|45|42391.80|0.03|0.00|A|F|1995-01-20|1995-02-19|1995-02-11|DELIVER IN PERSON|MAIL|are blithely c| +3586|194|7|1|2|2188.38|0.03|0.08|R|F|1994-02-10|1994-01-07|1994-03-03|DELIVER IN PERSON|RAIL|he even, unusual decoy| +3586|84|5|2|29|28538.32|0.04|0.07|R|F|1994-03-06|1994-03-02|1994-03-13|DELIVER IN PERSON|RAIL| slyly unusual i| +3586|58|3|3|2|1916.10|0.03|0.06|R|F|1994-03-22|1994-02-20|1994-04-08|NONE|REG AIR|unts. slyly final ideas agai| +3586|84|5|4|33|32474.64|0.06|0.01|R|F|1994-01-24|1994-02-09|1994-02-07|NONE|TRUCK|refully across the fur| +3586|108|1|5|8|8064.80|0.06|0.02|A|F|1994-03-29|1994-02-26|1994-04-02|NONE|FOB|theodolites hagg| +3586|99|1|6|8|7992.72|0.09|0.01|A|F|1994-03-18|1994-01-17|1994-04-06|DELIVER IN PERSON|RAIL| ironic pinto beans cajole carefully theo| +3586|123|4|7|33|33762.96|0.05|0.04|A|F|1994-02-11|1994-01-15|1994-03-03|NONE|REG AIR|iously regular pinto beans integrate| +3587|197|10|1|5|5485.95|0.09|0.07|N|O|1996-09-03|1996-07-05|1996-09-11|DELIVER IN PERSON|SHIP|ithely regular decoys above the | +3587|132|8|2|48|49542.24|0.00|0.03|N|O|1996-08-02|1996-07-02|1996-08-05|TAKE BACK RETURN|MAIL|beans. blithely final depe| +3587|151|3|3|36|37841.40|0.05|0.05|N|O|1996-07-26|1996-06-16|1996-08-23|TAKE BACK RETURN|MAIL|ully regular excuse| +3587|124|9|4|31|31747.72|0.03|0.01|N|O|1996-07-21|1996-07-01|1996-07-23|COLLECT COD|SHIP|press fluffily regul| +3587|70|7|5|12|11640.84|0.06|0.03|N|O|1996-08-30|1996-07-04|1996-09-22|DELIVER IN PERSON|RAIL|g the even pinto beans. special,| +3587|107|2|6|16|16113.60|0.01|0.03|N|O|1996-05-11|1996-06-19|1996-06-04|COLLECT COD|FOB|y ruthless dolphins to | +3587|74|2|7|23|22403.61|0.07|0.05|N|O|1996-08-30|1996-07-01|1996-09-10|COLLECT COD|FOB|l multipliers sleep theodolites-- slyly | +3588|91|5|1|28|27750.52|0.04|0.08|R|F|1995-05-03|1995-05-03|1995-05-14|DELIVER IN PERSON|TRUCK|special pinto beans cajole slyly. slyly | +3588|88|9|2|6|5928.48|0.06|0.08|A|F|1995-04-09|1995-05-30|1995-04-10|TAKE BACK RETURN|MAIL|s. fluffily fluf| +3588|159|10|3|45|47661.75|0.04|0.02|R|F|1995-05-07|1995-05-04|1995-05-28|TAKE BACK RETURN|TRUCK|ecial pains integrate blithely. reques| +3588|127|10|4|22|22596.64|0.05|0.00|A|F|1995-04-08|1995-05-06|1995-04-27|NONE|RAIL|inal accounts. pending, bo| +3588|55|3|5|28|26741.40|0.03|0.03|A|F|1995-04-23|1995-05-25|1995-04-28|DELIVER IN PERSON|TRUCK| express sheaves. unusual theodo| +3588|110|3|6|37|37374.07|0.08|0.04|N|F|1995-06-17|1995-05-25|1995-06-24|TAKE BACK RETURN|RAIL|xcuses sleep quickly along th| +3588|39|5|7|46|43195.38|0.08|0.07|A|F|1995-06-06|1995-05-08|1995-06-08|NONE|AIR| slyly ironic deposits sublate ab| +3589|37|3|1|42|39355.26|0.08|0.08|R|F|1994-08-11|1994-07-17|1994-08-23|DELIVER IN PERSON|AIR|he blithely unusual pac| +3590|176|6|1|10|10761.70|0.08|0.00|N|O|1995-07-17|1995-06-26|1995-08-12|TAKE BACK RETURN|SHIP|t the quickly ironic| +3590|95|6|2|19|18906.71|0.03|0.03|N|O|1995-08-02|1995-06-20|1995-08-08|NONE|SHIP|special pinto beans. blithely reg| +3590|96|9|3|43|42831.87|0.07|0.06|N|O|1995-07-12|1995-07-25|1995-07-16|DELIVER IN PERSON|SHIP|s could have to use| +3590|56|8|4|26|24857.30|0.01|0.03|N|O|1995-07-08|1995-06-17|1995-08-02|DELIVER IN PERSON|SHIP|arefully along th| +3590|191|2|5|37|40374.03|0.00|0.08|N|O|1995-09-01|1995-06-29|1995-09-10|NONE|SHIP|ccounts above the silent waters thrash f| +3590|119|10|6|31|31592.41|0.03|0.01|N|O|1995-06-24|1995-07-12|1995-06-25|DELIVER IN PERSON|REG AIR|ve furiously final instructions. slyly regu| +3590|194|7|7|44|48144.36|0.05|0.04|N|F|1995-06-07|1995-06-15|1995-06-27|NONE|MAIL|s sleep after the regular platelets. blit| +3591|29|8|1|21|19509.42|0.03|0.03|A|F|1994-02-25|1994-02-02|1994-03-05|DELIVER IN PERSON|TRUCK|structions against | +3591|69|6|2|24|23257.44|0.04|0.04|R|F|1993-12-26|1994-01-07|1994-01-25|COLLECT COD|FOB|ages. slyly regular dependencies cajo| +3591|164|9|3|4|4256.64|0.01|0.03|A|F|1994-04-04|1994-02-19|1994-05-02|DELIVER IN PERSON|RAIL|he final packages. deposits serve quick| +3591|153|4|4|49|51604.35|0.01|0.00|A|F|1994-03-21|1994-01-26|1994-03-28|COLLECT COD|AIR| mold slyly. bl| +3616|197|9|1|30|32915.70|0.01|0.00|A|F|1994-05-05|1994-04-24|1994-05-12|TAKE BACK RETURN|FOB|ly ironic accounts unwind b| +3616|138|9|2|28|29067.64|0.08|0.06|R|F|1994-02-20|1994-04-18|1994-03-05|DELIVER IN PERSON|REG AIR|ironic packages. furiously ev| +3617|117|8|1|46|46787.06|0.03|0.02|N|O|1996-05-19|1996-05-14|1996-06-11|NONE|RAIL|ar theodolites. regu| +3617|98|9|2|16|15969.44|0.05|0.02|N|O|1996-05-08|1996-06-03|1996-05-19|COLLECT COD|RAIL| slyly on th| +3617|98|2|3|32|31938.88|0.00|0.06|N|O|1996-04-20|1996-06-07|1996-05-19|DELIVER IN PERSON|MAIL|uriously against the express accounts. ex| +3617|41|10|4|22|20702.88|0.10|0.05|N|O|1996-07-11|1996-05-02|1996-07-25|NONE|REG AIR|uffily even accounts. packages sleep blithe| +3617|137|8|5|11|11408.43|0.08|0.05|N|O|1996-07-16|1996-04-23|1996-07-28|COLLECT COD|MAIL|ly quickly even requests. final| +3618|140|1|1|38|39525.32|0.08|0.00|N|O|1997-12-22|1998-02-23|1998-01-03|TAKE BACK RETURN|TRUCK|nts haggle fluffily above the regular | +3618|144|5|2|48|50118.72|0.04|0.00|N|O|1998-03-12|1998-02-13|1998-03-29|DELIVER IN PERSON|TRUCK|tructions atop the ironi| +3618|63|2|3|24|23113.44|0.01|0.04|N|O|1998-01-26|1998-01-15|1998-02-17|TAKE BACK RETURN|AIR|xpress acc| +3618|161|2|4|26|27590.16|0.01|0.05|N|O|1998-03-23|1998-01-24|1998-04-15|DELIVER IN PERSON|AIR|iously regular deposits cajole ruthless| +3619|96|7|1|49|48808.41|0.01|0.08|N|O|1997-01-22|1996-12-21|1997-02-17|TAKE BACK RETURN|MAIL| waters. furiously even deposits | +3619|116|10|2|27|27434.97|0.08|0.04|N|O|1996-12-12|1997-01-18|1996-12-18|TAKE BACK RETURN|SHIP|pecial accounts haggle care| +3619|48|7|3|46|43609.84|0.08|0.03|N|O|1997-01-31|1997-01-27|1997-02-11|NONE|SHIP|press, expres| +3619|93|6|4|18|17875.62|0.04|0.02|N|O|1997-03-18|1996-12-24|1997-03-21|COLLECT COD|AIR|eodolites | +3619|120|10|5|38|38764.56|0.05|0.08|N|O|1996-12-08|1997-02-03|1997-01-07|NONE|RAIL|theodolites detect abo| +3619|152|3|6|43|45242.45|0.01|0.01|N|O|1997-01-25|1997-01-06|1997-02-07|COLLECT COD|RAIL| bold, even| +3620|59|7|1|41|39321.05|0.03|0.08|N|O|1997-03-21|1997-04-20|1997-03-30|COLLECT COD|FOB|t attainments cajole qui| +3620|167|4|2|16|17074.56|0.00|0.06|N|O|1997-05-17|1997-05-08|1997-06-03|COLLECT COD|SHIP|s. even, pending in| +3621|17|8|1|29|26593.29|0.02|0.06|A|F|1993-08-03|1993-07-08|1993-08-10|DELIVER IN PERSON|FOB|al requests. fl| +3621|93|5|2|13|12910.17|0.09|0.04|R|F|1993-08-30|1993-06-30|1993-09-01|NONE|REG AIR|r the unusual packages. brave theodoli| +3621|164|9|3|45|47887.20|0.07|0.07|R|F|1993-08-09|1993-06-18|1993-09-05|DELIVER IN PERSON|AIR| doubt about the bold deposits. carefully| +3621|44|3|4|20|18880.80|0.05|0.04|R|F|1993-05-27|1993-07-04|1993-06-22|TAKE BACK RETURN|SHIP|gular accounts use carefully with| +3622|175|6|1|47|50532.99|0.09|0.00|N|O|1996-02-24|1996-02-22|1996-03-12|TAKE BACK RETURN|TRUCK|are careful| +3622|89|10|2|4|3956.32|0.04|0.04|N|O|1996-02-03|1996-02-19|1996-02-16|TAKE BACK RETURN|TRUCK|lithely brave foxes. furi| +3622|190|1|3|46|50148.74|0.07|0.07|N|O|1995-12-18|1996-01-23|1996-01-12|TAKE BACK RETURN|AIR|sits wake. blithe| +3622|177|8|4|9|9694.53|0.08|0.05|N|O|1995-12-12|1996-02-09|1995-12-13|TAKE BACK RETURN|SHIP|arefully. furiously regular ideas n| +3623|80|10|1|32|31362.56|0.05|0.00|N|O|1997-04-18|1997-03-15|1997-05-09|COLLECT COD|SHIP| courts. furiously regular ideas b| +3623|117|4|2|33|33564.63|0.08|0.01|N|O|1997-03-17|1997-02-13|1997-04-02|TAKE BACK RETURN|TRUCK|odolites. blithely spe| +3623|24|7|3|21|19404.42|0.02|0.02|N|O|1997-01-19|1997-03-18|1997-01-24|NONE|FOB|ress ideas are furio| +3623|165|2|4|42|44736.72|0.05|0.06|N|O|1997-01-11|1997-03-24|1997-01-21|COLLECT COD|RAIL|g to the slyly regular packa| +3623|88|9|5|30|29642.40|0.10|0.04|N|O|1997-04-04|1997-03-03|1997-05-01|NONE|RAIL| ironic somas sleep fluffily| +3623|186|7|6|7|7603.26|0.01|0.02|N|O|1997-01-05|1997-03-26|1997-01-26|NONE|TRUCK|aves. slyly special packages cajole. fu| +3623|140|6|7|13|13521.82|0.03|0.08|N|O|1997-01-02|1997-02-26|1997-01-26|DELIVER IN PERSON|SHIP|deas. furiously expres| +3648|144|5|1|16|16706.24|0.02|0.06|A|F|1993-08-14|1993-08-14|1993-08-15|COLLECT COD|FOB|s nag packages.| +3648|105|2|2|30|30153.00|0.00|0.01|R|F|1993-08-31|1993-09-06|1993-09-06|DELIVER IN PERSON|FOB| above the somas boost furious| +3648|46|7|3|34|32165.36|0.10|0.00|A|F|1993-08-21|1993-07-25|1993-09-15|DELIVER IN PERSON|FOB| deposits are furiously. careful, | +3648|13|10|4|16|14608.16|0.06|0.03|R|F|1993-07-27|1993-08-26|1993-08-24|DELIVER IN PERSON|FOB|uriously stealthy deposits haggle furi| +3648|117|7|5|25|25427.75|0.06|0.03|R|F|1993-08-15|1993-08-25|1993-09-09|TAKE BACK RETURN|TRUCK|s requests. silent asymp| +3648|169|10|6|14|14968.24|0.08|0.06|R|F|1993-10-02|1993-08-26|1993-10-09|COLLECT COD|AIR|sly pending excuses. carefully i| +3648|195|6|7|49|53664.31|0.09|0.03|R|F|1993-06-27|1993-07-27|1993-07-24|TAKE BACK RETURN|FOB|egular instructions. slyly regular pinto| +3649|5|6|1|25|22625.00|0.10|0.04|A|F|1994-10-27|1994-08-23|1994-11-05|TAKE BACK RETURN|TRUCK|special re| +3649|89|10|2|23|22748.84|0.08|0.00|R|F|1994-09-26|1994-10-01|1994-09-28|NONE|REG AIR|rs promise blithe| +3649|70|7|3|14|13580.98|0.02|0.04|A|F|1994-09-19|1994-08-17|1994-10-12|DELIVER IN PERSON|TRUCK|ithely bold accounts wake | +3649|76|4|4|40|39042.80|0.00|0.08|R|F|1994-07-20|1994-08-30|1994-08-14|TAKE BACK RETURN|RAIL|luffy somas sleep quickly-- ironic de| +3649|100|1|5|24|24002.40|0.05|0.03|A|F|1994-07-07|1994-08-20|1994-07-27|TAKE BACK RETURN|FOB|c accounts. quickly final theodo| +3649|122|3|6|3|3066.36|0.10|0.04|A|F|1994-07-17|1994-08-10|1994-08-03|NONE|FOB|lly bold requests nag; | +3650|136|2|1|30|31083.90|0.10|0.00|A|F|1992-08-26|1992-07-05|1992-09-01|DELIVER IN PERSON|SHIP|ckly special platelets. furiously sil| +3650|128|9|2|43|44209.16|0.05|0.05|A|F|1992-09-07|1992-08-12|1992-09-10|COLLECT COD|TRUCK|gside of the quick| +3650|2|9|3|1|902.00|0.04|0.06|A|F|1992-06-23|1992-07-18|1992-07-08|NONE|REG AIR|re about the pinto | +3650|63|2|4|31|29854.86|0.10|0.08|R|F|1992-06-15|1992-07-01|1992-07-15|DELIVER IN PERSON|RAIL| against the ironic accounts cajol| +3650|187|8|5|19|20656.42|0.05|0.04|R|F|1992-08-29|1992-08-09|1992-09-21|DELIVER IN PERSON|AIR|y even forges. fluffily furious accounts| +3650|94|8|6|27|26840.43|0.07|0.08|A|F|1992-07-03|1992-07-23|1992-07-13|COLLECT COD|MAIL|ular requests snooze fluffily regular pi| +3650|70|7|7|43|41713.01|0.10|0.07|A|F|1992-06-25|1992-07-09|1992-07-22|DELIVER IN PERSON|RAIL|structions use caref| +3651|19|9|1|20|18380.20|0.01|0.04|N|O|1998-06-10|1998-06-06|1998-06-23|NONE|SHIP|tect quickly among the r| +3651|155|7|2|24|25323.60|0.09|0.04|N|O|1998-06-22|1998-07-17|1998-07-10|DELIVER IN PERSON|RAIL|excuses haggle according to th| +3651|113|10|3|41|41537.51|0.00|0.05|N|O|1998-05-10|1998-07-09|1998-05-13|NONE|RAIL|blithely. furiously | +3651|110|5|4|27|27272.97|0.05|0.03|N|O|1998-05-03|1998-06-30|1998-05-05|DELIVER IN PERSON|RAIL| sleep blithely furiously do| +3652|180|8|1|24|25924.32|0.05|0.03|N|O|1997-06-07|1997-04-07|1997-06-12|COLLECT COD|MAIL|the final p| +3652|137|8|2|37|38373.81|0.02|0.05|N|O|1997-05-11|1997-04-06|1997-06-05|COLLECT COD|MAIL|osits haggle carefu| +3652|163|8|3|39|41463.24|0.01|0.02|N|O|1997-03-10|1997-04-03|1997-03-21|NONE|REG AIR|y express instructions. un| +3652|80|9|4|1|980.08|0.01|0.04|N|O|1997-04-20|1997-05-03|1997-05-18|DELIVER IN PERSON|SHIP| bold dependencies sublate. r| +3653|145|4|1|38|39715.32|0.08|0.05|A|F|1994-06-26|1994-05-13|1994-07-13|NONE|REG AIR|ainst the | +3653|64|1|2|29|27957.74|0.07|0.01|A|F|1994-04-11|1994-06-11|1994-04-29|COLLECT COD|RAIL|ording to the special, final| +3653|181|2|3|17|18380.06|0.09|0.03|R|F|1994-06-24|1994-06-02|1994-07-17|DELIVER IN PERSON|RAIL|gle slyly regular| +3653|186|7|4|9|9775.62|0.10|0.07|R|F|1994-04-03|1994-05-19|1994-04-10|COLLECT COD|FOB|slyly silent account| +3653|188|9|5|41|44615.38|0.08|0.01|A|F|1994-06-18|1994-05-18|1994-06-20|COLLECT COD|RAIL|onic packages affix sly| +3653|43|4|6|9|8487.36|0.05|0.03|A|F|1994-07-21|1994-05-31|1994-08-17|NONE|MAIL|tes: blithely bo| +3653|49|6|7|2|1898.08|0.06|0.03|R|F|1994-06-02|1994-05-31|1994-06-29|NONE|FOB|n accounts. fina| +3654|165|2|1|46|48997.36|0.08|0.05|A|F|1992-06-05|1992-08-19|1992-06-06|DELIVER IN PERSON|FOB|usly regular foxes. furio| +3654|93|4|2|29|28799.61|0.07|0.06|A|F|1992-09-11|1992-07-20|1992-10-04|DELIVER IN PERSON|FOB|odolites detect. quickly r| +3654|2|7|3|37|33374.00|0.07|0.05|A|F|1992-09-22|1992-07-20|1992-10-19|TAKE BACK RETURN|RAIL|unts doze bravely ab| +3654|168|9|4|11|11749.76|0.08|0.00|A|F|1992-07-20|1992-07-30|1992-07-23|TAKE BACK RETURN|SHIP|quickly along the express, ironic req| +3654|94|5|5|34|33799.06|0.04|0.00|R|F|1992-07-26|1992-08-26|1992-08-12|TAKE BACK RETURN|REG AIR| the quick| +3654|107|4|6|20|20142.00|0.03|0.02|A|F|1992-07-30|1992-07-05|1992-08-05|COLLECT COD|SHIP|s sleep about the slyly | +3654|173|1|7|45|48292.65|0.01|0.07|A|F|1992-09-15|1992-07-04|1992-09-20|DELIVER IN PERSON|FOB|sly ironic notornis nag slyly| +3655|184|5|1|5|5420.90|0.03|0.04|R|F|1993-01-17|1992-12-31|1993-01-23|DELIVER IN PERSON|TRUCK|riously bold pinto be| +3655|97|10|2|1|997.09|0.10|0.06|R|F|1992-10-24|1992-12-18|1992-11-07|DELIVER IN PERSON|AIR|arefully slow pinto beans are| +3655|30|5|3|35|32551.05|0.01|0.04|R|F|1992-12-20|1992-11-16|1993-01-15|TAKE BACK RETURN|MAIL|blithely even accounts! furiously regular| +3655|72|3|4|35|34022.45|0.04|0.07|R|F|1992-10-17|1992-12-23|1992-10-28|COLLECT COD|MAIL|ng foxes cajole fluffily slyly final fo| +3680|177|6|1|48|51704.16|0.00|0.06|R|F|1993-01-16|1993-01-23|1993-01-19|COLLECT COD|FOB|packages. quickly fluff| +3680|5|8|2|41|37105.00|0.00|0.04|A|F|1993-01-06|1993-03-02|1993-01-08|NONE|FOB|iously ironic platelets in| +3680|56|4|3|33|31549.65|0.09|0.08|R|F|1993-03-16|1993-02-19|1993-04-05|NONE|FOB|ts. ironic, fina| +3681|106|9|1|35|35213.50|0.03|0.08|R|F|1992-07-31|1992-05-18|1992-08-07|COLLECT COD|FOB|lyly special pinto | +3682|61|10|1|6|5766.36|0.07|0.02|N|O|1997-05-06|1997-04-04|1997-05-11|NONE|AIR|ronic deposits wake slyly. ca| +3682|116|7|2|18|18289.98|0.06|0.06|N|O|1997-04-30|1997-03-21|1997-05-10|NONE|FOB|regular dependencies| +3682|47|10|3|17|16099.68|0.03|0.05|N|O|1997-02-12|1997-04-04|1997-02-22|COLLECT COD|FOB|, ironic packages wake a| +3682|57|5|4|30|28711.50|0.09|0.05|N|O|1997-04-16|1997-04-16|1997-04-29|NONE|MAIL|he requests cajole quickly pending package| +3683|101|4|1|35|35038.50|0.05|0.03|A|F|1993-05-31|1993-04-17|1993-06-14|NONE|SHIP| the furiously expr| +3683|49|8|2|41|38910.64|0.01|0.06|A|F|1993-03-26|1993-05-06|1993-04-09|NONE|TRUCK|ress instructions. slyly express a| +3683|100|3|3|23|23002.30|0.00|0.08|R|F|1993-07-02|1993-05-16|1993-07-30|NONE|TRUCK|xpress accounts sleep slyly re| +3684|126|7|1|48|49253.76|0.04|0.06|A|F|1993-08-20|1993-09-02|1993-09-10|DELIVER IN PERSON|REG AIR|its boost alongside| +3684|46|7|2|6|5676.24|0.06|0.08|R|F|1993-08-09|1993-10-05|1993-09-06|DELIVER IN PERSON|FOB|he silent requests. packages sleep fu| +3684|163|8|3|19|20200.04|0.04|0.02|A|F|1993-10-19|1993-08-25|1993-11-02|COLLECT COD|FOB|e slyly carefully pending foxes. d| +3684|135|1|4|13|13456.69|0.02|0.05|A|F|1993-07-23|1993-09-16|1993-08-06|NONE|TRUCK|ing, unusual pinto beans! thinly p| +3685|47|4|1|37|35040.48|0.02|0.03|R|F|1992-03-11|1992-04-09|1992-04-05|DELIVER IN PERSON|TRUCK|ress attai| +3685|58|6|2|7|6706.35|0.05|0.00|R|F|1992-05-16|1992-02-23|1992-05-17|DELIVER IN PERSON|FOB|sits. special asymptotes about the r| +3685|134|5|3|38|39296.94|0.08|0.03|A|F|1992-05-17|1992-03-16|1992-06-06|TAKE BACK RETURN|TRUCK|thely unusual pack| +3685|192|5|4|39|42595.41|0.10|0.05|R|F|1992-02-19|1992-04-06|1992-03-02|COLLECT COD|FOB|ic courts nag carefully after the | +3685|56|7|5|37|35373.85|0.00|0.01|A|F|1992-03-02|1992-04-10|1992-03-04|NONE|FOB|. carefully sly requests are regular, regu| +3686|122|5|1|7|7154.84|0.02|0.04|N|O|1998-07-15|1998-08-22|1998-07-30|DELIVER IN PERSON|TRUCK| furiously unusual accou| +3686|200|2|2|38|41807.60|0.06|0.03|N|O|1998-09-04|1998-08-11|1998-09-19|DELIVER IN PERSON|AIR|y silent foxes! carefully ruthless cour| +3686|45|6|3|31|29296.24|0.10|0.06|N|O|1998-09-09|1998-08-28|1998-10-09|COLLECT COD|MAIL|gle across the courts. furiously regu| +3686|117|1|4|7|7119.77|0.10|0.01|N|O|1998-07-16|1998-09-02|1998-07-22|NONE|FOB|ake carefully carefully q| +3687|145|4|1|32|33444.48|0.03|0.06|R|F|1993-05-07|1993-04-05|1993-05-25|DELIVER IN PERSON|AIR|deas cajole fo| +3687|81|2|2|2|1962.16|0.00|0.08|R|F|1993-02-23|1993-03-25|1993-03-11|NONE|TRUCK| express requests. slyly regular depend| +3687|174|4|3|10|10741.70|0.01|0.02|A|F|1993-02-11|1993-03-22|1993-03-09|NONE|FOB|ing pinto beans| +3687|162|9|4|19|20181.04|0.02|0.05|A|F|1993-05-14|1993-04-24|1993-06-01|DELIVER IN PERSON|MAIL|ly final asymptotes according to t| +3687|119|9|5|31|31592.41|0.07|0.08|A|F|1993-05-28|1993-03-20|1993-06-05|DELIVER IN PERSON|FOB|foxes cajole quickly about the furiously f| +3712|141|4|1|27|28110.78|0.01|0.05|R|F|1992-02-01|1992-02-26|1992-03-02|TAKE BACK RETURN|SHIP|ctions. even accounts haggle alongside | +3712|185|6|2|13|14107.34|0.03|0.03|R|F|1992-04-30|1992-02-11|1992-05-30|DELIVER IN PERSON|FOB|s around the furiously ironic account| +3712|64|1|3|44|42418.64|0.01|0.01|A|F|1992-03-26|1992-02-19|1992-04-18|TAKE BACK RETURN|FOB|ously permanently regular req| +3712|148|7|4|38|39829.32|0.01|0.06|A|F|1992-01-15|1992-03-24|1992-01-27|COLLECT COD|RAIL|s nag carefully-- even, reg| +3713|112|6|1|41|41496.51|0.07|0.08|N|O|1998-05-11|1998-07-17|1998-05-22|COLLECT COD|RAIL|eposits wake blithely fina| +3713|177|7|2|19|20466.23|0.04|0.04|N|O|1998-06-25|1998-07-24|1998-07-08|DELIVER IN PERSON|AIR|tructions serve blithely around the furi| +3713|180|1|3|19|20523.42|0.03|0.02|N|O|1998-05-19|1998-07-06|1998-06-09|DELIVER IN PERSON|REG AIR|quests cajole careful| +3713|169|10|4|45|48112.20|0.06|0.04|N|O|1998-06-15|1998-07-30|1998-07-14|DELIVER IN PERSON|MAIL|al pinto beans affix after the slyly | +3713|90|1|5|46|45544.14|0.10|0.04|N|O|1998-08-22|1998-06-27|1998-08-31|NONE|MAIL|totes. carefully special theodolites s| +3713|182|3|6|29|31383.22|0.09|0.03|N|O|1998-08-04|1998-06-13|1998-08-21|NONE|RAIL|the regular dugouts wake furiously sil| +3713|130|1|7|14|14421.82|0.04|0.00|N|O|1998-07-19|1998-07-02|1998-07-28|DELIVER IN PERSON|SHIP|eposits impress according| +3714|69|6|1|13|12597.78|0.07|0.03|N|O|1998-06-26|1998-06-17|1998-07-07|TAKE BACK RETURN|REG AIR| the furiously final| +3714|146|3|2|14|14645.96|0.02|0.05|N|O|1998-05-30|1998-06-30|1998-05-31|DELIVER IN PERSON|RAIL|ending ideas. thinly unusual theodo| +3714|159|10|3|16|16946.40|0.00|0.02|N|O|1998-05-25|1998-07-07|1998-06-17|TAKE BACK RETURN|AIR|ccounts cajole fu| +3714|30|9|4|44|40921.32|0.04|0.02|N|O|1998-07-18|1998-07-10|1998-07-22|DELIVER IN PERSON|AIR|s. quickly ironic dugouts sublat| +3715|97|1|1|13|12962.17|0.00|0.03|N|O|1996-05-11|1996-04-25|1996-06-09|TAKE BACK RETURN|SHIP|e quickly ironic| +3715|169|6|2|16|17106.56|0.01|0.06|N|O|1996-06-28|1996-04-22|1996-06-30|TAKE BACK RETURN|AIR|usly regular pearls haggle final packages| +3715|12|3|3|37|33744.37|0.05|0.02|N|O|1996-05-03|1996-04-30|1996-05-17|NONE|SHIP|ut the carefully expr| +3716|32|8|1|10|9320.30|0.09|0.04|N|O|1997-12-02|1997-11-09|1997-12-14|TAKE BACK RETURN|SHIP|ts. quickly sly ideas slee| +3716|194|5|2|39|42673.41|0.02|0.08|N|O|1997-11-27|1997-10-23|1997-12-24|COLLECT COD|REG AIR|even deposits.| +3716|107|8|3|42|42298.20|0.02|0.08|N|O|1997-12-03|1997-10-12|1997-12-15|NONE|TRUCK| of the pend| +3716|165|10|4|19|20238.04|0.05|0.08|N|O|1997-09-25|1997-10-18|1997-10-12|NONE|TRUCK|arefully unusual accounts. flu| +3716|182|3|5|25|27054.50|0.06|0.05|N|O|1997-11-23|1997-10-24|1997-11-24|COLLECT COD|REG AIR|fully unusual accounts. carefu| +3717|153|8|1|45|47391.75|0.07|0.04|N|O|1998-08-09|1998-08-18|1998-08-14|TAKE BACK RETURN|TRUCK|ests wake whithout the blithely final pl| +3717|53|5|2|3|2859.15|0.01|0.07|N|O|1998-06-09|1998-07-31|1998-06-14|NONE|REG AIR|nside the regular packages sleep| +3717|196|7|3|45|49328.55|0.05|0.08|N|O|1998-09-19|1998-07-22|1998-09-28|DELIVER IN PERSON|MAIL|s the blithely unu| +3717|69|6|4|5|4845.30|0.06|0.03|N|O|1998-09-02|1998-08-20|1998-09-26|TAKE BACK RETURN|AIR|quickly among | +3717|16|7|5|7|6412.07|0.09|0.02|N|O|1998-09-08|1998-07-18|1998-09-10|DELIVER IN PERSON|RAIL| after the packa| +3717|64|1|6|38|36634.28|0.01|0.07|N|O|1998-07-10|1998-07-08|1998-07-29|COLLECT COD|RAIL|ly about the car| +3717|106|7|7|28|28170.80|0.03|0.01|N|O|1998-07-25|1998-08-12|1998-08-16|COLLECT COD|RAIL|ts sleep q| +3718|21|10|1|40|36840.80|0.01|0.04|N|O|1996-11-20|1996-12-17|1996-12-03|DELIVER IN PERSON|MAIL|out the express deposits| +3718|163|8|2|16|17010.56|0.02|0.06|N|O|1996-11-11|1996-12-25|1996-11-12|COLLECT COD|TRUCK|slyly even accounts. blithely special acco| +3718|70|5|3|8|7760.56|0.05|0.03|N|O|1996-12-06|1996-12-06|1996-12-15|TAKE BACK RETURN|AIR| the even deposits sleep carefully b| +3719|22|5|1|35|32270.70|0.06|0.08|N|O|1997-06-11|1997-04-03|1997-06-15|TAKE BACK RETURN|TRUCK|ly foxes. pending braids haggle furio| +3719|174|4|2|2|2148.34|0.02|0.08|N|O|1997-02-17|1997-04-25|1997-03-03|NONE|REG AIR|ccounts boost carefu| +3719|182|3|3|12|12986.16|0.05|0.06|N|O|1997-06-10|1997-05-04|1997-07-09|TAKE BACK RETURN|REG AIR|grate according to the | +3719|90|1|4|13|12871.17|0.02|0.00|N|O|1997-05-03|1997-04-16|1997-05-27|TAKE BACK RETURN|SHIP|iously. regular dep| +3719|78|8|5|19|18583.33|0.06|0.08|N|O|1997-05-22|1997-03-20|1997-06-12|COLLECT COD|TRUCK|he regular ideas integrate acros| +3719|142|5|6|43|44812.02|0.03|0.08|N|O|1997-05-08|1997-04-15|1997-06-06|COLLECT COD|RAIL|the furiously special pinto bean| +3719|19|10|7|16|14704.16|0.10|0.01|N|O|1997-03-02|1997-03-18|1997-03-28|TAKE BACK RETURN|RAIL| express asymptotes. ir| +3744|195|8|1|30|32855.70|0.05|0.06|A|F|1992-05-07|1992-02-12|1992-05-17|TAKE BACK RETURN|FOB|nts among | +3745|137|8|1|18|18668.34|0.01|0.05|A|F|1993-10-17|1993-11-16|1993-11-13|DELIVER IN PERSON|SHIP| slyly bold pinto beans according to | +3746|165|6|1|37|39410.92|0.07|0.00|A|F|1994-12-29|1994-10-25|1995-01-03|COLLECT COD|FOB|e of the careful| +3746|144|7|2|28|29235.92|0.06|0.08|R|F|1994-09-20|1994-10-21|1994-09-27|DELIVER IN PERSON|FOB|s after the even, special requests| +3746|188|9|3|3|3264.54|0.10|0.01|R|F|1994-11-03|1994-12-10|1994-11-12|NONE|MAIL| the silent ideas cajole carefully | +3746|28|7|4|11|10208.22|0.00|0.05|R|F|1994-10-02|1994-11-19|1994-10-10|COLLECT COD|SHIP| ironic theodolites are among th| +3747|141|10|1|42|43727.88|0.05|0.05|N|O|1996-11-10|1996-10-19|1996-11-19|TAKE BACK RETURN|REG AIR|y. blithely fina| +3747|170|1|2|33|35315.61|0.01|0.03|N|O|1996-10-14|1996-11-12|1996-11-11|NONE|REG AIR| regular p| +3747|139|10|3|30|31173.90|0.00|0.07|N|O|1996-12-16|1996-11-15|1996-12-17|NONE|RAIL|! furiously f| +3747|33|9|4|21|19593.63|0.00|0.06|N|O|1996-11-18|1996-09-23|1996-11-26|TAKE BACK RETURN|AIR|ithely bold orbits mold furiously blit| +3747|126|5|5|32|32835.84|0.08|0.05|N|O|1996-09-10|1996-11-04|1996-10-10|DELIVER IN PERSON|MAIL|quests shall h| +3747|154|5|6|14|14758.10|0.08|0.07|N|O|1996-11-03|1996-10-29|1996-11-06|TAKE BACK RETURN|AIR|packages cajole carefu| +3747|118|2|7|23|23416.53|0.00|0.04|N|O|1996-11-08|1996-11-10|1996-12-03|NONE|REG AIR|kages are ironic| +3748|104|7|1|12|12049.20|0.06|0.01|N|O|1998-04-17|1998-04-15|1998-05-12|NONE|AIR|old reques| +3748|165|4|2|24|25563.84|0.08|0.04|N|O|1998-06-07|1998-05-02|1998-06-21|DELIVER IN PERSON|TRUCK|al deposits. blithely| +3748|197|1|3|19|20846.61|0.05|0.01|N|O|1998-04-23|1998-05-17|1998-05-23|COLLECT COD|RAIL|pinto beans run carefully quic| +3748|187|8|4|5|5435.90|0.00|0.07|N|O|1998-06-29|1998-05-06|1998-07-12|DELIVER IN PERSON|MAIL| regular accounts sleep quickly-- furious| +3748|147|4|5|21|21989.94|0.07|0.08|N|O|1998-03-30|1998-04-07|1998-04-05|TAKE BACK RETURN|MAIL|fix carefully furiously express ideas. furi| +3749|173|3|1|11|11804.87|0.07|0.05|N|O|1995-06-25|1995-05-23|1995-07-10|TAKE BACK RETURN|RAIL|egular requests along the | +3749|129|8|2|9|9262.08|0.08|0.05|A|F|1995-04-23|1995-04-18|1995-04-26|NONE|REG AIR|uses cajole blithely pla| +3749|199|2|3|31|34074.89|0.00|0.05|N|F|1995-06-11|1995-05-20|1995-06-27|COLLECT COD|REG AIR|s. foxes sleep slyly unusual grouc| +3749|131|2|4|7|7217.91|0.07|0.06|A|F|1995-03-31|1995-04-05|1995-04-11|NONE|TRUCK|he slyly ironic packages| +3749|183|4|5|14|15164.52|0.02|0.00|N|F|1995-06-11|1995-05-19|1995-07-11|DELIVER IN PERSON|SHIP|press instruc| +3749|54|6|6|10|9540.50|0.10|0.03|N|O|1995-06-24|1995-05-24|1995-07-18|COLLECT COD|SHIP|essly. regular pi| +3750|134|10|1|37|38262.81|0.04|0.03|N|O|1995-07-08|1995-07-28|1995-07-28|DELIVER IN PERSON|REG AIR|usly busy account| +3750|152|3|2|33|34720.95|0.05|0.03|N|O|1995-06-27|1995-06-20|1995-07-03|TAKE BACK RETURN|REG AIR|theodolites haggle. slyly pendin| +3750|80|10|3|20|19601.60|0.09|0.05|N|F|1995-06-17|1995-06-06|1995-06-28|TAKE BACK RETURN|REG AIR|ss, ironic requests! fur| +3750|166|1|4|33|35183.28|0.04|0.03|N|F|1995-06-15|1995-06-04|1995-06-29|COLLECT COD|RAIL|ep blithely according to the flu| +3750|83|4|5|1|983.08|0.05|0.01|N|O|1995-07-24|1995-06-25|1995-08-21|DELIVER IN PERSON|REG AIR|l dolphins against the slyly| +3750|113|7|6|47|47616.17|0.01|0.08|R|F|1995-05-11|1995-06-13|1995-06-02|TAKE BACK RETURN|FOB|slowly regular accounts. blithely ev| +3751|172|2|1|37|39670.29|0.00|0.04|R|F|1994-04-30|1994-05-30|1994-05-30|NONE|REG AIR|ly express courts | +3751|141|8|2|32|33316.48|0.03|0.05|R|F|1994-05-05|1994-07-02|1994-06-02|COLLECT COD|MAIL|rthogs could have to slee| +3751|65|2|3|45|43427.70|0.08|0.06|R|F|1994-05-27|1994-06-19|1994-06-14|NONE|RAIL|according to | +3751|14|4|4|39|35646.39|0.07|0.01|A|F|1994-08-16|1994-07-11|1994-09-12|COLLECT COD|TRUCK|refully according to the iro| +3751|58|3|5|12|11496.60|0.02|0.03|A|F|1994-08-09|1994-06-30|1994-08-12|TAKE BACK RETURN|TRUCK|accounts wake furious| +3751|76|5|6|39|38066.73|0.02|0.08|R|F|1994-08-01|1994-06-01|1994-08-26|COLLECT COD|SHIP|to beans. pending, express packages c| +3776|3|10|1|39|35217.00|0.05|0.01|R|F|1993-01-03|1993-02-05|1993-01-08|COLLECT COD|FOB|yly blithely pending packages| +3776|159|4|2|14|14828.10|0.06|0.08|R|F|1992-12-30|1993-02-12|1993-01-27|DELIVER IN PERSON|RAIL|y special ideas. express packages pr| +3776|141|8|3|49|51015.86|0.01|0.08|R|F|1992-12-03|1993-02-16|1992-12-28|TAKE BACK RETURN|RAIL|equests. final, thin grouches | +3776|92|6|4|49|48612.41|0.08|0.05|A|F|1993-02-11|1993-01-06|1993-02-27|COLLECT COD|MAIL|es: careful warthogs haggle fluffi| +3777|100|4|1|11|11001.10|0.02|0.03|A|F|1994-04-09|1994-06-05|1994-04-14|NONE|FOB|ld ideas. even theodolites| +3777|8|5|2|10|9080.00|0.03|0.01|R|F|1994-05-22|1994-05-29|1994-06-13|COLLECT COD|RAIL|le. ironic depths a| +3777|166|7|3|18|19190.88|0.10|0.06|R|F|1994-05-04|1994-05-23|1994-05-22|COLLECT COD|REG AIR|eful packages use slyly: even deposits | +3777|18|9|4|35|32130.35|0.10|0.04|A|F|1994-05-25|1994-05-26|1994-06-13|COLLECT COD|AIR|s. carefully express asymptotes accordi| +3777|98|10|5|14|13973.26|0.04|0.05|R|F|1994-05-06|1994-06-24|1994-05-31|NONE|TRUCK|ording to the iro| +3778|57|2|1|21|20098.05|0.01|0.06|R|F|1993-05-27|1993-07-10|1993-06-03|COLLECT COD|REG AIR|ts. blithely special theodoli| +3778|29|10|2|32|29728.64|0.09|0.00|A|F|1993-06-22|1993-08-18|1993-07-03|TAKE BACK RETURN|MAIL|tes affix carefully above the | +3778|94|6|3|41|40757.69|0.05|0.00|R|F|1993-06-21|1993-07-27|1993-07-15|COLLECT COD|FOB|e the furiously ironi| +3778|169|4|4|28|29936.48|0.03|0.05|R|F|1993-08-18|1993-07-10|1993-09-06|TAKE BACK RETURN|REG AIR|y silent orbits print carefully against | +3778|98|2|5|28|27946.52|0.01|0.06|R|F|1993-09-02|1993-08-08|1993-10-02|DELIVER IN PERSON|FOB|r deposits. theodol| +3778|20|7|6|26|23920.52|0.00|0.01|A|F|1993-09-24|1993-07-06|1993-10-22|NONE|TRUCK| against the fluffily| +3778|105|6|7|49|49249.90|0.02|0.04|A|F|1993-06-13|1993-08-08|1993-07-04|DELIVER IN PERSON|MAIL|ans. furiously | +3779|46|5|1|28|26489.12|0.04|0.05|N|O|1997-05-06|1997-04-01|1997-05-18|TAKE BACK RETURN|AIR|s. close requests sleep| +3779|110|3|2|5|5050.55|0.07|0.03|N|O|1997-01-07|1997-03-26|1997-02-05|DELIVER IN PERSON|AIR|heodolites. slyly regular a| +3780|127|8|1|25|25678.00|0.08|0.04|N|O|1996-06-27|1996-07-02|1996-07-22|NONE|AIR|l, unusual | +3780|190|1|2|40|43607.60|0.10|0.04|N|O|1996-06-06|1996-05-29|1996-07-01|COLLECT COD|SHIP|gular deposits-- furiously regular | +3781|14|5|1|48|43872.48|0.02|0.06|N|O|1996-08-22|1996-08-13|1996-09-15|NONE|REG AIR|equests may cajole careful| +3781|188|9|2|39|42439.02|0.10|0.00|N|O|1996-08-20|1996-08-16|1996-09-01|DELIVER IN PERSON|REG AIR|unts are carefully. ir| +3781|30|1|3|17|15810.51|0.01|0.03|N|O|1996-06-23|1996-09-04|1996-07-19|TAKE BACK RETURN|REG AIR|. theodolite| +3781|31|2|4|15|13965.45|0.05|0.00|N|O|1996-08-23|1996-08-08|1996-09-06|TAKE BACK RETURN|AIR| carefully blithe| +3781|16|6|5|23|21068.23|0.09|0.08|N|O|1996-09-05|1996-08-18|1996-09-27|DELIVER IN PERSON|SHIP|pendencies are b| +3782|27|10|1|29|26883.58|0.01|0.07|N|O|1996-09-17|1996-10-03|1996-10-07|DELIVER IN PERSON|REG AIR|quickly unusual pinto beans. carefully fina| +3782|153|1|2|10|10531.50|0.03|0.05|N|O|1996-09-07|1996-11-19|1996-10-04|COLLECT COD|FOB|ven pinto b| +3782|136|7|3|30|31083.90|0.06|0.06|N|O|1996-12-19|1996-10-31|1997-01-14|TAKE BACK RETURN|MAIL|slyly even pinto beans hag| +3782|117|7|4|34|34581.74|0.02|0.06|N|O|1996-11-07|1996-10-22|1996-11-19|DELIVER IN PERSON|MAIL|gage after the even| +3782|130|3|5|40|41205.20|0.09|0.04|N|O|1996-12-16|1996-11-22|1997-01-01|COLLECT COD|AIR|s instructions. regular accou| +3783|167|4|1|36|38417.76|0.04|0.08|R|F|1993-12-17|1994-02-26|1994-01-03|DELIVER IN PERSON|SHIP|ites haggle among the carefully unusu| +3783|73|3|2|36|35030.52|0.02|0.02|R|F|1994-03-02|1994-02-09|1994-03-15|COLLECT COD|TRUCK|egular accounts| +3783|85|6|3|50|49254.00|0.04|0.01|R|F|1994-03-14|1994-01-09|1994-04-10|DELIVER IN PERSON|FOB|he furiously regular deposits. | +3783|27|6|4|37|34299.74|0.10|0.05|R|F|1993-12-09|1994-02-17|1993-12-30|COLLECT COD|REG AIR|ing to the ideas. regular accounts de| +3808|43|10|1|28|26405.12|0.02|0.01|R|F|1994-05-27|1994-06-18|1994-06-22|TAKE BACK RETURN|FOB|lly final accounts alo| +3808|127|6|2|47|48274.64|0.04|0.08|R|F|1994-06-12|1994-06-03|1994-07-02|COLLECT COD|TRUCK|fully for the quickly final deposits: flu| +3808|31|2|3|45|41896.35|0.00|0.03|R|F|1994-07-03|1994-05-29|1994-07-14|TAKE BACK RETURN|REG AIR| carefully special| +3808|100|1|4|34|34003.40|0.07|0.04|R|F|1994-08-13|1994-07-22|1994-08-31|DELIVER IN PERSON|FOB| pearls will have to | +3808|155|7|5|29|30599.35|0.08|0.03|A|F|1994-06-22|1994-05-26|1994-07-06|TAKE BACK RETURN|TRUCK| deposits across the pac| +3808|168|5|6|44|46999.04|0.06|0.06|A|F|1994-06-07|1994-06-04|1994-06-25|NONE|REG AIR|the blithely regular foxes. even, final | +3809|191|3|1|17|18550.23|0.10|0.04|N|O|1996-08-14|1996-07-05|1996-09-04|DELIVER IN PERSON|FOB|es detect furiously sil| +3809|133|4|2|32|33060.16|0.01|0.02|N|O|1996-07-03|1996-06-01|1996-07-25|COLLECT COD|SHIP|xcuses would boost against the fluffily eve| +3809|105|6|3|46|46234.60|0.10|0.06|N|O|1996-08-20|1996-06-01|1996-08-24|TAKE BACK RETURN|TRUCK|l asymptotes. special | +3809|178|9|4|43|46361.31|0.00|0.04|N|O|1996-05-06|1996-06-22|1996-06-05|TAKE BACK RETURN|TRUCK|yly ironic decoys; regular, iron| +3810|184|5|1|49|53124.82|0.05|0.01|R|F|1992-11-27|1992-10-30|1992-12-16|COLLECT COD|AIR|cajole. fur| +3810|169|8|2|18|19244.88|0.01|0.04|A|F|1992-11-28|1992-11-15|1992-12-27|DELIVER IN PERSON|SHIP|s. furiously careful deposi| +3810|137|3|3|41|42522.33|0.08|0.08|A|F|1992-10-26|1992-10-27|1992-11-05|COLLECT COD|SHIP|l requests boost slyly along the slyl| +3810|182|3|4|11|11903.98|0.06|0.04|A|F|1992-12-18|1992-12-11|1993-01-15|DELIVER IN PERSON|MAIL| the pending pinto beans. expr| +3811|164|3|1|24|25539.84|0.04|0.02|N|O|1998-07-13|1998-05-16|1998-08-12|TAKE BACK RETURN|TRUCK|deposits. slyly regular accounts cajo| +3811|166|5|2|2|2132.32|0.01|0.08|N|O|1998-06-16|1998-06-16|1998-06-23|NONE|MAIL|slyly fluff| +3811|43|6|3|19|17917.76|0.02|0.06|N|O|1998-07-20|1998-06-14|1998-07-29|NONE|MAIL|s boost blithely furiou| +3811|171|1|4|50|53558.50|0.08|0.03|N|O|1998-07-28|1998-07-06|1998-08-16|COLLECT COD|FOB|ts are slyly fluffy ideas. furiou| +3811|182|3|5|23|24890.14|0.00|0.04|N|O|1998-08-13|1998-07-09|1998-08-29|COLLECT COD|AIR|nstructions sleep quickly. slyly final | +3811|2|7|6|35|31570.00|0.04|0.07|N|O|1998-04-17|1998-06-30|1998-04-25|NONE|REG AIR|yly final dolphins? quickly ironic frets| +3812|145|4|1|33|34489.62|0.00|0.05|N|O|1996-10-10|1996-10-05|1996-10-15|TAKE BACK RETURN|MAIL|posits engage. ironic, regular p| +3812|173|2|2|33|35414.61|0.06|0.03|N|O|1996-10-05|1996-10-13|1996-10-22|TAKE BACK RETURN|MAIL|inal excuses d| +3813|176|7|1|37|39818.29|0.05|0.04|N|O|1998-10-13|1998-09-19|1998-10-28|NONE|REG AIR|ravely special packages haggle p| +3813|123|2|2|39|39901.68|0.05|0.00|N|O|1998-08-30|1998-08-12|1998-09-29|COLLECT COD|FOB|y ideas. final ideas about the sp| +3814|131|7|1|7|7217.91|0.02|0.02|R|F|1995-05-01|1995-05-09|1995-05-28|DELIVER IN PERSON|REG AIR|es sleep furiou| +3814|173|3|2|14|15024.38|0.01|0.00|R|F|1995-03-17|1995-05-10|1995-04-16|DELIVER IN PERSON|AIR|sits along the final, ironic deposit| +3814|168|7|3|36|38453.76|0.06|0.02|N|O|1995-06-19|1995-04-18|1995-06-28|COLLECT COD|SHIP|beans cajole quickly sl| +3814|66|7|4|20|19321.20|0.04|0.07|R|F|1995-02-23|1995-03-26|1995-03-04|DELIVER IN PERSON|SHIP|. doggedly ironic deposits will have to wa| +3814|107|2|5|15|15106.50|0.03|0.04|N|O|1995-06-23|1995-03-25|1995-07-09|COLLECT COD|SHIP| carefully final deposits haggle slyly| +3814|83|4|6|47|46204.76|0.09|0.05|A|F|1995-04-16|1995-04-03|1995-05-14|DELIVER IN PERSON|AIR|nusual requests. bli| +3814|132|8|7|12|12385.56|0.10|0.01|R|F|1995-03-18|1995-04-16|1995-03-20|TAKE BACK RETURN|REG AIR|ages cajole. packages haggle. final| +3815|77|7|1|3|2931.21|0.07|0.00|N|O|1997-11-16|1997-11-15|1997-11-30|NONE|FOB|egular, express ideas. ironic, final dep| +3815|130|5|2|11|11331.43|0.02|0.04|N|O|1997-11-01|1997-11-05|1997-11-27|COLLECT COD|TRUCK|sleep blithe| +3840|187|8|1|45|48923.10|0.02|0.08|N|O|1998-10-31|1998-09-19|1998-11-30|DELIVER IN PERSON|TRUCK|o beans are. carefully final courts x| +3840|46|9|2|12|11352.48|0.04|0.07|N|O|1998-10-02|1998-08-19|1998-10-20|TAKE BACK RETURN|RAIL|xpress pinto beans. accounts a| +3840|73|4|3|45|43788.15|0.02|0.05|N|O|1998-10-12|1998-10-12|1998-10-28|TAKE BACK RETURN|FOB|onic, even packages are. pe| +3840|148|9|4|41|42973.74|0.07|0.02|N|O|1998-07-21|1998-10-08|1998-08-01|TAKE BACK RETURN|MAIL| nag slyly? slyly pending accounts | +3840|173|3|5|7|7512.19|0.09|0.08|N|O|1998-09-17|1998-09-20|1998-10-14|DELIVER IN PERSON|MAIL|. furiously final gifts sleep carefully pin| +3840|107|8|6|33|33234.30|0.10|0.02|N|O|1998-07-29|1998-10-06|1998-08-04|DELIVER IN PERSON|SHIP|hely silent deposits w| +3841|157|5|1|1|1057.15|0.06|0.03|A|F|1994-10-10|1994-11-12|1994-10-21|DELIVER IN PERSON|AIR| boost even re| +3841|21|10|2|31|28551.62|0.09|0.03|A|F|1995-01-24|1994-11-25|1995-02-20|TAKE BACK RETURN|SHIP|n theodolites shall promise carefully. qui| +3841|152|10|3|40|42086.00|0.06|0.02|A|F|1995-02-02|1994-11-30|1995-02-14|TAKE BACK RETURN|MAIL|its. quickly regular ideas nag carefully| +3841|50|1|4|9|8550.45|0.10|0.07|A|F|1994-11-21|1994-12-26|1994-11-26|NONE|FOB|s according to the courts shall nag s| +3841|176|7|5|3|3228.51|0.04|0.02|R|F|1994-10-24|1994-12-07|1994-11-09|COLLECT COD|FOB|foxes integrate | +3841|163|8|6|48|51031.68|0.03|0.00|R|F|1994-11-23|1994-11-22|1994-12-01|DELIVER IN PERSON|FOB| according to the regular, | +3842|162|7|1|28|29740.48|0.05|0.07|A|F|1992-06-17|1992-06-03|1992-06-24|DELIVER IN PERSON|TRUCK|s excuses thrash carefully.| +3842|122|1|2|21|21464.52|0.07|0.05|R|F|1992-07-15|1992-06-02|1992-07-21|NONE|RAIL|r pinto be| +3842|194|7|3|28|30637.32|0.00|0.00|A|F|1992-06-20|1992-05-22|1992-07-13|DELIVER IN PERSON|MAIL|lly alongside of the| +3842|88|9|4|15|14821.20|0.07|0.01|A|F|1992-06-26|1992-06-23|1992-07-09|COLLECT COD|MAIL|ave packages are slyl| +3842|68|3|5|13|12584.78|0.09|0.02|R|F|1992-04-13|1992-06-22|1992-05-11|COLLECT COD|RAIL|t blithely. busily regular accounts alon| +3842|107|4|6|24|24170.40|0.08|0.08|R|F|1992-08-05|1992-06-29|1992-08-16|TAKE BACK RETURN|MAIL|phins are quickly| +3843|15|6|1|7|6405.07|0.10|0.03|N|O|1997-02-13|1997-02-21|1997-02-20|TAKE BACK RETURN|SHIP|slyly even instructions. furiously eve| +3843|1|4|2|30|27030.00|0.01|0.05|N|O|1997-02-14|1997-03-25|1997-03-13|DELIVER IN PERSON|AIR| wake. slyly even packages boost | +3844|135|1|1|2|2070.26|0.03|0.07|R|F|1995-02-24|1995-02-03|1995-03-18|TAKE BACK RETURN|AIR|es haggle final acco| +3844|102|7|2|5|5010.50|0.10|0.03|R|F|1995-04-29|1995-02-24|1995-05-05|TAKE BACK RETURN|RAIL| unwind quickly about the pending, i| +3845|34|5|1|44|41097.32|0.01|0.08|A|F|1992-07-20|1992-07-15|1992-07-24|DELIVER IN PERSON|REG AIR|s haggle among the fluffily regula| +3845|24|7|2|16|14784.32|0.09|0.05|A|F|1992-08-08|1992-06-08|1992-08-26|DELIVER IN PERSON|SHIP|ely bold ideas use. ex| +3845|59|1|3|17|16303.85|0.08|0.01|A|F|1992-06-12|1992-07-05|1992-06-26|TAKE BACK RETURN|RAIL|counts haggle. reg| +3845|46|9|4|1|946.04|0.04|0.05|R|F|1992-05-21|1992-06-07|1992-06-17|COLLECT COD|REG AIR| blithely ironic t| +3845|196|7|5|27|29597.13|0.00|0.05|R|F|1992-08-20|1992-07-17|1992-09-02|COLLECT COD|REG AIR|kages. care| +3845|105|8|6|30|30153.00|0.09|0.06|R|F|1992-08-21|1992-07-07|1992-08-25|COLLECT COD|FOB|counts do wake blithely. ironic requests | +3846|61|10|1|15|14415.90|0.06|0.03|N|O|1998-02-17|1998-04-27|1998-02-21|NONE|REG AIR|uternes. carefully even| +3846|171|2|2|30|32135.10|0.08|0.07|N|O|1998-05-01|1998-03-12|1998-05-20|TAKE BACK RETURN|FOB|deposits according to the fur| +3846|15|5|3|49|44835.49|0.08|0.07|N|O|1998-02-14|1998-03-22|1998-02-17|DELIVER IN PERSON|RAIL|efully even packages against the blithe| +3846|165|10|4|33|35150.28|0.05|0.00|N|O|1998-05-12|1998-03-14|1998-05-14|DELIVER IN PERSON|TRUCK|s instructions are. fu| +3847|189|10|1|7|7624.26|0.08|0.00|A|F|1993-05-06|1993-06-06|1993-05-22|COLLECT COD|MAIL| about the blithely daring Tiresias. fl| +3872|181|2|1|28|30273.04|0.10|0.04|N|O|1996-11-05|1996-11-10|1996-11-24|DELIVER IN PERSON|REG AIR|t after the carefully ironic excuses. f| +3872|17|4|2|38|34846.38|0.04|0.05|N|O|1996-10-18|1996-12-03|1996-11-15|TAKE BACK RETURN|AIR|iously against the ironic, unusual a| +3872|169|4|3|18|19244.88|0.07|0.07|N|O|1996-12-25|1996-10-24|1997-01-08|TAKE BACK RETURN|SHIP|s. regular, brave accounts sleep blith| +3872|11|2|4|41|37351.41|0.07|0.03|N|O|1996-11-23|1996-11-12|1996-12-03|COLLECT COD|REG AIR|ly regular epitaphs boost| +3872|70|7|5|42|40742.94|0.03|0.00|N|O|1997-01-03|1996-10-12|1997-01-16|COLLECT COD|MAIL|s the furio| +3872|140|6|6|40|41605.60|0.07|0.05|N|O|1997-01-02|1996-10-29|1997-01-14|NONE|REG AIR|nts? regularly ironic ex| +3873|68|3|1|19|18393.14|0.04|0.04|N|O|1998-05-15|1998-05-10|1998-05-17|NONE|FOB|y final ac| +3873|145|8|2|44|45986.16|0.05|0.05|N|O|1998-07-23|1998-05-22|1998-08-14|COLLECT COD|AIR|yly even platelets wake. | +3873|140|6|3|29|30164.06|0.01|0.04|N|O|1998-06-22|1998-05-20|1998-07-05|COLLECT COD|REG AIR|olphins af| +3874|170|7|1|21|22473.57|0.09|0.08|R|F|1993-06-19|1993-07-20|1993-07-08|DELIVER IN PERSON|SHIP| requests cajole fluff| +3874|19|6|2|48|44112.48|0.06|0.07|R|F|1993-06-13|1993-07-20|1993-06-20|NONE|RAIL| ideas throughout | +3875|81|2|1|24|23545.92|0.02|0.08|N|O|1997-10-15|1997-11-27|1997-11-09|COLLECT COD|AIR|ecial packages. | +3875|113|7|2|49|49642.39|0.04|0.04|N|O|1997-10-18|1997-10-13|1997-10-19|NONE|MAIL|sleep furiously about the deposits. quickl| +3876|141|8|1|12|12493.68|0.06|0.07|N|O|1996-09-16|1996-10-23|1996-10-05|TAKE BACK RETURN|REG AIR|y above the pending tithes. blithely ironi| +3876|140|6|2|37|38485.18|0.00|0.03|N|O|1996-11-30|1996-10-18|1996-12-18|DELIVER IN PERSON|AIR|t dependencies. blithely final packages u| +3876|127|8|3|41|42111.92|0.02|0.04|N|O|1996-10-15|1996-10-17|1996-10-19|NONE|AIR| quickly blit| +3877|50|7|1|12|11400.60|0.06|0.01|R|F|1993-05-30|1993-08-09|1993-06-24|TAKE BACK RETURN|FOB|nal requests. even requests are. pac| +3877|145|4|2|47|49121.58|0.05|0.00|A|F|1993-08-01|1993-08-16|1993-08-04|NONE|FOB|furiously quick requests nag along the theo| +3877|80|8|3|44|43123.52|0.09|0.00|A|F|1993-06-07|1993-07-15|1993-07-06|DELIVER IN PERSON|REG AIR|elets. quickly regular accounts caj| +3877|148|9|4|36|37733.04|0.06|0.01|A|F|1993-07-27|1993-07-13|1993-08-11|DELIVER IN PERSON|AIR|lithely about the dogged ideas. ac| +3877|5|6|5|41|37105.00|0.03|0.07|A|F|1993-06-30|1993-07-20|1993-07-01|DELIVER IN PERSON|FOB|integrate against the expres| +3877|123|4|6|7|7161.84|0.04|0.08|R|F|1993-06-14|1993-07-09|1993-06-28|NONE|TRUCK|lar dolphins cajole silently | +3878|200|1|1|6|6601.20|0.07|0.04|N|O|1997-06-21|1997-05-22|1997-07-01|COLLECT COD|FOB|s. regular instru| +3878|88|9|2|13|12845.04|0.01|0.06|N|O|1997-06-08|1997-06-03|1997-06-25|TAKE BACK RETURN|TRUCK|leep ruthlessly about the carefu| +3878|41|8|3|20|18820.80|0.08|0.03|N|O|1997-06-20|1997-05-24|1997-07-20|TAKE BACK RETURN|MAIL|the furiously careful ideas cajole slyly sl| +3878|152|3|4|20|21043.00|0.01|0.07|N|O|1997-07-13|1997-05-22|1997-07-20|NONE|FOB|about the carefully ironic pa| +3879|126|5|1|45|46175.40|0.10|0.08|N|O|1996-03-18|1996-01-03|1996-04-03|COLLECT COD|RAIL|ly according to the expr| +3879|45|4|2|35|33076.40|0.00|0.07|N|O|1995-12-08|1996-01-23|1995-12-28|TAKE BACK RETURN|MAIL|o beans. accounts cajole furiously. re| +3904|38|4|1|22|20636.66|0.04|0.03|N|O|1998-02-02|1998-02-09|1998-02-10|TAKE BACK RETURN|REG AIR|structions cajole carefully. carefully f| +3904|184|5|2|19|20599.42|0.09|0.01|N|O|1998-02-10|1998-02-13|1998-02-20|TAKE BACK RETURN|AIR| excuses sleep slyly according to th| +3905|101|8|1|43|43047.30|0.07|0.08|A|F|1994-03-30|1994-02-18|1994-04-09|DELIVER IN PERSON|REG AIR|uses are care| +3905|116|10|2|7|7112.77|0.03|0.00|R|F|1994-03-01|1994-02-19|1994-03-11|DELIVER IN PERSON|AIR|ully furiously furious packag| +3905|170|7|3|6|6421.02|0.07|0.02|R|F|1994-04-07|1994-03-07|1994-04-21|DELIVER IN PERSON|RAIL|ow furiously. deposits wake ironic | +3906|153|1|1|42|44232.30|0.00|0.04|R|F|1992-09-03|1992-07-22|1992-09-04|COLLECT COD|RAIL|jole blithely after the furiously regular | +3906|40|1|2|50|47002.00|0.01|0.07|R|F|1992-09-24|1992-08-24|1992-09-29|NONE|MAIL|ke slyly. stealt| +3906|180|9|3|15|16202.70|0.06|0.02|R|F|1992-07-30|1992-08-26|1992-08-02|TAKE BACK RETURN|FOB|dependencies at the | +3906|59|10|4|36|34525.80|0.08|0.08|A|F|1992-08-07|1992-08-08|1992-08-24|NONE|SHIP|y. ironic deposits haggle sl| +3907|112|6|1|41|41496.51|0.06|0.02|A|F|1992-09-13|1992-10-23|1992-09-29|COLLECT COD|MAIL|ackages wake along the carefully regul| +3907|145|4|2|41|42850.74|0.03|0.00|A|F|1992-10-25|1992-10-17|1992-11-01|TAKE BACK RETURN|RAIL|s above the unusual ideas sleep furiousl| +3907|52|4|3|45|42842.25|0.02|0.07|R|F|1992-09-21|1992-09-19|1992-10-18|COLLECT COD|REG AIR| about the regular pac| +3907|176|5|4|48|51656.16|0.05|0.07|A|F|1992-09-24|1992-10-16|1992-10-06|DELIVER IN PERSON|TRUCK|nt asymptotes lose across th| +3907|62|3|5|22|21165.32|0.09|0.01|R|F|1992-09-20|1992-10-30|1992-09-29|TAKE BACK RETURN|TRUCK|ly. furiously unusual deposits use afte| +3907|126|9|6|34|34888.08|0.02|0.02|R|F|1992-09-06|1992-10-08|1992-09-12|COLLECT COD|FOB| requests according to the slyly pending | +3907|110|5|7|8|8080.88|0.10|0.01|A|F|1992-09-18|1992-10-29|1992-09-27|NONE|REG AIR|furiously final packages.| +3908|92|4|1|50|49604.50|0.05|0.04|R|F|1993-06-19|1993-04-27|1993-07-05|DELIVER IN PERSON|MAIL| even accounts wake | +3908|148|9|2|8|8385.12|0.06|0.03|A|F|1993-03-12|1993-04-13|1993-03-22|DELIVER IN PERSON|SHIP|r instructions was requests. ironically | +3909|178|6|1|30|32345.10|0.03|0.07|N|O|1998-10-17|1998-10-14|1998-10-28|COLLECT COD|TRUCK|ly even deposits across the ironic notorni| +3909|191|4|2|46|50194.74|0.03|0.01|N|O|1998-10-08|1998-10-15|1998-10-24|NONE|FOB|the blithely unusual ideas| +3910|139|10|1|10|10391.30|0.00|0.08|N|O|1996-10-18|1996-10-31|1996-11-14|DELIVER IN PERSON|FOB|tions boost furiously unusual e| +3910|71|10|2|31|30103.17|0.05|0.03|N|O|1996-12-22|1996-11-14|1997-01-01|TAKE BACK RETURN|SHIP|ess instructions. | +3910|20|7|3|6|5520.12|0.04|0.04|N|O|1996-12-08|1996-10-30|1996-12-31|DELIVER IN PERSON|MAIL|ly sly platelets are fluffily slyly si| +3910|153|1|4|1|1053.15|0.03|0.06|N|O|1996-09-12|1996-10-21|1996-09-19|DELIVER IN PERSON|FOB|s sleep neve| +3911|113|7|1|10|10131.10|0.07|0.06|N|O|1995-06-22|1995-05-30|1995-06-28|COLLECT COD|FOB|ss theodolites are blithely along t| +3911|119|9|2|14|14267.54|0.08|0.05|R|F|1995-04-28|1995-05-03|1995-05-22|NONE|RAIL|e blithely brave depo| +3911|92|5|3|12|11905.08|0.10|0.05|R|F|1995-04-04|1995-04-16|1995-04-10|COLLECT COD|FOB|uctions. blithely regula| +3936|137|8|1|25|25928.25|0.06|0.03|N|O|1996-12-03|1996-12-27|1997-01-01|DELIVER IN PERSON|RAIL|gular requests nag quic| +3936|188|9|2|24|26116.32|0.10|0.07|N|O|1996-11-22|1997-01-01|1996-12-08|NONE|AIR|ns. accounts mold fl| +3936|83|4|3|42|41289.36|0.00|0.07|N|O|1997-01-03|1997-01-29|1997-01-14|COLLECT COD|AIR|elets wake amo| +3936|62|7|4|12|11544.72|0.06|0.05|N|O|1996-11-25|1997-01-09|1996-12-06|DELIVER IN PERSON|SHIP|ithely across the carefully brave req| +3936|84|5|5|35|34442.80|0.02|0.08|N|O|1996-12-04|1997-01-06|1996-12-22|NONE|SHIP|lly ironic requ| +3936|103|6|6|26|26080.60|0.01|0.02|N|O|1997-02-27|1997-01-16|1997-03-22|NONE|RAIL|quickly pen| +3937|70|7|1|48|46563.36|0.10|0.02|N|O|1998-03-15|1998-02-22|1998-03-30|DELIVER IN PERSON|FOB|gainst the thinl| +3937|48|1|2|30|28441.20|0.01|0.07|N|O|1998-01-17|1998-01-03|1998-02-08|COLLECT COD|TRUCK|al packages slee| +3937|115|5|3|27|27407.97|0.03|0.00|N|O|1998-02-06|1998-01-12|1998-02-20|NONE|MAIL|ven ideas. slyly expr| +3937|154|2|4|50|52707.50|0.01|0.02|N|O|1998-01-15|1998-01-09|1998-02-04|DELIVER IN PERSON|AIR|ong the carefully exp| +3937|3|10|5|29|26187.00|0.03|0.07|N|O|1998-03-06|1998-02-22|1998-03-14|NONE|TRUCK|nt pinto beans above the pending instr| +3937|193|6|6|6|6559.14|0.00|0.00|N|O|1998-01-24|1998-02-13|1998-01-27|DELIVER IN PERSON|FOB|into beans. slyly silent orbits alongside o| +3937|164|9|7|1|1064.16|0.02|0.05|N|O|1998-03-29|1998-01-08|1998-04-27|TAKE BACK RETURN|TRUCK|refully agains| +3938|159|4|1|46|48720.90|0.10|0.07|R|F|1993-05-20|1993-05-04|1993-06-12|DELIVER IN PERSON|FOB|ly even foxes are slyly fu| +3939|160|8|1|8|8481.28|0.03|0.06|N|O|1996-01-29|1996-04-05|1996-02-26|COLLECT COD|REG AIR|e packages. express, pen| +3940|178|7|1|33|35579.61|0.10|0.07|N|O|1996-05-19|1996-04-19|1996-05-23|TAKE BACK RETURN|RAIL|ly ironic packages about the pending accou| +3940|69|4|2|40|38762.40|0.08|0.02|N|O|1996-02-29|1996-03-22|1996-03-04|NONE|MAIL|ts. regular fox| +3940|89|10|3|8|7912.64|0.07|0.08|N|O|1996-04-04|1996-04-12|1996-04-18|DELIVER IN PERSON|RAIL|ions cajole furiously regular pinto beans. | +3940|137|3|4|11|11408.43|0.09|0.05|N|O|1996-03-09|1996-05-13|1996-03-17|COLLECT COD|REG AIR|e of the special packages. furiously| +3940|1|6|5|41|36941.00|0.00|0.07|N|O|1996-05-08|1996-05-03|1996-06-03|COLLECT COD|MAIL|thily. deposits cajole.| +3941|41|2|1|47|44228.88|0.05|0.07|N|O|1996-11-24|1996-10-09|1996-12-22|DELIVER IN PERSON|RAIL| carefully pending| +3941|123|6|2|19|19439.28|0.05|0.00|N|O|1996-11-10|1996-10-26|1996-12-05|COLLECT COD|RAIL|eposits haggle furiously even| +3941|10|3|3|2|1820.02|0.01|0.03|N|O|1996-12-04|1996-10-01|1996-12-25|NONE|REG AIR|es wake after the| +3941|110|7|4|29|29293.19|0.00|0.03|N|O|1996-09-14|1996-10-04|1996-09-19|NONE|MAIL|g the blithely| +3942|183|4|1|6|6499.08|0.05|0.05|A|F|1993-07-01|1993-09-14|1993-07-23|DELIVER IN PERSON|SHIP|ep ruthlessly carefully final accounts: s| +3942|194|7|2|5|5470.95|0.06|0.02|R|F|1993-09-27|1993-09-24|1993-10-07|DELIVER IN PERSON|MAIL|. fluffily pending deposits above the flu| +3942|156|4|3|25|26403.75|0.04|0.06|R|F|1993-09-13|1993-08-01|1993-09-29|COLLECT COD|RAIL|d the quick packages| +3943|198|2|1|15|16472.85|0.03|0.01|N|O|1997-01-13|1996-12-17|1997-02-02|COLLECT COD|REG AIR| grow fluffily according to the | +3943|96|7|2|9|8964.81|0.00|0.06|N|O|1996-11-27|1997-01-03|1996-12-17|COLLECT COD|RAIL|refully ironic | +3943|17|4|3|32|29344.32|0.00|0.02|N|O|1996-10-22|1996-12-17|1996-11-04|TAKE BACK RETURN|TRUCK| unusual ideas into the furiously even pack| +3943|50|1|4|5|4750.25|0.04|0.04|N|O|1997-01-09|1996-11-10|1997-02-06|COLLECT COD|RAIL|arefully regular deposits accord| +3968|54|2|1|27|25759.35|0.04|0.05|N|O|1997-04-25|1997-04-17|1997-05-11|TAKE BACK RETURN|MAIL|t silently.| +3968|26|9|2|45|41670.90|0.00|0.07|N|O|1997-06-18|1997-04-24|1997-06-25|DELIVER IN PERSON|FOB|ully slyly fi| +3968|156|7|3|43|45414.45|0.07|0.06|N|O|1997-04-30|1997-05-14|1997-05-18|TAKE BACK RETURN|SHIP|ly regular accounts| +3968|61|8|4|7|6727.42|0.07|0.02|N|O|1997-03-30|1997-05-01|1997-04-12|DELIVER IN PERSON|SHIP|efully bold instructions. express| +3969|52|4|1|39|37129.95|0.04|0.04|N|O|1997-06-12|1997-06-13|1997-07-05|NONE|MAIL|ly bold ideas s| +3969|197|1|2|26|28526.94|0.05|0.03|N|O|1997-07-08|1997-07-30|1997-07-10|TAKE BACK RETURN|AIR|fluffily; braids detect.| +3969|79|8|3|46|45037.22|0.04|0.02|N|O|1997-05-29|1997-06-15|1997-06-10|TAKE BACK RETURN|SHIP|fully final requests sleep stealthily. care| +3969|151|9|4|21|22074.15|0.07|0.04|N|O|1997-08-31|1997-07-16|1997-09-02|TAKE BACK RETURN|MAIL|unts doze quickly final reque| +3969|72|3|5|40|38882.80|0.09|0.00|N|O|1997-05-19|1997-08-02|1997-06-05|COLLECT COD|TRUCK|lar requests cajole furiously blithely regu| +3969|105|8|6|4|4020.40|0.02|0.01|N|O|1997-06-04|1997-07-31|1997-06-13|COLLECT COD|REG AIR|dencies wake blithely? quickly even theodo| +3970|88|9|1|2|1976.16|0.01|0.07|R|F|1992-04-24|1992-06-03|1992-05-16|TAKE BACK RETURN|RAIL|carefully pending foxes wake blithely | +3970|109|6|2|18|18163.80|0.03|0.08|A|F|1992-06-06|1992-06-18|1992-07-05|DELIVER IN PERSON|TRUCK| maintain slyly. ir| +3970|154|6|3|10|10541.50|0.10|0.04|A|F|1992-07-01|1992-05-31|1992-07-02|NONE|AIR| special packages wake after the final br| +3970|22|5|4|34|31348.68|0.05|0.00|A|F|1992-06-25|1992-05-23|1992-07-12|COLLECT COD|SHIP|y final gifts are. carefully pe| +3970|30|3|5|23|21390.69|0.05|0.04|A|F|1992-06-04|1992-06-14|1992-06-13|COLLECT COD|TRUCK| above the final braids. regular| +3970|9|6|6|46|41814.00|0.07|0.04|R|F|1992-04-29|1992-05-14|1992-05-24|NONE|FOB|yly ironic| +3970|5|8|7|46|41630.00|0.08|0.08|R|F|1992-05-02|1992-05-12|1992-05-10|COLLECT COD|MAIL|ix slyly. quickly silen| +3971|96|8|1|47|46816.23|0.06|0.04|N|O|1996-07-07|1996-08-08|1996-08-01|TAKE BACK RETURN|RAIL|e slyly final dependencies x-ray | +3971|191|5|2|2|2182.38|0.04|0.03|N|O|1996-07-15|1996-08-12|1996-07-26|NONE|SHIP|haggle abou| +3972|51|3|1|2|1902.10|0.05|0.03|A|F|1994-07-24|1994-06-30|1994-08-13|TAKE BACK RETURN|SHIP|y final theodolite| +3973|30|9|1|21|19530.63|0.02|0.06|R|F|1992-06-18|1992-06-03|1992-07-02|COLLECT COD|REG AIR|equests. furiously| +3973|115|2|2|37|37559.07|0.07|0.00|A|F|1992-05-29|1992-05-04|1992-06-23|TAKE BACK RETURN|SHIP|inos wake fluffily. pending requests nag | +3973|40|6|3|40|37601.60|0.08|0.05|R|F|1992-05-03|1992-06-09|1992-05-21|COLLECT COD|RAIL|g the carefully blithe f| +3974|22|1|1|47|43334.94|0.10|0.03|N|O|1996-06-03|1996-05-08|1996-06-28|NONE|TRUCK|dencies above the re| +3974|61|8|2|17|16338.02|0.05|0.07|N|O|1996-04-05|1996-05-21|1996-04-28|COLLECT COD|TRUCK|ions eat slyly after the blithely | +3975|57|9|1|38|36367.90|0.01|0.05|N|O|1995-08-02|1995-06-18|1995-08-19|COLLECT COD|TRUCK|es are furiously: furi| +4000|196|7|1|41|44943.79|0.06|0.01|A|F|1992-03-02|1992-03-14|1992-03-27|COLLECT COD|FOB|ve the even, fi| +4000|75|5|2|44|42903.08|0.09|0.06|A|F|1992-03-27|1992-02-18|1992-03-31|COLLECT COD|AIR|equests use blithely blithely bold d| +4001|106|1|1|26|26158.60|0.00|0.01|N|O|1997-07-26|1997-06-18|1997-08-08|DELIVER IN PERSON|RAIL|tegrate blithely| +4001|41|10|2|19|17879.76|0.03|0.02|N|O|1997-08-23|1997-06-15|1997-09-18|COLLECT COD|SHIP|ackages. carefully ironi| +4001|94|5|3|18|17893.62|0.07|0.00|N|O|1997-06-04|1997-06-22|1997-06-13|DELIVER IN PERSON|MAIL|lithely ironic d| +4001|2|9|4|39|35178.00|0.00|0.00|N|O|1997-06-13|1997-06-17|1997-06-25|NONE|SHIP| dogged excuses. blithe| +4002|111|5|1|35|35388.85|0.01|0.08|N|O|1997-05-16|1997-06-15|1997-06-02|DELIVER IN PERSON|TRUCK|eep. quickly| +4002|198|9|2|20|21963.80|0.00|0.03|N|O|1997-06-15|1997-05-20|1997-07-11|NONE|MAIL|lly even ins| +4002|40|1|3|6|5640.24|0.08|0.07|N|O|1997-05-02|1997-07-07|1997-05-16|TAKE BACK RETURN|RAIL| furiously furiously special theodoli| +4002|199|3|4|6|6595.14|0.06|0.06|N|O|1997-07-01|1997-05-15|1997-07-31|NONE|MAIL|he slyly iro| +4002|99|1|5|4|3996.36|0.08|0.07|N|O|1997-05-06|1997-06-15|1997-05-24|NONE|REG AIR|ccording to the careful| +4003|52|4|1|18|17136.90|0.04|0.07|R|F|1993-02-02|1993-04-15|1993-02-28|TAKE BACK RETURN|AIR|ar grouches s| +4004|121|2|1|23|23485.76|0.07|0.02|A|F|1993-08-12|1993-07-13|1993-08-16|TAKE BACK RETURN|TRUCK| bold theodolites? special packages accordi| +4004|64|5|2|47|45310.82|0.07|0.04|R|F|1993-06-25|1993-08-03|1993-07-12|NONE|SHIP|thely instead of the even, unu| +4004|114|5|3|39|39550.29|0.10|0.05|R|F|1993-07-12|1993-07-27|1993-07-18|NONE|MAIL|ccounts sleep furious| +4004|74|4|4|46|44807.22|0.10|0.04|R|F|1993-09-04|1993-07-13|1993-09-28|COLLECT COD|FOB|ncies. slyly pending dolphins sleep furio| +4004|155|3|5|9|9496.35|0.04|0.06|A|F|1993-08-25|1993-06-10|1993-09-24|COLLECT COD|MAIL|ly ironic requests. quickly pending ide| +4004|161|10|6|44|46691.04|0.07|0.05|R|F|1993-07-25|1993-07-23|1993-08-16|TAKE BACK RETURN|REG AIR|ut the sauternes. bold, ironi| +4004|126|9|7|20|20522.40|0.07|0.05|A|F|1993-06-19|1993-06-14|1993-07-04|COLLECT COD|REG AIR|. ironic deposits cajole blithely?| +4005|4|1|1|26|23504.00|0.09|0.05|N|O|1996-12-01|1997-02-03|1996-12-15|NONE|REG AIR| to the quic| +4005|17|8|2|28|25676.28|0.02|0.06|N|O|1996-12-11|1997-01-24|1996-12-17|DELIVER IN PERSON|REG AIR|ly carefully ironic deposits. slyly| +4005|72|10|3|28|27217.96|0.03|0.01|N|O|1996-12-08|1997-01-14|1996-12-30|TAKE BACK RETURN|MAIL|y pending dependenc| +4005|15|9|4|49|44835.49|0.09|0.00|N|O|1997-01-31|1996-12-24|1997-03-02|NONE|RAIL|tions sleep across the silent d| +4005|6|7|5|14|12684.00|0.09|0.08|N|O|1996-11-27|1997-01-09|1996-12-25|NONE|TRUCK|ld requests. slyly final instructi| +4006|55|7|1|11|10505.55|0.05|0.08|A|F|1995-04-29|1995-02-21|1995-05-20|TAKE BACK RETURN|RAIL|ress foxes cajole quick| +4006|159|4|2|18|19064.70|0.05|0.03|A|F|1995-01-29|1995-03-08|1995-02-02|TAKE BACK RETURN|MAIL|gouts! slyly iron| +4006|24|5|3|15|13860.30|0.01|0.02|R|F|1995-02-23|1995-04-02|1995-02-25|TAKE BACK RETURN|RAIL|n deposits cajole slyl| +4006|114|5|4|25|25352.75|0.00|0.07|A|F|1995-02-23|1995-02-09|1995-02-24|DELIVER IN PERSON|SHIP| requests use depos| +4007|57|2|1|32|30625.60|0.00|0.03|R|F|1993-09-30|1993-08-16|1993-10-03|DELIVER IN PERSON|RAIL|nal accounts across t| +4007|116|10|2|41|41660.51|0.04|0.06|A|F|1993-10-11|1993-08-30|1993-11-04|DELIVER IN PERSON|TRUCK|eposits. regular epitaphs boost blithely.| +4007|102|9|3|5|5010.50|0.09|0.06|A|F|1993-09-17|1993-08-29|1993-10-12|TAKE BACK RETURN|FOB|y unusual packa| +4007|138|4|4|15|15571.95|0.05|0.02|A|F|1993-09-01|1993-07-19|1993-09-03|DELIVER IN PERSON|FOB|le furiously quickly | +4007|26|7|5|23|21298.46|0.02|0.07|A|F|1993-10-08|1993-09-09|1993-10-23|COLLECT COD|MAIL|ter the accounts. expr| +4032|102|3|1|8|8016.80|0.06|0.00|N|O|1998-06-04|1998-05-17|1998-07-03|TAKE BACK RETURN|RAIL|ometimes even cou| +4032|2|9|2|27|24354.00|0.09|0.00|N|O|1998-05-31|1998-04-19|1998-06-24|COLLECT COD|REG AIR|le furiously according to| +4032|154|2|3|23|24245.45|0.09|0.06|N|O|1998-06-12|1998-05-11|1998-06-24|COLLECT COD|MAIL|ording to the | +4032|85|6|4|10|9850.80|0.09|0.05|N|O|1998-03-31|1998-04-22|1998-04-07|NONE|REG AIR| carefully bol| +4033|110|1|1|27|27272.97|0.01|0.04|R|F|1993-08-08|1993-08-14|1993-08-09|NONE|AIR|pinto beans| +4033|38|4|2|34|31893.02|0.07|0.00|R|F|1993-07-19|1993-08-05|1993-07-26|NONE|RAIL|t the blithely dogg| +4034|190|1|1|48|52329.12|0.03|0.03|A|F|1994-03-01|1994-01-16|1994-03-16|NONE|RAIL| blithely regular requests play carefull| +4034|57|5|2|47|44981.35|0.07|0.05|A|F|1994-01-27|1993-12-26|1994-02-04|NONE|TRUCK|eodolites was slyly ironic ideas. de| +4034|54|5|3|43|41024.15|0.10|0.03|A|F|1993-11-29|1994-01-08|1993-12-10|DELIVER IN PERSON|FOB|posits wake carefully af| +4034|28|9|4|46|42688.92|0.06|0.00|A|F|1994-02-22|1994-01-09|1994-03-04|DELIVER IN PERSON|AIR|uests. furiously unusual instructions wake| +4034|196|10|5|7|7673.33|0.07|0.06|R|F|1994-03-04|1994-01-22|1994-04-01|NONE|AIR|y even theodolites. slyly regular instru| +4034|50|9|6|5|4750.25|0.01|0.06|A|F|1994-02-12|1994-01-24|1994-02-13|COLLECT COD|AIR|fully around the furiously ironic re| +4035|97|8|1|4|3988.36|0.08|0.03|R|F|1992-04-21|1992-04-23|1992-04-25|COLLECT COD|AIR|ilent, even pear| +4035|136|7|2|4|4144.52|0.07|0.00|A|F|1992-05-21|1992-04-24|1992-05-24|DELIVER IN PERSON|FOB|en instructions sleep blith| +4035|118|8|3|1|1018.11|0.03|0.01|R|F|1992-06-18|1992-05-19|1992-07-02|COLLECT COD|FOB| requests. quickly | +4035|182|3|4|13|14068.34|0.00|0.01|R|F|1992-06-10|1992-05-16|1992-07-10|NONE|SHIP|s. furiously even courts wake slyly| +4036|6|1|1|46|41676.00|0.09|0.00|N|O|1997-06-21|1997-05-29|1997-07-18|NONE|REG AIR|usly across the even th| +4036|53|1|2|21|20014.05|0.09|0.07|N|O|1997-08-08|1997-06-28|1997-08-09|COLLECT COD|MAIL|e carefully. qui| +4036|142|3|3|6|6252.84|0.07|0.01|N|O|1997-06-19|1997-06-16|1997-07-01|DELIVER IN PERSON|SHIP|equests wake about the bold id| +4036|127|10|4|20|20542.40|0.08|0.02|N|O|1997-08-11|1997-07-11|1997-09-03|NONE|TRUCK|slyly bold deposits cajole pending, blithe| +4037|64|9|1|32|30849.92|0.00|0.06|A|F|1993-05-06|1993-06-08|1993-05-31|DELIVER IN PERSON|AIR|e of the pending, iron| +4037|47|8|2|4|3788.16|0.09|0.07|A|F|1993-07-05|1993-06-12|1993-08-03|DELIVER IN PERSON|RAIL|s around the blithely ironic ac| +4038|196|10|1|40|43847.60|0.05|0.01|N|O|1996-01-15|1996-03-13|1996-01-25|COLLECT COD|TRUCK|t. slyly silent pinto beans amo| +4038|12|9|2|37|33744.37|0.04|0.03|N|O|1996-03-17|1996-03-19|1996-04-07|DELIVER IN PERSON|REG AIR| packages | +4038|32|3|3|24|22368.72|0.10|0.04|N|O|1996-04-06|1996-02-15|1996-04-18|TAKE BACK RETURN|RAIL|the furiously regu| +4038|150|1|4|29|30454.35|0.07|0.06|N|O|1996-01-07|1996-03-08|1996-01-13|NONE|FOB|ffix. quietly ironic packages a| +4038|79|7|5|24|23497.68|0.07|0.06|N|O|1996-04-01|1996-04-05|1996-04-28|DELIVER IN PERSON|TRUCK|ake quickly after the final, ironic ac| +4038|36|2|6|6|5616.18|0.07|0.05|N|O|1996-02-09|1996-03-05|1996-03-10|COLLECT COD|SHIP| special instructions. packa| +4039|94|5|1|38|37775.42|0.03|0.06|N|O|1998-03-09|1997-12-31|1998-03-21|DELIVER IN PERSON|REG AIR|sual asymptotes. ironic deposits nag aft| +4039|122|5|2|17|17376.04|0.10|0.04|N|O|1998-01-15|1998-01-20|1998-01-28|TAKE BACK RETURN|MAIL| regular foxes haggle carefully bo| +4039|64|1|3|9|8676.54|0.10|0.01|N|O|1998-03-08|1998-02-05|1998-04-05|TAKE BACK RETURN|FOB|t? pinto beans cajole across the thinly r| +4039|28|3|4|43|39904.86|0.01|0.02|N|O|1998-01-02|1997-12-22|1998-01-15|NONE|FOB|beans believe bene| +4039|134|5|5|43|44467.59|0.09|0.00|N|O|1998-01-20|1998-01-11|1998-02-05|COLLECT COD|SHIP|sts along the regular in| +4064|199|1|1|3|3297.57|0.10|0.04|N|O|1997-01-04|1997-01-01|1997-01-23|NONE|SHIP|its! quickly sp| +4064|40|6|2|15|14100.60|0.02|0.02|N|O|1996-11-09|1996-12-04|1996-11-18|DELIVER IN PERSON|MAIL|braids affix across the regular sheave| +4064|197|10|3|32|35110.08|0.04|0.07|N|O|1997-01-14|1997-01-01|1997-01-21|COLLECT COD|REG AIR|es boost. careful| +4064|163|8|4|24|25515.84|0.02|0.02|N|O|1997-01-01|1996-12-31|1997-01-23|DELIVER IN PERSON|SHIP|ly regular ideas.| +4064|21|2|5|12|11052.24|0.08|0.08|N|O|1997-02-08|1996-12-18|1997-03-06|TAKE BACK RETURN|RAIL|ding to the requests| +4064|184|5|6|46|49872.28|0.03|0.00|N|O|1996-10-13|1997-01-05|1996-11-06|DELIVER IN PERSON|REG AIR|alongside of the f| +4064|200|2|7|9|9901.80|0.01|0.06|N|O|1996-12-17|1996-12-13|1997-01-12|NONE|AIR|furiously f| +4065|138|9|1|14|14533.82|0.04|0.02|A|F|1994-08-22|1994-07-29|1994-09-19|DELIVER IN PERSON|TRUCK|e furiously outside | +4065|15|6|2|46|42090.46|0.03|0.05|A|F|1994-06-29|1994-08-01|1994-07-03|TAKE BACK RETURN|SHIP|, regular requests may mold above the | +4065|97|10|3|33|32903.97|0.00|0.03|A|F|1994-09-03|1994-08-16|1994-09-13|DELIVER IN PERSON|AIR|ain blithely | +4065|107|2|4|8|8056.80|0.00|0.01|R|F|1994-10-04|1994-08-05|1994-10-25|TAKE BACK RETURN|SHIP|ages haggle carefully| +4065|123|4|5|29|29670.48|0.02|0.07|A|F|1994-06-29|1994-08-19|1994-07-17|NONE|RAIL|equests. packages sleep slyl| +4065|110|5|6|16|16161.76|0.05|0.00|R|F|1994-08-25|1994-08-06|1994-09-09|COLLECT COD|TRUCK|ncies use furiously. quickly un| +4065|144|7|7|11|11485.54|0.10|0.04|A|F|1994-07-25|1994-08-02|1994-07-30|NONE|RAIL|hang silently about | +4066|139|5|1|9|9352.17|0.01|0.05|N|O|1997-05-06|1997-03-25|1997-05-27|COLLECT COD|FOB|nal, ironic accounts. blithel| +4066|93|5|2|19|18868.71|0.05|0.00|N|O|1997-05-13|1997-04-17|1997-06-08|NONE|TRUCK|quests. slyly regu| +4066|76|5|3|8|7808.56|0.03|0.03|N|O|1997-04-24|1997-03-11|1997-05-20|NONE|REG AIR|accounts. special pinto beans| +4066|179|9|4|49|52879.33|0.01|0.01|N|O|1997-02-17|1997-03-24|1997-02-19|NONE|TRUCK|ial braids. furiously final deposits sl| +4066|171|2|5|43|46060.31|0.05|0.02|N|O|1997-02-16|1997-04-14|1997-02-18|DELIVER IN PERSON|MAIL|r instructions. slyly special | +4066|109|2|6|44|44400.40|0.01|0.00|N|O|1997-03-01|1997-04-27|1997-03-29|DELIVER IN PERSON|MAIL|express accounts nag bli| +4067|180|1|1|18|19443.24|0.03|0.08|A|F|1993-01-24|1992-12-23|1993-02-20|TAKE BACK RETURN|FOB|e the slyly final packages d| +4067|96|10|2|14|13945.26|0.00|0.00|R|F|1993-02-03|1992-12-02|1993-02-07|TAKE BACK RETURN|TRUCK|ructions. quickly ironic accounts detect | +4067|141|10|3|17|17699.38|0.03|0.05|A|F|1993-01-26|1992-11-23|1993-01-27|NONE|REG AIR|ts haggle slyly unusual, final| +4067|90|1|4|40|39603.60|0.07|0.08|R|F|1993-01-09|1992-11-21|1993-01-16|DELIVER IN PERSON|TRUCK|lar theodolites nag blithely above the| +4067|85|6|5|17|16746.36|0.08|0.03|A|F|1993-01-20|1992-12-29|1993-02-03|DELIVER IN PERSON|REG AIR|r accounts. slyly special pa| +4067|96|8|6|12|11953.08|0.04|0.03|A|F|1992-12-12|1992-11-28|1992-12-15|DELIVER IN PERSON|AIR|lly slyly even theodol| +4067|83|4|7|17|16712.36|0.01|0.01|R|F|1992-12-12|1992-12-23|1992-12-30|NONE|AIR|ts affix. regular, regular requests s| +4068|110|1|1|43|43434.73|0.05|0.06|N|O|1996-11-28|1996-11-16|1996-12-22|NONE|AIR|ructions. regular, special packag| +4068|57|5|2|31|29668.55|0.08|0.03|N|O|1996-12-11|1996-12-07|1996-12-30|NONE|SHIP|ds wake carefully amon| +4069|129|2|1|39|40135.68|0.09|0.02|R|F|1992-09-06|1992-07-22|1992-09-25|COLLECT COD|SHIP|ven theodolites nag quickly. fluffi| +4069|43|4|2|32|30177.28|0.10|0.08|A|F|1992-06-18|1992-07-20|1992-07-07|TAKE BACK RETURN|TRUCK|unts. deposit| +4069|186|7|3|3|3258.54|0.06|0.01|R|F|1992-07-26|1992-07-07|1992-08-04|COLLECT COD|FOB|l packages. even, | +4069|79|8|4|22|21539.54|0.10|0.05|A|F|1992-08-05|1992-08-04|1992-08-25|COLLECT COD|SHIP|ts. slyly special instruction| +4069|157|5|5|50|52857.50|0.09|0.06|A|F|1992-07-26|1992-06-30|1992-08-01|TAKE BACK RETURN|REG AIR|even foxes among the express wate| +4069|125|8|6|3|3075.36|0.02|0.01|A|F|1992-05-24|1992-06-18|1992-06-12|COLLECT COD|MAIL|y final deposits wake furiously! slyl| +4069|184|5|7|50|54209.00|0.00|0.01|R|F|1992-09-03|1992-06-14|1992-10-01|NONE|REG AIR|ages. carefully regular | +4070|183|4|1|2|2166.36|0.09|0.08|N|O|1995-08-03|1995-09-10|1995-08-17|TAKE BACK RETURN|REG AIR|ptotes affix| +4070|155|3|2|40|42206.00|0.07|0.07|N|O|1995-07-13|1995-07-23|1995-08-06|COLLECT COD|MAIL|about the sentiments. quick| +4070|62|3|3|11|10582.66|0.00|0.08|N|O|1995-08-23|1995-08-15|1995-08-31|TAKE BACK RETURN|MAIL| carefully final pack| +4070|29|4|4|46|42734.92|0.02|0.02|N|O|1995-06-22|1995-07-14|1995-07-11|DELIVER IN PERSON|REG AIR|nticing ideas. boldly| +4071|112|2|1|22|22266.42|0.02|0.07|N|O|1996-10-31|1996-12-14|1996-11-05|NONE|REG AIR|sits cajole carefully final instructio| +4071|18|8|2|47|43146.47|0.00|0.03|N|O|1996-11-04|1996-12-09|1996-11-16|NONE|TRUCK|ts cajole furiously along the| +4096|27|10|1|31|28737.62|0.10|0.02|A|F|1992-07-14|1992-09-03|1992-07-31|COLLECT COD|TRUCK|y final, even platelets. boldly| +4096|57|9|2|17|16269.85|0.07|0.03|R|F|1992-09-30|1992-08-11|1992-10-11|TAKE BACK RETURN|REG AIR|platelets alongside of the | +4096|9|10|3|21|19089.00|0.08|0.00|A|F|1992-08-24|1992-09-04|1992-09-11|DELIVER IN PERSON|MAIL|tes mold flu| +4096|128|3|4|20|20562.40|0.02|0.07|R|F|1992-08-24|1992-09-13|1992-08-28|DELIVER IN PERSON|TRUCK|sual requests. furiously bold packages wake| +4097|74|5|1|50|48703.50|0.04|0.04|N|O|1996-08-31|1996-08-14|1996-09-27|DELIVER IN PERSON|MAIL|egular deposits. blithely pending| +4097|74|4|2|46|44807.22|0.10|0.01|N|O|1996-07-29|1996-08-19|1996-08-25|COLLECT COD|AIR| even depend| +4097|174|2|3|42|45115.14|0.06|0.06|N|O|1996-08-11|1996-07-30|1996-08-15|NONE|FOB|carefully silent foxes are against the | +4098|200|1|1|46|50609.20|0.07|0.03|N|O|1997-01-26|1997-01-27|1997-02-13|TAKE BACK RETURN|SHIP|e slyly blithely silent deposits. fluff| +4099|4|7|1|29|26216.00|0.09|0.07|R|F|1992-11-21|1992-11-04|1992-11-30|NONE|FOB| slowly final warthogs sleep blithely. q| +4099|137|3|2|3|3111.39|0.04|0.06|A|F|1992-09-12|1992-10-18|1992-10-01|NONE|RAIL|. special packages sleep| +4099|51|3|3|36|34237.80|0.06|0.06|R|F|1992-11-06|1992-09-28|1992-12-02|NONE|FOB|beans cajole slyly quickly ironic | +4099|139|5|4|7|7273.91|0.05|0.02|A|F|1992-09-12|1992-11-13|1992-09-14|TAKE BACK RETURN|AIR|onic foxes. quickly final fox| +4099|163|10|5|48|51031.68|0.00|0.02|R|F|1992-10-18|1992-10-14|1992-11-01|NONE|REG AIR|ts haggle according to the slyly f| +4099|59|10|6|39|37402.95|0.07|0.02|R|F|1992-12-13|1992-11-13|1992-12-26|DELIVER IN PERSON|REG AIR|fluffy accounts impress pending, iro| +4099|180|8|7|46|49688.28|0.06|0.07|R|F|1992-10-29|1992-11-03|1992-11-10|DELIVER IN PERSON|REG AIR|ages nag requests.| +4100|74|5|1|4|3896.28|0.03|0.03|N|O|1996-06-20|1996-04-29|1996-06-21|TAKE BACK RETURN|FOB|lyly regular, bold requ| +4101|115|2|1|22|22332.42|0.05|0.02|R|F|1994-02-02|1994-02-19|1994-02-12|COLLECT COD|AIR|ly express instructions. careful| +4102|10|3|1|17|15470.17|0.02|0.02|N|O|1996-06-03|1996-05-06|1996-07-02|COLLECT COD|AIR|ly silent theodolites sleep unusual exc| +4102|69|8|2|5|4845.30|0.08|0.02|N|O|1996-05-11|1996-05-11|1996-05-16|COLLECT COD|AIR| the furiously even| +4102|67|4|3|39|37715.34|0.08|0.01|N|O|1996-04-14|1996-05-18|1996-04-20|DELIVER IN PERSON|AIR|ffix blithely slyly special | +4102|140|6|4|39|40565.46|0.02|0.00|N|O|1996-06-15|1996-06-06|1996-06-30|DELIVER IN PERSON|SHIP|y among the furiously special| +4102|1|6|5|32|28832.00|0.08|0.01|N|O|1996-05-14|1996-04-29|1996-05-29|NONE|RAIL| the even requests; regular pinto| +4102|137|8|6|7|7259.91|0.02|0.01|N|O|1996-06-19|1996-05-21|1996-07-15|NONE|REG AIR|bove the carefully pending the| +4103|75|4|1|40|39002.80|0.05|0.03|R|F|1992-09-19|1992-08-14|1992-09-21|COLLECT COD|RAIL|usly across the slyly busy accounts! fin| +4128|196|8|1|5|5480.95|0.04|0.04|N|O|1995-10-18|1995-11-28|1995-10-28|TAKE BACK RETURN|FOB|ake permanently | +4129|56|8|1|32|30593.60|0.03|0.04|A|F|1993-09-16|1993-08-25|1993-09-25|TAKE BACK RETURN|MAIL|ckages haggl| +4129|27|6|2|39|36153.78|0.06|0.07|R|F|1993-10-21|1993-08-04|1993-10-29|COLLECT COD|MAIL|y regular foxes. slyly ironic deposits | +4130|178|6|1|44|47439.48|0.07|0.04|N|O|1996-05-14|1996-04-15|1996-05-15|COLLECT COD|TRUCK|eaves haggle qui| +4130|63|10|2|2|1926.12|0.05|0.06|N|O|1996-05-19|1996-04-24|1996-06-17|TAKE BACK RETURN|RAIL|uriously regular instructions around th| +4131|50|7|1|6|5700.30|0.05|0.01|N|O|1998-04-27|1998-04-18|1998-04-29|TAKE BACK RETURN|MAIL|ns cajole slyly. even, iro| +4131|178|8|2|32|34501.44|0.08|0.01|N|O|1998-03-02|1998-03-21|1998-03-07|TAKE BACK RETURN|TRUCK| furiously regular asymptotes nod sly| +4131|26|9|3|25|23150.50|0.02|0.07|N|O|1998-02-24|1998-03-01|1998-02-27|TAKE BACK RETURN|FOB|uickly exp| +4131|36|7|4|8|7488.24|0.04|0.01|N|O|1998-03-03|1998-03-15|1998-03-26|COLLECT COD|FOB| after the furiously ironic d| +4131|125|6|5|30|30753.60|0.01|0.01|N|O|1998-04-01|1998-04-13|1998-04-08|TAKE BACK RETURN|FOB|he fluffily express depen| +4131|102|7|6|47|47098.70|0.02|0.00|N|O|1998-03-09|1998-04-05|1998-03-13|TAKE BACK RETURN|RAIL|ges. ironic pinto be| +4132|138|4|1|28|29067.64|0.07|0.03|N|O|1995-08-16|1995-08-01|1995-08-29|TAKE BACK RETURN|SHIP|pths wake against the stealthily special pi| +4132|15|5|2|23|21045.23|0.07|0.07|N|O|1995-06-27|1995-07-27|1995-07-13|TAKE BACK RETURN|FOB|d deposits. fluffily even requests haggle b| +4132|87|8|3|18|17767.44|0.09|0.04|A|F|1995-06-01|1995-08-01|1995-06-02|TAKE BACK RETURN|RAIL|y final de| +4133|24|5|1|35|32340.70|0.02|0.00|A|F|1992-11-25|1992-09-15|1992-12-25|NONE|AIR|g above the quickly bold packages. ev| +4134|121|4|1|34|34718.08|0.02|0.05|R|F|1995-04-29|1995-03-13|1995-05-11|DELIVER IN PERSON|FOB|e furiously regular sheaves sleep| +4134|96|10|2|34|33867.06|0.01|0.03|A|F|1995-05-06|1995-03-28|1995-05-13|DELIVER IN PERSON|SHIP|ual asymptotes wake carefully alo| +4134|171|9|3|12|12854.04|0.05|0.04|A|F|1995-03-19|1995-03-27|1995-04-14|COLLECT COD|TRUCK|kly above the quickly regular | +4134|100|4|4|45|45004.50|0.08|0.02|A|F|1995-04-11|1995-03-27|1995-04-17|TAKE BACK RETURN|MAIL|ironic pin| +4135|2|3|1|23|20746.00|0.06|0.01|N|O|1997-04-09|1997-05-12|1997-04-16|TAKE BACK RETURN|FOB|posits cajole furiously carefully| +4135|120|1|2|32|32643.84|0.07|0.00|N|O|1997-03-14|1997-04-23|1997-04-12|TAKE BACK RETURN|TRUCK| ideas. requests use. furiously| +4135|160|5|3|33|34985.28|0.05|0.05|N|O|1997-05-01|1997-05-23|1997-05-23|DELIVER IN PERSON|AIR|he fluffil| +4135|195|6|4|13|14237.47|0.04|0.07|N|O|1997-03-16|1997-05-19|1997-04-03|COLLECT COD|RAIL|efully special account| +4160|113|10|1|25|25327.75|0.10|0.04|N|O|1996-09-22|1996-10-17|1996-09-24|NONE|SHIP|ar accounts sleep blithe| +4160|122|7|2|12|12265.44|0.00|0.03|N|O|1996-11-22|1996-09-25|1996-12-10|DELIVER IN PERSON|REG AIR|y bold package| +4160|63|4|3|48|46226.88|0.04|0.04|N|O|1996-09-19|1996-11-02|1996-09-24|COLLECT COD|FOB| unusual dolphins | +4161|122|7|1|12|12265.44|0.08|0.02|R|F|1993-08-25|1993-10-04|1993-09-22|COLLECT COD|RAIL|onic dolphins. in| +4161|28|3|2|47|43616.94|0.05|0.00|A|F|1993-12-20|1993-10-29|1994-01-19|TAKE BACK RETURN|RAIL|r requests about the final, even foxes hag| +4161|138|4|3|42|43601.46|0.03|0.04|R|F|1993-11-12|1993-10-04|1993-11-27|COLLECT COD|MAIL|thely across the even attainments. express| +4161|10|5|4|45|40950.45|0.02|0.06|A|F|1993-10-22|1993-10-17|1993-10-30|COLLECT COD|REG AIR|about the ironic packages cajole blithe| +4161|29|10|5|46|42734.92|0.05|0.01|A|F|1993-11-09|1993-11-17|1993-11-17|TAKE BACK RETURN|TRUCK|he stealthily ironic foxes. ideas haggl| +4161|148|9|6|19|19914.66|0.07|0.00|R|F|1993-08-22|1993-11-11|1993-09-01|TAKE BACK RETURN|REG AIR|beans breach s| +4162|74|3|1|45|43833.15|0.10|0.07|A|F|1992-03-21|1992-05-02|1992-03-29|DELIVER IN PERSON|AIR|elets. slyly regular i| +4162|90|1|2|29|28712.61|0.00|0.05|R|F|1992-02-25|1992-04-25|1992-03-17|NONE|REG AIR|nding pinto beans haggle blithe| +4163|33|4|1|13|12129.39|0.08|0.03|A|F|1993-02-17|1993-03-13|1993-03-15|DELIVER IN PERSON|REG AIR|phins wake. pending requests inte| +4164|120|7|1|9|9181.08|0.07|0.02|N|O|1998-08-25|1998-08-13|1998-09-19|DELIVER IN PERSON|SHIP|re fluffily slyly bold requests. | +4165|41|2|1|12|11292.48|0.00|0.01|N|O|1997-09-20|1997-10-20|1997-10-12|TAKE BACK RETURN|REG AIR|nwind slow theodolites. carefully pending | +4166|141|10|1|8|8329.12|0.00|0.08|A|F|1993-06-05|1993-04-10|1993-07-05|COLLECT COD|MAIL|uickly. blithely pending de| +4166|93|5|2|8|7944.72|0.06|0.04|A|F|1993-06-07|1993-04-17|1993-06-16|DELIVER IN PERSON|REG AIR|es along the furiously regular acc| +4166|7|10|3|17|15419.00|0.02|0.06|R|F|1993-06-29|1993-05-15|1993-07-24|DELIVER IN PERSON|SHIP|ackages. re| +4166|86|7|4|36|35498.88|0.06|0.05|R|F|1993-03-01|1993-05-25|1993-03-05|COLLECT COD|MAIL|unts. furiously express accounts w| +4166|77|6|5|5|4885.35|0.08|0.01|A|F|1993-06-19|1993-04-24|1993-06-27|NONE|REG AIR|hely unusual packages are above the f| +4166|102|5|6|6|6012.60|0.04|0.08|R|F|1993-04-30|1993-04-17|1993-05-08|DELIVER IN PERSON|MAIL|ily ironic deposits print furiously. iron| +4166|24|5|7|26|24024.52|0.09|0.01|R|F|1993-03-17|1993-05-09|1993-03-25|NONE|MAIL|lar dependencies. s| +4167|61|8|1|47|45169.82|0.04|0.02|N|O|1998-08-02|1998-08-24|1998-08-28|DELIVER IN PERSON|REG AIR| carefully final asymptotes. slyly bo| +4167|87|8|2|17|16780.36|0.06|0.07|N|O|1998-09-18|1998-09-06|1998-10-07|COLLECT COD|REG AIR|ly around the even instr| +4167|73|3|3|1|973.07|0.03|0.06|N|O|1998-10-11|1998-08-14|1998-10-13|COLLECT COD|TRUCK|xpress platelets. blithely | +4192|11|1|1|36|32796.36|0.06|0.08|N|O|1998-04-25|1998-05-26|1998-05-03|COLLECT COD|TRUCK|eodolites sleep| +4192|121|6|2|15|15316.80|0.04|0.08|N|O|1998-06-26|1998-05-26|1998-07-16|COLLECT COD|AIR|e slyly special grouches. express pinto b| +4192|135|6|3|7|7245.91|0.06|0.03|N|O|1998-05-19|1998-07-08|1998-05-31|COLLECT COD|FOB|y; excuses use. ironic, close instru| +4192|24|3|4|32|29568.64|0.09|0.04|N|O|1998-06-23|1998-06-25|1998-07-17|NONE|FOB|ounts are fluffily slyly bold req| +4192|48|7|5|48|45505.92|0.08|0.01|N|O|1998-08-17|1998-07-11|1998-09-03|NONE|AIR|ests. quickly bol| +4192|150|7|6|44|46206.60|0.10|0.02|N|O|1998-08-06|1998-07-09|1998-08-20|NONE|FOB|structions mai| +4192|170|5|7|27|28894.59|0.02|0.00|N|O|1998-07-03|1998-06-26|1998-07-13|TAKE BACK RETURN|AIR| carefully even escapades. care| +4193|131|7|1|37|38151.81|0.09|0.06|A|F|1994-04-25|1994-02-24|1994-05-08|NONE|AIR|er the quickly regular dependencies wake| +4193|117|7|2|3|3051.33|0.09|0.05|R|F|1994-04-29|1994-03-20|1994-05-29|TAKE BACK RETURN|REG AIR|osits above the depo| +4193|179|10|3|10|10791.70|0.06|0.03|A|F|1994-02-10|1994-03-22|1994-03-09|COLLECT COD|RAIL|uffily spe| +4193|51|9|4|29|27580.45|0.09|0.05|A|F|1994-02-11|1994-03-11|1994-03-13|TAKE BACK RETURN|RAIL|ly. final packages use blit| +4193|20|7|5|50|46001.00|0.01|0.01|R|F|1994-04-28|1994-03-23|1994-05-09|NONE|FOB| beans. regular accounts cajole. de| +4193|66|1|6|21|20287.26|0.02|0.04|R|F|1994-04-26|1994-03-22|1994-05-23|DELIVER IN PERSON|TRUCK|accounts cajole b| +4194|197|1|1|43|47179.17|0.08|0.06|A|F|1994-11-06|1994-12-09|1994-11-16|NONE|TRUCK|olites are after the exp| +4194|47|10|2|18|17046.72|0.07|0.07|A|F|1995-02-14|1994-12-04|1995-03-11|TAKE BACK RETURN|TRUCK|ld packages. quickly eve| +4195|6|9|1|14|12684.00|0.09|0.04|R|F|1993-09-06|1993-07-21|1993-09-18|DELIVER IN PERSON|REG AIR|ironic packages. carefully express| +4195|66|1|2|22|21253.32|0.10|0.08|R|F|1993-07-01|1993-07-23|1993-07-28|COLLECT COD|RAIL|lly express pinto bea| +4195|194|8|3|19|20789.61|0.01|0.06|R|F|1993-09-06|1993-08-13|1993-09-15|TAKE BACK RETURN|REG AIR|telets sleep even requests. final, even i| +4196|156|4|1|30|31684.50|0.02|0.06|N|O|1998-08-09|1998-06-30|1998-09-05|COLLECT COD|SHIP|egular foxes us| +4196|9|6|2|31|28179.00|0.09|0.08|N|O|1998-06-12|1998-07-28|1998-07-11|NONE|MAIL|ut the blithely ironic inst| +4196|178|9|3|46|49595.82|0.05|0.00|N|O|1998-09-05|1998-06-28|1998-09-10|TAKE BACK RETURN|MAIL|according to t| +4196|114|8|4|42|42592.62|0.04|0.06|N|O|1998-08-13|1998-07-18|1998-09-07|TAKE BACK RETURN|AIR| instructions. courts cajole slyly ev| +4196|72|2|5|3|2916.21|0.01|0.03|N|O|1998-05-17|1998-07-21|1998-05-18|DELIVER IN PERSON|TRUCK| accounts. fu| +4196|87|8|6|43|42444.44|0.01|0.06|N|O|1998-08-12|1998-07-12|1998-08-22|DELIVER IN PERSON|FOB|es. slyly even | +4196|4|1|7|3|2712.00|0.00|0.06|N|O|1998-08-05|1998-07-28|1998-08-15|DELIVER IN PERSON|REG AIR|y regular packages haggle furiously alongs| +4197|129|8|1|50|51456.00|0.06|0.03|N|O|1996-11-15|1996-11-01|1996-11-20|NONE|FOB|. carefully bold asymptotes nag blithe| +4197|70|9|2|39|37832.73|0.02|0.08|N|O|1996-10-07|1996-10-11|1996-10-18|DELIVER IN PERSON|RAIL|ronic requests. quickly bold packages in| +4197|32|8|3|28|26096.84|0.06|0.02|N|O|1996-10-05|1996-10-24|1996-10-22|TAKE BACK RETURN|AIR|regular pin| +4197|96|7|4|23|22910.07|0.00|0.03|N|O|1996-09-10|1996-10-10|1996-09-25|NONE|AIR|l instructions print slyly past the reg| +4197|121|6|5|37|37781.44|0.03|0.04|N|O|1996-10-20|1996-10-10|1996-11-10|COLLECT COD|TRUCK|carefully enticing decoys boo| +4197|31|7|6|48|44689.44|0.08|0.00|N|O|1996-10-07|1996-10-25|1996-10-23|COLLECT COD|REG AIR| final instructions. blithe, spe| +4198|146|9|1|48|50214.72|0.09|0.05|N|O|1997-09-03|1997-07-18|1997-09-11|NONE|REG AIR|cajole carefully final, ironic ide| +4198|143|6|2|46|47984.44|0.09|0.01|N|O|1997-08-17|1997-09-08|1997-09-11|COLLECT COD|TRUCK|posits among th| +4198|145|4|3|13|13586.82|0.03|0.04|N|O|1997-07-18|1997-07-24|1997-08-10|NONE|REG AIR| furious excuses. bli| +4199|70|5|1|16|15521.12|0.10|0.00|A|F|1992-06-11|1992-04-10|1992-07-10|COLLECT COD|TRUCK|ncies. furiously special accounts| +4199|9|10|2|18|16362.00|0.00|0.01|A|F|1992-06-01|1992-03-30|1992-06-28|DELIVER IN PERSON|RAIL|pending, regular accounts. carefully| +4224|199|10|1|27|29678.13|0.05|0.03|N|O|1997-09-05|1997-08-19|1997-09-30|NONE|SHIP|ly special deposits sleep qui| +4224|37|3|2|20|18740.60|0.07|0.05|N|O|1997-11-09|1997-08-23|1997-11-14|NONE|FOB|unts promise across the requests. blith| +4224|24|7|3|4|3696.08|0.08|0.05|N|O|1997-09-07|1997-09-05|1997-09-25|TAKE BACK RETURN|FOB| even dinos. carefull| +4224|160|2|4|50|53008.00|0.10|0.06|N|O|1997-07-30|1997-09-10|1997-08-19|COLLECT COD|RAIL|side of the carefully silent dep| +4224|85|6|5|48|47283.84|0.00|0.04|N|O|1997-10-03|1997-08-31|1997-10-10|NONE|RAIL| final, regular asymptotes use alway| +4225|49|8|1|25|23726.00|0.08|0.04|N|O|1997-07-10|1997-08-08|1997-07-31|TAKE BACK RETURN|TRUCK|se fluffily. busily ironic requests are;| +4225|96|8|2|23|22910.07|0.02|0.04|N|O|1997-09-18|1997-08-31|1997-10-11|TAKE BACK RETURN|RAIL|. quickly b| +4225|98|10|3|28|27946.52|0.08|0.03|N|O|1997-07-11|1997-09-01|1997-08-03|DELIVER IN PERSON|FOB|ts are requests. even, bold depos| +4226|188|9|1|27|29380.86|0.06|0.08|A|F|1993-05-03|1993-04-12|1993-05-16|COLLECT COD|AIR|sly alongside of the slyly ironic pac| +4227|158|6|1|19|20104.85|0.01|0.08|A|F|1995-05-05|1995-05-03|1995-05-22|COLLECT COD|REG AIR|ns sleep along the blithely even theodolit| +4227|33|4|2|8|7464.24|0.09|0.00|N|F|1995-06-11|1995-04-30|1995-06-28|COLLECT COD|REG AIR| packages since the bold, u| +4227|75|6|3|11|10725.77|0.10|0.04|A|F|1995-03-30|1995-05-02|1995-04-26|DELIVER IN PERSON|SHIP|l requests-- bold requests cajole dogg| +4227|200|4|4|2|2200.40|0.02|0.05|R|F|1995-04-24|1995-05-09|1995-05-21|DELIVER IN PERSON|AIR|ep. specia| +4227|147|6|5|49|51309.86|0.05|0.06|R|F|1995-05-19|1995-04-12|1995-06-12|TAKE BACK RETURN|REG AIR|ts sleep blithely carefully unusual ideas.| +4228|141|10|1|20|20822.80|0.00|0.06|N|O|1997-04-24|1997-05-29|1997-05-17|NONE|RAIL|f the slyly fluffy pinto beans are| +4229|96|9|1|44|43827.96|0.02|0.05|N|O|1998-05-29|1998-05-12|1998-06-16|DELIVER IN PERSON|AIR|s. carefully e| +4229|5|8|2|34|30770.00|0.07|0.05|N|O|1998-05-26|1998-04-13|1998-06-08|DELIVER IN PERSON|MAIL|thely final accounts use even packa| +4230|46|5|1|38|35949.52|0.10|0.03|A|F|1992-04-28|1992-04-21|1992-05-28|TAKE BACK RETURN|FOB|ly regular packages. regular ideas boost| +4230|199|3|2|43|47265.17|0.02|0.08|R|F|1992-03-14|1992-05-13|1992-03-28|NONE|FOB|ses lose blithely slyly final e| +4230|196|9|3|10|10961.90|0.06|0.02|A|F|1992-06-11|1992-04-11|1992-07-02|TAKE BACK RETURN|MAIL|ar packages are | +4230|75|6|4|28|27301.96|0.01|0.03|R|F|1992-05-12|1992-05-10|1992-06-01|TAKE BACK RETURN|MAIL|nt instruct| +4230|125|10|5|50|51256.00|0.00|0.01|A|F|1992-03-29|1992-05-19|1992-04-20|TAKE BACK RETURN|SHIP|ts. final instructions in| +4230|35|6|6|30|28050.90|0.05|0.07|A|F|1992-03-11|1992-04-29|1992-03-30|NONE|AIR|s. final excuses across the| +4230|152|3|7|18|18938.70|0.10|0.04|R|F|1992-06-23|1992-05-10|1992-07-04|COLLECT COD|SHIP| the final acco| +4231|142|3|1|47|48980.58|0.09|0.03|N|O|1997-11-27|1998-01-26|1997-12-17|NONE|REG AIR|hely along the silent at| +4231|166|3|2|4|4264.64|0.06|0.02|N|O|1997-11-28|1998-01-26|1997-12-12|TAKE BACK RETURN|MAIL|lithely even packages. | +4231|121|2|3|31|31654.72|0.07|0.08|N|O|1998-02-14|1997-12-27|1998-03-01|DELIVER IN PERSON|FOB|ublate. theodoli| +4231|40|1|4|35|32901.40|0.10|0.00|N|O|1998-02-21|1998-01-24|1998-03-18|DELIVER IN PERSON|FOB|le quickly regular, unus| +4256|151|9|1|22|23125.30|0.05|0.05|R|F|1992-07-30|1992-05-14|1992-08-14|NONE|TRUCK|, final platelets are slyly final pint| +4257|65|10|1|3|2895.18|0.10|0.03|N|O|1995-06-18|1995-05-01|1995-07-12|DELIVER IN PERSON|MAIL|thin the theodolites use after the bl| +4257|35|6|2|5|4675.15|0.01|0.04|R|F|1995-04-29|1995-06-05|1995-05-13|TAKE BACK RETURN|TRUCK|n deposits. furiously e| +4257|128|9|3|33|33927.96|0.03|0.04|A|F|1995-05-23|1995-05-03|1995-05-31|COLLECT COD|AIR|uffily regular accounts ar| +4258|166|7|1|36|38381.76|0.02|0.06|N|O|1997-02-23|1997-01-25|1997-02-27|TAKE BACK RETURN|SHIP|ns use alongs| +4258|162|1|2|19|20181.04|0.03|0.02|N|O|1997-01-14|1996-12-12|1997-01-20|TAKE BACK RETURN|AIR|ly busily ironic foxes. f| +4258|31|7|3|46|42827.38|0.04|0.07|N|O|1997-01-02|1996-12-26|1997-01-12|DELIVER IN PERSON|AIR| furiously pend| +4258|35|6|4|22|20570.66|0.04|0.04|N|O|1996-12-12|1996-12-06|1996-12-20|TAKE BACK RETURN|AIR|e regular, even asym| +4258|163|10|5|9|9568.44|0.04|0.03|N|O|1996-12-04|1996-12-08|1996-12-20|DELIVER IN PERSON|TRUCK|counts wake permanently after the bravely| +4259|43|6|1|14|13202.56|0.05|0.03|N|O|1998-01-09|1997-11-21|1998-01-29|TAKE BACK RETURN|RAIL| furiously pending excuses. ideas hagg| +4260|24|7|1|21|19404.42|0.08|0.04|R|F|1992-08-06|1992-06-18|1992-08-22|NONE|AIR|al, pending accounts must| +4261|110|1|1|12|12121.32|0.05|0.01|A|F|1992-11-01|1993-01-01|1992-11-12|NONE|FOB|into beans | +4261|82|3|2|4|3928.32|0.02|0.07|R|F|1992-12-11|1992-12-18|1992-12-24|DELIVER IN PERSON|FOB|ackages unwind furiously fluff| +4261|175|5|3|3|3225.51|0.07|0.02|R|F|1992-11-10|1992-12-14|1992-11-17|COLLECT COD|RAIL|ly even deposits eat blithely alo| +4261|174|3|4|36|38670.12|0.04|0.06|R|F|1992-12-02|1992-12-18|1992-12-25|NONE|REG AIR| slyly pendi| +4261|24|7|5|28|25872.56|0.07|0.06|A|F|1992-10-08|1992-12-23|1992-10-11|TAKE BACK RETURN|MAIL|packages. fluffily i| +4262|76|7|1|30|29282.10|0.01|0.03|N|O|1996-08-11|1996-10-11|1996-09-09|TAKE BACK RETURN|RAIL|tes after the carefully| +4262|96|7|2|5|4980.45|0.02|0.05|N|O|1996-09-27|1996-09-05|1996-10-25|COLLECT COD|SHIP|blithely final asymptotes integrate| +4262|162|1|3|5|5310.80|0.08|0.00|N|O|1996-10-02|1996-10-16|1996-10-05|NONE|REG AIR|ironic accounts are unusu| +4262|74|2|4|45|43833.15|0.02|0.01|N|O|1996-11-09|1996-09-09|1996-11-12|DELIVER IN PERSON|SHIP|ackages boost. pending, even instruction| +4262|100|3|5|28|28002.80|0.06|0.02|N|O|1996-10-22|1996-09-06|1996-11-13|DELIVER IN PERSON|FOB|ironic, regular depend| +4262|17|7|6|26|23842.26|0.03|0.02|N|O|1996-08-29|1996-09-25|1996-08-31|NONE|RAIL|s boost slyly along the bold, iro| +4262|160|5|7|41|43466.56|0.03|0.01|N|O|1996-08-28|1996-09-14|1996-09-20|COLLECT COD|RAIL|cuses unwind ac| +4263|18|9|1|9|8262.09|0.08|0.07|N|O|1998-04-04|1998-04-29|1998-05-04|COLLECT COD|AIR|structions cajole quic| +4263|196|10|2|28|30693.32|0.05|0.03|N|O|1998-06-24|1998-06-08|1998-07-14|NONE|MAIL|ideas for the carefully re| +4263|11|1|3|38|34618.38|0.01|0.01|N|O|1998-07-10|1998-05-08|1998-07-17|NONE|TRUCK|rding to the dep| +4263|19|3|4|20|18380.20|0.02|0.07|N|O|1998-04-09|1998-04-30|1998-05-04|NONE|RAIL|uietly regular deposits. sly deposits w| +4263|198|2|5|14|15374.66|0.09|0.06|N|O|1998-05-06|1998-04-17|1998-05-11|DELIVER IN PERSON|TRUCK|d accounts. daringly regular accounts hagg| +4263|113|10|6|47|47616.17|0.08|0.06|N|O|1998-06-28|1998-05-09|1998-07-02|DELIVER IN PERSON|TRUCK|y. theodolites wake idly ironic do| +4263|29|4|7|6|5574.12|0.04|0.04|N|O|1998-05-01|1998-06-02|1998-05-14|TAKE BACK RETURN|REG AIR|g the final, regular instructions: | +4288|74|5|1|32|31170.24|0.10|0.07|R|F|1993-03-19|1993-01-26|1993-04-18|TAKE BACK RETURN|AIR|e blithely even instructions. speci| +4288|105|6|2|39|39198.90|0.05|0.02|R|F|1993-03-25|1993-02-06|1993-03-28|DELIVER IN PERSON|AIR|uffy theodolites run| +4288|125|8|3|7|7175.84|0.03|0.01|A|F|1993-01-15|1993-02-05|1993-01-26|NONE|TRUCK|ngside of the special platelet| +4289|196|7|1|19|20827.61|0.06|0.06|R|F|1993-12-31|1993-11-06|1994-01-23|DELIVER IN PERSON|TRUCK|e carefully regular ideas. sl| +4290|137|3|1|23|23853.99|0.06|0.04|R|F|1995-04-04|1995-02-16|1995-04-07|TAKE BACK RETURN|REG AIR|uests cajole carefully.| +4290|99|2|2|3|2997.27|0.09|0.03|A|F|1995-03-25|1995-03-07|1995-04-11|NONE|RAIL|lar platelets cajole| +4291|192|6|1|3|3276.57|0.08|0.08|A|F|1994-03-17|1994-02-21|1994-03-27|COLLECT COD|SHIP|tes sleep slyly above the quickly sl| +4291|125|8|2|43|44080.16|0.01|0.06|A|F|1994-02-01|1994-02-27|1994-02-06|DELIVER IN PERSON|REG AIR|s. quietly regular | +4291|8|1|3|25|22700.00|0.09|0.08|R|F|1994-02-14|1994-02-08|1994-03-15|COLLECT COD|AIR|uctions. furiously regular ins| +4292|44|3|1|22|20768.88|0.08|0.03|R|F|1992-02-14|1992-02-16|1992-03-01|NONE|FOB|refully expres| +4292|40|6|2|1|940.04|0.03|0.01|A|F|1992-02-07|1992-03-16|1992-02-10|DELIVER IN PERSON|FOB| the furiously ev| +4292|120|10|3|35|35704.20|0.03|0.06|A|F|1992-03-23|1992-04-04|1992-04-02|COLLECT COD|TRUCK|dugouts use. furiously bold packag| +4292|163|10|4|40|42526.40|0.05|0.04|A|F|1992-04-27|1992-03-07|1992-05-04|COLLECT COD|REG AIR|ounts according to the furiously | +4292|131|7|5|6|6186.78|0.07|0.08|R|F|1992-03-03|1992-02-24|1992-03-25|COLLECT COD|FOB|bove the silently regula| +4292|4|1|6|47|42488.00|0.05|0.00|R|F|1992-05-02|1992-03-21|1992-05-27|TAKE BACK RETURN|FOB|y packages; even ideas boost| +4293|1|6|1|34|30634.00|0.03|0.08|N|O|1996-11-05|1996-10-12|1996-12-04|NONE|FOB|ions sleep blithely on| +4293|77|5|2|50|48853.50|0.01|0.05|N|O|1996-11-27|1996-10-30|1996-12-22|COLLECT COD|MAIL| special deposits. furiousl| +4293|199|1|3|47|51661.93|0.08|0.02|N|O|1996-09-07|1996-10-24|1996-09-15|NONE|RAIL|ithely pending deposits af| +4293|88|9|4|25|24702.00|0.04|0.04|N|O|1996-09-11|1996-11-14|1996-09-22|DELIVER IN PERSON|FOB|inal asympt| +4293|181|2|5|1|1081.18|0.06|0.05|N|O|1996-11-15|1996-10-09|1996-11-26|COLLECT COD|AIR|eposits should boost along the | +4293|79|7|6|45|44058.15|0.10|0.04|N|O|1996-11-04|1996-11-06|1996-11-23|NONE|MAIL|lar ideas use carefully| +4294|105|8|1|19|19096.90|0.03|0.04|A|F|1992-10-16|1992-11-13|1992-10-26|DELIVER IN PERSON|AIR|nt dependencies. furiously regular ideas d| +4294|27|2|2|16|14832.32|0.01|0.02|R|F|1992-08-17|1992-09-24|1992-09-04|TAKE BACK RETURN|REG AIR|lithely pint| +4294|198|1|3|30|32945.70|0.01|0.00|A|F|1992-09-12|1992-11-06|1992-09-25|NONE|MAIL|olites. bold foxes affix ironic theodolite| +4294|105|2|4|34|34173.40|0.02|0.01|R|F|1992-09-09|1992-11-06|1992-10-04|TAKE BACK RETURN|REG AIR|pendencies!| +4294|119|3|5|37|37707.07|0.05|0.01|R|F|1992-09-07|1992-10-13|1992-09-08|NONE|REG AIR|cial packages nag f| +4294|87|8|6|42|41457.36|0.02|0.03|A|F|1992-09-30|1992-11-13|1992-10-15|DELIVER IN PERSON|FOB| carefully; furiously ex| +4294|175|3|7|47|50532.99|0.02|0.08|R|F|1992-11-09|1992-11-03|1992-12-05|TAKE BACK RETURN|SHIP|es. blithely r| +4295|29|2|1|49|45521.98|0.09|0.01|N|O|1996-05-25|1996-03-17|1996-06-19|TAKE BACK RETURN|REG AIR|refully silent requests. f| +4295|71|9|2|4|3884.28|0.09|0.07|N|O|1996-06-05|1996-04-26|1996-06-13|DELIVER IN PERSON|TRUCK|arefully according to the pending ac| +4295|193|4|3|3|3279.57|0.04|0.00|N|O|1996-06-04|1996-04-24|1996-06-24|DELIVER IN PERSON|AIR|telets cajole bravely| +4295|80|9|4|30|29402.40|0.07|0.06|N|O|1996-03-22|1996-04-23|1996-04-20|NONE|SHIP|yly ironic frets. pending foxes after | +4320|46|5|1|28|26489.12|0.02|0.06|N|O|1997-01-28|1997-02-07|1997-02-07|COLLECT COD|FOB|nts. even, ironic excuses hagg| +4320|140|6|2|6|6240.84|0.08|0.08|N|O|1997-01-11|1997-01-26|1997-01-22|DELIVER IN PERSON|SHIP|against the carefully careful asym| +4320|188|9|3|33|35909.94|0.09|0.02|N|O|1996-12-11|1997-02-27|1997-01-08|TAKE BACK RETURN|SHIP|ess asymptotes so| +4321|147|6|1|33|34555.62|0.09|0.02|A|F|1994-09-01|1994-08-17|1994-09-05|DELIVER IN PERSON|TRUCK|yly special excuses. fluffily | +4321|54|2|2|45|42932.25|0.00|0.08|R|F|1994-11-13|1994-09-15|1994-11-18|DELIVER IN PERSON|SHIP| haggle ironically bold theodolites. quick| +4321|186|7|3|23|24982.14|0.01|0.05|A|F|1994-11-03|1994-10-08|1994-11-06|DELIVER IN PERSON|SHIP|ly even orbits slee| +4321|91|2|4|4|3964.36|0.02|0.00|R|F|1994-09-10|1994-10-06|1994-09-11|NONE|FOB|ironic deposi| +4321|172|2|5|10|10721.70|0.04|0.03|A|F|1994-09-07|1994-08-23|1994-09-17|TAKE BACK RETURN|SHIP|wake carefully alongside of | +4322|69|4|1|39|37793.34|0.04|0.02|N|O|1998-04-27|1998-06-03|1998-05-04|TAKE BACK RETURN|MAIL|its integrate fluffily | +4322|140|1|2|9|9361.26|0.05|0.08|N|O|1998-05-18|1998-04-27|1998-05-28|COLLECT COD|AIR|ual instructio| +4322|8|9|3|12|10896.00|0.09|0.05|N|O|1998-03-29|1998-06-05|1998-04-16|DELIVER IN PERSON|TRUCK|e blithely against the slyly unusu| +4322|46|7|4|17|16082.68|0.09|0.08|N|O|1998-05-31|1998-05-31|1998-06-10|TAKE BACK RETURN|FOB|ructions boost | +4322|102|7|5|10|10021.00|0.00|0.05|N|O|1998-05-31|1998-04-27|1998-06-25|TAKE BACK RETURN|REG AIR| regular ideas engage carefully quick| +4322|60|8|6|39|37442.34|0.09|0.08|N|O|1998-03-16|1998-05-21|1998-04-11|COLLECT COD|AIR|ccounts. dogged pin| +4322|14|4|7|34|31076.34|0.05|0.00|N|O|1998-05-27|1998-04-12|1998-06-16|NONE|REG AIR|ounts haggle fluffily ideas. pend| +4323|1|2|1|33|29733.00|0.09|0.02|A|F|1994-05-04|1994-03-06|1994-05-23|COLLECT COD|TRUCK|the slyly bold deposits slee| +4324|51|2|1|44|41846.20|0.05|0.04|N|O|1995-10-15|1995-09-07|1995-11-07|DELIVER IN PERSON|AIR|ainst the u| +4324|48|7|2|12|11376.48|0.04|0.02|N|O|1995-10-05|1995-09-07|1995-10-18|NONE|REG AIR|c packages. furiously express sauternes| +4324|82|3|3|14|13749.12|0.07|0.06|N|O|1995-11-12|1995-08-26|1995-11-21|COLLECT COD|AIR| packages nag express excuses. qui| +4324|50|7|4|14|13300.70|0.02|0.04|N|O|1995-09-20|1995-10-08|1995-10-06|COLLECT COD|RAIL| express ideas. blithely blit| +4324|84|5|5|22|21649.76|0.07|0.03|N|O|1995-09-13|1995-10-04|1995-09-23|DELIVER IN PERSON|SHIP|ke express, special ideas.| +4324|43|2|6|31|29234.24|0.08|0.04|N|O|1995-10-23|1995-09-14|1995-11-09|COLLECT COD|RAIL|efully flu| +4324|154|6|7|46|48490.90|0.00|0.03|N|O|1995-11-03|1995-09-28|1995-11-22|NONE|SHIP|ular, final theodo| +4325|160|2|1|18|19082.88|0.01|0.07|N|O|1996-10-07|1996-09-28|1996-10-31|DELIVER IN PERSON|RAIL|. blithely| +4326|163|4|1|11|11694.76|0.01|0.01|N|O|1997-02-02|1996-12-10|1997-02-20|DELIVER IN PERSON|TRUCK|press reque| +4326|167|6|2|27|28813.32|0.06|0.01|N|O|1996-11-29|1997-01-20|1996-12-23|COLLECT COD|AIR|inal packages. final asymptotes about t| +4327|95|8|1|18|17911.62|0.08|0.00|N|F|1995-06-16|1995-04-20|1995-07-12|COLLECT COD|RAIL|y final excuses. ironic, special requests a| +4327|106|9|2|40|40244.00|0.07|0.01|N|F|1995-05-26|1995-04-17|1995-06-18|NONE|AIR|quests. packages are after th| +4327|145|2|3|11|11496.54|0.10|0.07|R|F|1995-04-24|1995-05-27|1995-05-24|TAKE BACK RETURN|FOB| ironic dolphins| +4327|21|10|4|8|7368.16|0.04|0.08|N|F|1995-05-26|1995-05-28|1995-06-19|DELIVER IN PERSON|AIR|eodolites cajole; unusual Tiresias| +4327|190|1|5|39|42517.41|0.01|0.00|N|O|1995-06-23|1995-04-18|1995-07-13|TAKE BACK RETURN|FOB|kages against the blit| +4327|152|4|6|10|10521.50|0.00|0.06|A|F|1995-04-28|1995-06-11|1995-05-07|TAKE BACK RETURN|TRUCK|arefully sile| +4352|106|9|1|18|18109.80|0.00|0.03|N|O|1998-02-27|1998-02-02|1998-03-01|DELIVER IN PERSON|RAIL|ding to th| +4353|94|8|1|22|21869.98|0.05|0.05|N|O|1998-01-19|1998-01-23|1998-02-10|COLLECT COD|FOB|ent packages. accounts are slyly. | +4354|15|9|1|30|27450.30|0.08|0.07|R|F|1995-01-27|1994-11-24|1995-02-25|TAKE BACK RETURN|REG AIR|around the ir| +4354|153|8|2|23|24222.45|0.01|0.08|R|F|1994-11-20|1994-12-23|1994-11-27|TAKE BACK RETURN|AIR|kly along the ironic, ent| +4354|51|6|3|2|1902.10|0.10|0.04|A|F|1995-01-09|1994-12-15|1995-01-24|TAKE BACK RETURN|REG AIR|s nag quickly | +4354|86|7|4|36|35498.88|0.05|0.05|A|F|1994-11-20|1994-12-06|1994-12-06|DELIVER IN PERSON|AIR| wake slyly eve| +4354|65|10|5|37|35707.22|0.06|0.02|R|F|1995-01-13|1994-12-29|1995-01-31|DELIVER IN PERSON|FOB|deas use blithely! special foxes print af| +4354|108|3|6|36|36291.60|0.03|0.04|R|F|1994-12-03|1994-12-05|1995-01-02|TAKE BACK RETURN|TRUCK|efully special packages use fluffily| +4354|139|5|7|18|18704.34|0.03|0.04|A|F|1994-12-07|1994-12-11|1994-12-11|TAKE BACK RETURN|SHIP|ross the furiously | +4355|195|7|1|32|35046.08|0.10|0.02|N|O|1996-12-29|1997-02-08|1997-01-24|DELIVER IN PERSON|REG AIR|y silent deposits. b| +4355|17|1|2|4|3668.04|0.05|0.02|N|O|1997-02-25|1997-01-29|1997-03-17|TAKE BACK RETURN|TRUCK|slyly blithely regular packag| +4355|1|2|3|13|11713.00|0.07|0.05|N|O|1997-01-21|1996-12-22|1997-02-14|COLLECT COD|TRUCK| ought to mold. blithely pending ideas | +4355|194|6|4|14|15318.66|0.04|0.02|N|O|1997-03-08|1997-01-22|1997-03-26|NONE|RAIL|he furiously ironic accounts. quickly iro| +4355|31|7|5|50|46551.50|0.10|0.00|N|O|1996-11-25|1997-01-01|1996-12-06|DELIVER IN PERSON|REG AIR| regular accounts boost along the | +4355|122|7|6|35|35774.20|0.00|0.08|N|O|1997-01-28|1997-01-28|1997-02-20|NONE|FOB|ess accounts affix ironic| +4355|101|4|7|47|47051.70|0.09|0.02|N|O|1996-12-28|1996-12-29|1997-01-09|NONE|RAIL|e. realms integrate | +4356|194|5|1|35|38296.65|0.00|0.04|R|F|1994-05-30|1994-06-14|1994-06-08|COLLECT COD|MAIL|arefully ironic | +4357|84|5|1|50|49204.00|0.04|0.07|N|O|1997-11-25|1997-12-03|1997-12-17|DELIVER IN PERSON|RAIL|s. final, e| +4357|108|9|2|17|17137.70|0.10|0.07|N|O|1998-02-01|1997-12-08|1998-02-09|DELIVER IN PERSON|MAIL|e carefully furiou| +4358|126|5|1|47|48227.64|0.04|0.00|N|O|1997-10-15|1997-10-14|1997-11-04|DELIVER IN PERSON|SHIP|refully busy dep| +4359|174|3|1|41|44040.97|0.03|0.07|A|F|1993-04-06|1993-05-06|1993-04-14|COLLECT COD|RAIL|s affix sly| +4359|153|8|2|8|8425.20|0.03|0.08|R|F|1993-06-27|1993-05-16|1993-07-04|DELIVER IN PERSON|MAIL|packages affix. fluffily regular f| +4359|193|6|3|32|34982.08|0.10|0.03|R|F|1993-06-18|1993-04-04|1993-07-18|COLLECT COD|MAIL|olites nag quietly caref| +4359|78|8|4|1|978.07|0.05|0.03|R|F|1993-04-27|1993-05-09|1993-05-08|NONE|MAIL| fluffily ironic, bold pac| +4359|33|4|5|22|20526.66|0.04|0.01|A|F|1993-03-28|1993-06-01|1993-04-13|NONE|REG AIR|accounts wake ironic deposits. ironic| +4384|136|7|1|5|5180.65|0.09|0.01|A|F|1992-08-22|1992-08-24|1992-09-20|DELIVER IN PERSON|MAIL|instructions sleep. blithely express pa| +4384|89|10|2|38|37585.04|0.07|0.06|A|F|1992-10-18|1992-09-24|1992-11-04|NONE|FOB|ly final requests. regu| +4384|89|10|3|11|10879.88|0.05|0.04|R|F|1992-08-31|1992-10-04|1992-09-28|TAKE BACK RETURN|FOB|deposits promise carefully even, regular e| +4385|111|8|1|38|38422.18|0.00|0.02|N|O|1996-11-22|1996-10-30|1996-12-21|DELIVER IN PERSON|TRUCK|inal frays. final, bold exc| +4386|130|3|1|10|10301.30|0.05|0.07|N|O|1998-06-03|1998-04-16|1998-06-28|TAKE BACK RETURN|MAIL|gainst the quickly expre| +4386|118|2|2|28|28507.08|0.03|0.06|N|O|1998-03-19|1998-05-01|1998-03-27|NONE|FOB|. quick packages play slyly | +4386|140|1|3|4|4160.56|0.07|0.05|N|O|1998-04-07|1998-03-25|1998-04-19|COLLECT COD|FOB|ns wake carefully carefully iron| +4386|121|2|4|21|21443.52|0.09|0.00|N|O|1998-05-05|1998-03-19|1998-05-13|NONE|RAIL|e pending, sp| +4386|130|3|5|39|40175.07|0.09|0.06|N|O|1998-03-05|1998-03-15|1998-03-16|NONE|RAIL|structions cajole quickly express| +4386|90|1|6|18|17821.62|0.02|0.05|N|O|1998-04-12|1998-04-09|1998-05-12|TAKE BACK RETURN|SHIP| deposits use according to the pending, | +4386|20|4|7|16|14720.32|0.07|0.02|N|O|1998-05-05|1998-03-17|1998-06-03|COLLECT COD|AIR|e furiously final pint| +4387|122|5|1|3|3066.36|0.02|0.01|N|O|1996-01-17|1996-01-14|1996-01-28|COLLECT COD|AIR| boost slyly ironic instructions. furiou| +4387|177|5|2|48|51704.16|0.06|0.05|N|O|1995-10-29|1995-12-11|1995-11-01|NONE|REG AIR|sleep slyly. blithely sl| +4387|2|5|3|15|13530.00|0.00|0.03|N|O|1996-01-11|1996-01-14|1996-01-30|TAKE BACK RETURN|REG AIR|s hinder quietly across the pla| +4387|47|8|4|9|8523.36|0.00|0.03|N|O|1996-01-04|1995-12-26|1996-01-12|DELIVER IN PERSON|REG AIR|c ideas. slyly regular packages sol| +4387|82|3|5|3|2946.24|0.05|0.08|N|O|1995-11-17|1995-12-28|1995-11-25|COLLECT COD|SHIP| pinto beans | +4387|6|3|6|40|36240.00|0.02|0.04|N|O|1995-11-29|1995-12-10|1995-12-20|NONE|REG AIR|deas according to the blithely regular fox| +4388|65|10|1|30|28951.80|0.02|0.07|N|O|1996-06-07|1996-05-07|1996-06-22|DELIVER IN PERSON|FOB|s cajole fluffil| +4388|84|5|2|28|27554.24|0.05|0.04|N|O|1996-05-08|1996-06-20|1996-05-12|TAKE BACK RETURN|RAIL|ove the ide| +4388|52|4|3|13|12376.65|0.07|0.05|N|O|1996-06-28|1996-05-23|1996-07-04|DELIVER IN PERSON|REG AIR|ly even, expre| +4389|157|2|1|20|21143.00|0.08|0.00|A|F|1994-06-06|1994-06-17|1994-06-17|DELIVER IN PERSON|SHIP|ng the carefully express d| +4389|153|5|2|13|13690.95|0.00|0.00|A|F|1994-08-18|1994-06-06|1994-08-20|NONE|RAIL|nal, regula| +4389|79|9|3|39|38183.73|0.04|0.07|A|F|1994-06-08|1994-06-04|1994-06-10|TAKE BACK RETURN|TRUCK| unusual, final excuses cajole carefully | +4389|160|2|4|5|5300.80|0.09|0.00|A|F|1994-09-03|1994-06-23|1994-09-16|NONE|FOB| ironic request| +4389|11|5|5|22|20042.22|0.08|0.00|R|F|1994-07-05|1994-06-12|1994-07-12|NONE|TRUCK|lly silent de| +4389|2|3|6|22|19844.00|0.01|0.04|R|F|1994-06-07|1994-06-29|1994-06-19|COLLECT COD|TRUCK|at the final excuses hinder carefully a| +4389|185|6|7|4|4340.72|0.09|0.08|R|F|1994-06-14|1994-06-30|1994-07-06|NONE|REG AIR| blithely even d| +4390|152|10|1|35|36825.25|0.07|0.04|R|F|1995-05-30|1995-07-02|1995-06-15|DELIVER IN PERSON|TRUCK|ongside of the slyly regular ideas| +4390|196|8|2|28|30693.32|0.03|0.00|N|O|1995-09-07|1995-06-22|1995-10-05|COLLECT COD|SHIP|ld braids haggle atop the for| +4390|101|8|3|42|42046.20|0.05|0.08|A|F|1995-06-12|1995-07-16|1995-06-17|NONE|AIR|arefully even accoun| +4390|98|2|4|32|31938.88|0.07|0.08|N|O|1995-09-15|1995-08-12|1995-10-05|TAKE BACK RETURN|TRUCK|ctions across| +4391|161|10|1|1|1061.16|0.09|0.00|R|F|1992-06-18|1992-04-27|1992-06-20|COLLECT COD|TRUCK|ong the silent deposits| +4391|187|8|2|45|48923.10|0.07|0.04|R|F|1992-04-01|1992-05-01|1992-04-13|TAKE BACK RETURN|TRUCK|ep quickly after | +4416|94|7|1|37|36781.33|0.08|0.03|A|F|1992-10-23|1992-08-23|1992-11-16|COLLECT COD|RAIL|fluffily ironic | +4416|89|10|2|3|2967.24|0.06|0.03|R|F|1992-10-22|1992-08-06|1992-11-13|DELIVER IN PERSON|SHIP| requests sleep along the | +4416|9|6|3|45|40905.00|0.09|0.03|A|F|1992-10-16|1992-09-09|1992-10-28|COLLECT COD|AIR|the final pinto beans. special frets | +4417|75|5|1|28|27301.96|0.08|0.02|N|O|1998-09-04|1998-10-04|1998-09-19|TAKE BACK RETURN|REG AIR|ies across the furious| +4417|181|2|2|1|1081.18|0.06|0.08|N|O|1998-10-23|1998-08-22|1998-10-24|NONE|REG AIR|press deposits promise stealthily amo| +4417|98|2|3|35|34933.15|0.06|0.04|N|O|1998-08-08|1998-09-23|1998-09-02|DELIVER IN PERSON|FOB|slyly regular, silent courts. even packag| +4418|35|1|1|32|29920.96|0.02|0.06|A|F|1993-05-28|1993-06-02|1993-05-30|TAKE BACK RETURN|RAIL|ly. bold pinto b| +4418|22|5|2|14|12908.28|0.03|0.04|A|F|1993-05-20|1993-06-18|1993-06-05|TAKE BACK RETURN|SHIP| blithely regular requests. blith| +4418|79|7|3|3|2937.21|0.00|0.02|R|F|1993-04-08|1993-06-04|1993-05-02|NONE|SHIP|luffily across the unusual ideas. reque| +4419|108|9|1|45|45364.50|0.01|0.05|N|O|1996-07-20|1996-09-07|1996-08-18|DELIVER IN PERSON|TRUCK|s doze sometimes fluffily regular a| +4419|32|8|2|42|39145.26|0.00|0.03|N|O|1996-09-18|1996-07-25|1996-09-21|COLLECT COD|RAIL|sts. furious| +4419|132|3|3|6|6192.78|0.02|0.08|N|O|1996-06-25|1996-09-04|1996-07-20|DELIVER IN PERSON|AIR|ts wake slyly final dugou| +4420|8|5|1|7|6356.00|0.07|0.03|R|F|1994-08-30|1994-09-03|1994-09-25|NONE|FOB| regular instructions sleep around| +4421|98|2|1|37|36929.33|0.09|0.08|N|O|1997-07-22|1997-06-27|1997-07-25|DELIVER IN PERSON|SHIP|l accounts. ironic request| +4421|56|1|2|46|43978.30|0.04|0.04|N|O|1997-04-21|1997-05-13|1997-05-15|DELIVER IN PERSON|FOB|reful packages. bold, | +4421|167|6|3|46|49089.36|0.00|0.06|N|O|1997-05-25|1997-05-21|1997-06-23|COLLECT COD|TRUCK|g dependenci| +4421|191|4|4|32|34918.08|0.06|0.04|N|O|1997-07-09|1997-06-03|1997-07-25|NONE|SHIP|ar ideas eat among the furiousl| +4421|190|1|5|32|34886.08|0.06|0.04|N|O|1997-07-28|1997-06-14|1997-08-13|NONE|REG AIR|uickly final pinto beans impress. bold | +4421|47|6|6|44|41669.76|0.09|0.06|N|O|1997-06-17|1997-06-20|1997-06-29|NONE|TRUCK|le carefully. bl| +4421|116|3|7|18|18289.98|0.01|0.00|N|O|1997-06-07|1997-05-13|1997-06-10|DELIVER IN PERSON|FOB|. regular, s| +4422|135|1|1|5|5175.65|0.09|0.07|N|O|1995-07-17|1995-08-13|1995-07-25|NONE|SHIP|e furiously about t| +4422|48|5|2|41|38869.64|0.08|0.05|N|F|1995-06-12|1995-07-09|1995-06-20|COLLECT COD|TRUCK| theodolites shal| +4422|103|10|3|39|39120.90|0.00|0.05|N|O|1995-09-02|1995-06-24|1995-09-14|NONE|TRUCK|en hockey players engage| +4422|153|4|4|4|4212.60|0.02|0.05|N|O|1995-09-18|1995-08-12|1995-10-18|COLLECT COD|FOB|cies along the bo| +4422|80|9|5|20|19601.60|0.07|0.05|N|O|1995-08-17|1995-07-16|1995-09-13|DELIVER IN PERSON|RAIL|ructions wake slyly al| +4423|150|9|1|3|3150.45|0.03|0.00|A|F|1995-03-22|1995-04-06|1995-04-19|NONE|TRUCK| final theodolites nag after the bli| +4423|60|5|2|2|1920.12|0.07|0.04|A|F|1995-03-04|1995-04-04|1995-03-08|TAKE BACK RETURN|REG AIR|old sheaves sleep| +4448|52|7|1|24|22849.20|0.10|0.07|N|O|1998-09-09|1998-07-06|1998-09-27|DELIVER IN PERSON|SHIP|nal packages along the ironic instructi| +4448|189|10|2|13|14159.34|0.00|0.01|N|O|1998-07-26|1998-07-03|1998-08-14|COLLECT COD|MAIL|fluffily express accounts integrate furiou| +4448|41|4|3|35|32936.40|0.10|0.06|N|O|1998-09-18|1998-07-27|1998-10-08|NONE|REG AIR|aggle carefully alongside of the q| +4448|141|2|4|3|3123.42|0.01|0.01|N|O|1998-07-20|1998-07-10|1998-08-07|DELIVER IN PERSON|TRUCK|ronic theod| +4448|91|2|5|41|40634.69|0.00|0.08|N|O|1998-07-30|1998-08-09|1998-08-03|NONE|AIR|pon the permanently even excuses nag | +4448|172|3|6|12|12866.04|0.06|0.03|N|O|1998-08-21|1998-06-30|1998-09-09|COLLECT COD|RAIL|sits about the ironic, bu| +4449|32|3|1|42|39145.26|0.10|0.07|N|O|1998-03-22|1998-05-09|1998-04-03|NONE|FOB| packages. blithely final | +4449|141|8|2|10|10411.40|0.02|0.03|N|O|1998-05-09|1998-05-04|1998-05-15|NONE|SHIP|ccounts alongside of the platelets integr| +4450|174|5|1|44|47263.48|0.10|0.00|N|O|1997-10-12|1997-10-13|1997-10-29|DELIVER IN PERSON|RAIL| the slyly eve| +4450|15|6|2|9|8235.09|0.03|0.03|N|O|1997-08-13|1997-08-16|1997-08-15|NONE|FOB|gular requests cajole carefully. regular c| +4450|96|8|3|45|44824.05|0.08|0.01|N|O|1997-09-01|1997-10-06|1997-09-19|NONE|TRUCK|express ideas are furiously regular| +4450|62|9|4|13|12506.78|0.00|0.00|N|O|1997-08-26|1997-09-18|1997-09-20|COLLECT COD|MAIL| brave foxes. slyly unusual| +4450|56|7|5|6|5736.30|0.09|0.01|N|O|1997-09-02|1997-09-30|1997-09-09|NONE|FOB|eposits. foxes cajole unusual fox| +4451|164|5|1|40|42566.40|0.03|0.03|A|F|1994-11-18|1994-12-25|1994-11-26|DELIVER IN PERSON|RAIL|y. slyly special deposits are sly| +4451|63|4|2|34|32744.04|0.10|0.02|A|F|1994-11-30|1994-12-04|1994-12-13|COLLECT COD|SHIP| regular ideas.| +4451|159|10|3|19|20123.85|0.05|0.06|R|F|1994-10-09|1994-11-26|1994-10-23|COLLECT COD|FOB|ly after the fluffi| +4452|114|8|1|21|21296.31|0.07|0.03|R|F|1994-10-06|1994-08-23|1994-10-15|COLLECT COD|TRUCK|multipliers x-ray carefully in place of | +4452|1|8|2|47|42347.00|0.01|0.06|A|F|1994-10-08|1994-08-09|1994-10-09|TAKE BACK RETURN|TRUCK|ts. slyly regular cour| +4453|147|10|1|41|42932.74|0.00|0.08|N|O|1997-07-17|1997-05-15|1997-07-31|NONE|REG AIR|anent theodolites are slyly except t| +4453|133|4|2|16|16530.08|0.03|0.00|N|O|1997-07-22|1997-05-05|1997-08-03|COLLECT COD|FOB|ar excuses nag quickly even accounts. b| +4453|62|7|3|48|46178.88|0.02|0.07|N|O|1997-05-29|1997-06-24|1997-06-03|NONE|SHIP|eep. fluffily express accounts at the furi| +4453|102|5|4|26|26054.60|0.06|0.07|N|O|1997-05-07|1997-06-07|1997-05-22|NONE|TRUCK|express packages are| +4454|151|9|1|20|21023.00|0.10|0.08|R|F|1994-05-06|1994-03-17|1994-05-20|COLLECT COD|SHIP|lar theodolites. even instructio| +4454|152|10|2|22|23147.30|0.06|0.02|A|F|1994-02-06|1994-04-11|1994-03-06|DELIVER IN PERSON|RAIL|ully. carefully final accounts accordi| +4454|192|3|3|45|49148.55|0.07|0.04|A|F|1994-03-29|1994-03-26|1994-04-04|TAKE BACK RETURN|RAIL|ests promise. packages print fur| +4454|2|3|4|1|902.00|0.09|0.05|A|F|1994-02-05|1994-04-19|1994-02-12|COLLECT COD|RAIL|equests run.| +4454|52|4|5|48|45698.40|0.00|0.07|R|F|1994-04-23|1994-04-03|1994-04-26|COLLECT COD|FOB|to beans wake across th| +4454|160|8|6|20|21203.20|0.10|0.03|A|F|1994-04-08|1994-03-06|1994-04-26|DELIVER IN PERSON|TRUCK|quickly regular requests. furiously| +4455|70|5|1|20|19401.40|0.01|0.05|A|F|1994-01-31|1993-11-21|1994-03-02|DELIVER IN PERSON|MAIL| express packages. packages boost quickly| +4455|153|4|2|47|49498.05|0.09|0.01|R|F|1994-01-01|1993-12-25|1994-01-05|COLLECT COD|FOB| requests. even, even accou| +4455|123|2|3|34|34786.08|0.00|0.06|A|F|1993-10-24|1993-11-27|1993-11-04|TAKE BACK RETURN|AIR| slyly ironic requests. quickly even d| +4480|108|5|1|30|30243.00|0.08|0.03|R|F|1994-07-29|1994-06-22|1994-08-01|NONE|FOB|ven braids us| +4481|24|9|1|50|46201.00|0.02|0.06|N|O|1996-07-22|1996-05-13|1996-08-14|DELIVER IN PERSON|RAIL|ar packages. regula| +4481|190|1|2|27|29435.13|0.02|0.03|N|O|1996-04-06|1996-05-17|1996-04-12|TAKE BACK RETURN|AIR|ackages haggle even, | +4482|71|2|1|32|31074.24|0.06|0.03|A|F|1995-05-16|1995-07-22|1995-06-07|NONE|RAIL| quickly pendin| +4482|96|9|2|32|31874.88|0.01|0.06|N|O|1995-08-16|1995-06-26|1995-09-10|DELIVER IN PERSON|AIR|eans wake according | +4483|6|7|1|32|28992.00|0.07|0.07|R|F|1992-04-05|1992-05-25|1992-04-08|DELIVER IN PERSON|MAIL|ests haggle. slyl| +4483|62|1|2|50|48103.00|0.01|0.06|A|F|1992-06-19|1992-05-12|1992-07-08|DELIVER IN PERSON|TRUCK|ag blithely even| +4483|9|4|3|50|45450.00|0.00|0.04|R|F|1992-06-10|1992-04-18|1992-06-17|DELIVER IN PERSON|MAIL|ackages. furiously ironi| +4484|95|9|1|4|3980.36|0.06|0.03|N|O|1997-04-09|1997-02-11|1997-04-12|TAKE BACK RETURN|TRUCK|packages de| +4484|137|8|2|39|40448.07|0.05|0.02|N|O|1997-04-01|1997-01-26|1997-04-21|NONE|RAIL|onic accounts wake blithel| +4484|190|1|3|38|41427.22|0.06|0.07|N|O|1997-03-07|1997-01-31|1997-04-01|COLLECT COD|REG AIR|. even requests un| +4484|122|5|4|41|41906.92|0.06|0.03|N|O|1997-01-25|1997-02-15|1997-01-29|TAKE BACK RETURN|REG AIR|ress accounts. ironic deposits unwind fur| +4484|3|4|5|42|37926.00|0.03|0.07|N|O|1997-03-25|1997-02-21|1997-04-05|DELIVER IN PERSON|REG AIR|ding, pending requests wake. fluffily | +4484|36|7|6|29|27144.87|0.09|0.06|N|O|1996-12-27|1997-03-10|1997-01-13|NONE|FOB| wake blithely ironic| +4484|103|8|7|50|50155.00|0.07|0.01|N|O|1997-03-17|1997-03-16|1997-03-21|COLLECT COD|FOB|the ironic, final theodo| +4485|191|5|1|1|1091.19|0.03|0.05|R|F|1994-12-04|1995-02-07|1994-12-09|NONE|AIR|play according to the ironic, ironic| +4485|141|10|2|46|47892.44|0.04|0.06|R|F|1995-03-09|1994-12-14|1995-03-23|DELIVER IN PERSON|AIR|. ironic foxes haggle. regular war| +4485|175|6|3|43|46232.31|0.01|0.05|R|F|1995-01-17|1995-02-11|1995-02-07|DELIVER IN PERSON|TRUCK|al accounts according to the slyly r| +4485|144|5|4|43|44898.02|0.08|0.06|R|F|1995-01-28|1995-01-26|1995-02-07|DELIVER IN PERSON|AIR|. blithely| +4485|6|7|5|47|42582.00|0.08|0.04|R|F|1995-03-11|1995-01-11|1995-03-21|TAKE BACK RETURN|RAIL|luffily pending acc| +4486|135|1|1|46|47615.98|0.08|0.00|N|O|1998-05-02|1998-04-05|1998-05-08|COLLECT COD|MAIL|ackages. specia| +4486|49|2|2|19|18031.76|0.10|0.01|N|O|1998-06-07|1998-05-28|1998-07-02|NONE|MAIL|pending foxes after| +4486|96|7|3|47|46816.23|0.02|0.07|N|O|1998-04-09|1998-05-24|1998-05-07|DELIVER IN PERSON|MAIL|ts around the quiet packages ar| +4486|91|4|4|28|27750.52|0.07|0.02|N|O|1998-04-21|1998-04-19|1998-04-26|TAKE BACK RETURN|AIR|to the furious, regular foxes play abov| +4487|138|4|1|37|38410.81|0.03|0.07|R|F|1993-02-28|1993-04-18|1993-03-17|TAKE BACK RETURN|MAIL|bove the fu| +4487|113|10|2|49|49642.39|0.10|0.00|R|F|1993-06-13|1993-05-08|1993-07-10|COLLECT COD|FOB|sual packages should ha| +4487|190|1|3|1|1090.19|0.02|0.07|A|F|1993-05-11|1993-05-23|1993-05-17|TAKE BACK RETURN|FOB|ithely final asym| +4487|93|4|4|25|24827.25|0.07|0.03|A|F|1993-03-09|1993-04-27|1993-03-30|COLLECT COD|RAIL|g the final instructions. slyly c| +4512|162|1|1|30|31864.80|0.07|0.07|N|O|1996-01-28|1995-12-22|1996-02-22|TAKE BACK RETURN|TRUCK|ly unusual package| +4512|41|4|2|24|22584.96|0.04|0.06|N|O|1995-12-16|1996-01-16|1995-12-25|NONE|SHIP|ly regular pinto beans. carefully bold depo| +4512|145|8|3|21|21947.94|0.00|0.00|N|O|1995-10-31|1995-12-30|1995-11-15|NONE|REG AIR|lly unusual pinto b| +4512|141|2|4|32|33316.48|0.10|0.01|N|O|1995-11-25|1995-12-28|1995-12-06|NONE|FOB|counts are against the quickly regular | +4512|133|4|5|43|44424.59|0.06|0.00|N|O|1995-12-20|1995-11-28|1996-01-14|NONE|AIR|are carefully. theodolites wake| +4513|170|1|1|29|31034.93|0.03|0.01|N|O|1996-05-18|1996-05-23|1996-06-08|NONE|REG AIR|cajole. regular packages boost. s| +4513|70|9|2|39|37832.73|0.01|0.04|N|O|1996-06-25|1996-05-14|1996-07-24|NONE|MAIL|slyly furiously unusual deposits. blit| +4513|138|4|3|34|35296.42|0.00|0.03|N|O|1996-03-27|1996-06-12|1996-04-06|DELIVER IN PERSON|SHIP|sits. quickly even instructions | +4513|192|6|4|13|14198.47|0.08|0.08|N|O|1996-04-12|1996-05-19|1996-04-25|DELIVER IN PERSON|AIR|l, final excuses detect furi| +4514|164|9|1|27|28732.32|0.06|0.06|R|F|1994-07-01|1994-07-13|1994-07-26|COLLECT COD|AIR| even, silent foxes be| +4514|46|3|2|15|14190.60|0.10|0.04|R|F|1994-08-24|1994-07-11|1994-09-14|DELIVER IN PERSON|RAIL|! unusual, special deposits afte| +4514|78|8|3|10|9780.70|0.09|0.05|A|F|1994-06-19|1994-06-25|1994-07-01|COLLECT COD|SHIP|ake furiously. carefully regular requests| +4514|81|2|4|9|8829.72|0.10|0.03|A|F|1994-08-04|1994-07-01|1994-09-01|DELIVER IN PERSON|REG AIR|wly. quick| +4514|149|8|5|12|12589.68|0.02|0.03|R|F|1994-08-20|1994-06-09|1994-09-15|TAKE BACK RETURN|FOB| carefully ironic foxes nag caref| +4514|189|10|6|38|41388.84|0.03|0.05|A|F|1994-07-28|1994-07-06|1994-08-25|NONE|AIR|ending excuses. sl| +4514|177|8|7|27|29083.59|0.04|0.06|A|F|1994-06-24|1994-07-14|1994-06-30|TAKE BACK RETURN|TRUCK|. slyly sile| +4515|39|10|1|15|14085.45|0.06|0.01|R|F|1992-05-26|1992-05-25|1992-06-03|NONE|SHIP|posits wake| +4515|103|10|2|50|50155.00|0.06|0.03|A|F|1992-03-28|1992-05-16|1992-04-20|NONE|AIR|ding instructions again| +4515|154|6|3|27|28462.05|0.09|0.01|A|F|1992-06-06|1992-06-08|1992-06-07|DELIVER IN PERSON|REG AIR| against the even re| +4515|54|5|4|32|30529.60|0.06|0.03|R|F|1992-04-07|1992-05-11|1992-04-09|COLLECT COD|MAIL|carefully express depo| +4515|45|8|5|22|20790.88|0.09|0.07|A|F|1992-07-16|1992-05-07|1992-07-23|NONE|SHIP|le quickly above the even, bold ideas.| +4515|180|8|6|23|24844.14|0.04|0.00|R|F|1992-05-23|1992-06-15|1992-06-20|TAKE BACK RETURN|FOB|ns. bold r| +4516|170|9|1|34|36385.78|0.05|0.04|A|F|1994-05-16|1994-06-23|1994-06-12|NONE|SHIP|even pinto beans wake qui| +4517|43|4|1|50|47152.00|0.01|0.02|N|O|1998-06-08|1998-04-18|1998-06-20|DELIVER IN PERSON|MAIL|refully pending acco| +4518|144|7|1|9|9397.26|0.09|0.04|N|O|1997-06-26|1997-07-07|1997-07-10|NONE|RAIL| pending deposits. slyly re| +4518|45|6|2|19|17955.76|0.10|0.05|N|O|1997-08-09|1997-06-06|1997-08-27|COLLECT COD|RAIL|ter the slyly bo| +4519|55|3|1|30|28651.50|0.09|0.07|R|F|1993-04-11|1993-06-05|1993-04-22|DELIVER IN PERSON|REG AIR|totes. slyly bold somas after the | +4519|191|3|2|37|40374.03|0.06|0.08|R|F|1993-07-22|1993-06-16|1993-08-19|COLLECT COD|AIR|ly slyly furious depth| +4544|131|7|1|40|41245.20|0.07|0.01|N|O|1997-08-15|1997-10-16|1997-08-20|DELIVER IN PERSON|RAIL| detect slyly. evenly pending instru| +4544|172|2|2|19|20371.23|0.08|0.01|N|O|1997-08-14|1997-09-08|1997-08-25|NONE|SHIP|regular ideas are furiously about| +4544|71|9|3|20|19421.40|0.02|0.07|N|O|1997-10-12|1997-10-11|1997-10-13|COLLECT COD|REG AIR| waters about the| +4544|51|6|4|39|37090.95|0.07|0.05|N|O|1997-08-20|1997-09-07|1997-08-27|COLLECT COD|REG AIR|ular packages. s| +4544|133|4|5|31|32027.03|0.09|0.03|N|O|1997-08-09|1997-09-29|1997-08-17|COLLECT COD|TRUCK|dolites detect quickly reg| +4544|27|8|6|8|7416.16|0.10|0.03|N|O|1997-10-13|1997-10-06|1997-10-25|COLLECT COD|AIR|olites. fi| +4545|173|1|1|38|40780.46|0.06|0.06|R|F|1993-01-27|1993-03-01|1993-02-04|NONE|TRUCK|nts serve according to th| +4545|63|4|2|27|26002.62|0.01|0.06|R|F|1993-02-07|1993-02-18|1993-02-18|NONE|FOB|ously bold asymptotes! blithely pen| +4545|87|8|3|9|8883.72|0.10|0.06|R|F|1993-03-20|1993-02-23|1993-04-11|TAKE BACK RETURN|AIR|xpress accounts| +4545|64|9|4|2|1928.12|0.10|0.00|R|F|1993-04-16|1993-04-17|1993-05-03|NONE|REG AIR|ages use. slyly even i| +4545|117|1|5|27|27461.97|0.08|0.05|A|F|1993-03-18|1993-02-22|1993-03-23|NONE|RAIL|ccounts haggle carefully. deposits | +4545|109|2|6|8|8072.80|0.03|0.02|A|F|1993-05-01|1993-03-12|1993-05-15|NONE|FOB| boost slyly. slyly| +4545|9|2|7|36|32724.00|0.10|0.04|R|F|1993-01-28|1993-03-30|1993-02-04|DELIVER IN PERSON|SHIP|sublate slyly. furiously ironic accounts b| +4546|133|4|1|10|10331.30|0.09|0.02|N|O|1995-09-23|1995-10-10|1995-10-23|COLLECT COD|TRUCK|osits alongside of the| +4546|171|10|2|15|16067.55|0.04|0.07|N|O|1995-07-31|1995-10-17|1995-08-06|NONE|REG AIR|ught to cajole furiously. qu| +4546|77|8|3|4|3908.28|0.06|0.08|N|O|1995-08-14|1995-10-07|1995-08-16|COLLECT COD|MAIL|kly pending dependencies along the furio| +4546|149|6|4|10|10491.40|0.08|0.02|N|O|1995-09-02|1995-09-16|1995-09-10|DELIVER IN PERSON|FOB|above the enticingly ironic dependencies| +4547|188|9|1|15|16322.70|0.10|0.04|A|F|1993-12-08|1993-11-15|1993-12-22|NONE|REG AIR|ets haggle. regular dinos affix fu| +4547|116|10|2|7|7112.77|0.10|0.02|A|F|1993-09-04|1993-09-29|1993-09-20|COLLECT COD|RAIL|slyly express a| +4547|45|2|3|15|14175.60|0.00|0.00|R|F|1993-11-18|1993-10-06|1993-12-13|NONE|TRUCK|e carefully across the unus| +4547|148|7|4|15|15722.10|0.05|0.08|R|F|1993-11-29|1993-10-12|1993-12-29|COLLECT COD|REG AIR|ironic gifts integrate | +4548|14|8|1|21|19194.21|0.10|0.05|N|O|1996-07-11|1996-09-04|1996-07-30|COLLECT COD|REG AIR|pecial theodoli| +4548|47|10|2|17|16099.68|0.00|0.08|N|O|1996-07-23|1996-09-21|1996-07-26|DELIVER IN PERSON|REG AIR|y ironic requests above the fluffily d| +4548|123|2|3|47|48086.64|0.05|0.04|N|O|1996-07-24|1996-09-12|1996-08-08|NONE|MAIL|ts. excuses use slyly spec| +4548|177|6|4|22|23697.74|0.07|0.01|N|O|1996-07-06|1996-08-23|1996-07-15|DELIVER IN PERSON|RAIL|s. furiously ironic theodolites c| +4548|45|4|5|36|34021.44|0.04|0.06|N|O|1996-08-19|1996-09-12|1996-09-08|COLLECT COD|FOB|tions integrat| +4549|159|1|1|44|46602.60|0.08|0.00|N|O|1998-03-13|1998-04-15|1998-03-27|TAKE BACK RETURN|TRUCK|ding to the regular, silent requests| +4549|89|10|2|1|989.08|0.05|0.08|N|O|1998-05-04|1998-04-11|1998-05-14|TAKE BACK RETURN|AIR| requests wake. furiously even | +4550|150|7|1|9|9451.35|0.05|0.06|R|F|1995-04-19|1995-02-07|1995-04-24|COLLECT COD|SHIP|l dependencies boost slyly after th| +4550|66|5|2|19|18355.14|0.06|0.04|A|F|1995-01-01|1995-02-13|1995-01-20|NONE|AIR|quests. express | +4551|11|1|1|6|5466.06|0.08|0.08|N|O|1996-05-18|1996-04-23|1996-06-13|DELIVER IN PERSON|TRUCK|fily silent fo| +4551|179|8|2|26|28058.42|0.02|0.04|N|O|1996-04-14|1996-04-26|1996-04-17|TAKE BACK RETURN|RAIL|le. carefully dogged accounts use furiousl| +4551|22|1|3|22|20284.44|0.08|0.01|N|O|1996-05-12|1996-03-17|1996-05-29|TAKE BACK RETURN|REG AIR|ly ironic reques| +4551|198|10|4|27|29651.13|0.00|0.01|N|O|1996-04-28|1996-03-22|1996-05-22|TAKE BACK RETURN|RAIL|y along the slyly even | +4576|90|1|1|5|4950.45|0.09|0.03|N|O|1996-08-23|1996-11-08|1996-09-20|TAKE BACK RETURN|AIR|ly express, special asymptote| +4576|58|9|2|43|41196.15|0.08|0.06|N|O|1996-10-24|1996-09-23|1996-11-10|NONE|SHIP|ly final deposits. never| +4576|42|1|3|14|13188.56|0.09|0.01|N|O|1996-09-12|1996-09-30|1996-09-24|COLLECT COD|MAIL|detect slyly.| +4577|185|6|1|43|46662.74|0.01|0.03|N|O|1998-06-16|1998-07-09|1998-06-17|TAKE BACK RETURN|AIR|packages. | +4577|177|6|2|43|46318.31|0.05|0.03|N|O|1998-08-24|1998-06-02|1998-09-14|TAKE BACK RETURN|RAIL|ly accounts. carefully | +4577|69|6|3|12|11628.72|0.07|0.05|N|O|1998-07-29|1998-06-17|1998-08-04|DELIVER IN PERSON|TRUCK|equests alongsi| +4578|74|2|1|10|9740.70|0.09|0.06|R|F|1993-01-01|1992-11-19|1993-01-28|TAKE BACK RETURN|REG AIR|uests. blithely unus| +4578|169|10|2|42|44904.72|0.06|0.00|R|F|1993-01-05|1992-11-06|1993-01-13|DELIVER IN PERSON|FOB|s are caref| +4578|179|8|3|15|16187.55|0.01|0.01|R|F|1992-10-23|1992-11-22|1992-11-09|DELIVER IN PERSON|REG AIR|gular theodo| +4578|139|10|4|7|7273.91|0.09|0.08|A|F|1992-12-07|1992-11-27|1993-01-05|TAKE BACK RETURN|SHIP|odolites. carefully unusual ideas accor| +4578|163|2|5|20|21263.20|0.04|0.02|A|F|1993-01-11|1992-11-09|1993-01-23|TAKE BACK RETURN|RAIL|iously pending theodolites--| +4579|175|4|1|14|15052.38|0.02|0.02|N|O|1996-02-01|1996-01-08|1996-02-08|TAKE BACK RETURN|MAIL|nding theodolites. fluffil| +4579|42|3|2|28|26377.12|0.02|0.05|N|O|1996-01-22|1996-02-13|1996-02-03|DELIVER IN PERSON|RAIL|slyly across the | +4579|178|9|3|34|36657.78|0.05|0.02|N|O|1996-02-26|1996-02-22|1996-03-16|COLLECT COD|MAIL|hely. carefully blithe dependen| +4579|120|1|4|8|8160.96|0.05|0.06|N|O|1995-12-16|1996-01-15|1995-12-18|TAKE BACK RETURN|AIR|posits. carefully perman| +4580|92|5|1|22|21825.98|0.01|0.05|A|F|1994-01-16|1994-01-26|1994-02-05|COLLECT COD|AIR|nticingly final packag| +4580|32|3|2|10|9320.30|0.05|0.04|R|F|1993-12-20|1993-12-30|1994-01-17|COLLECT COD|RAIL|gular, pending deposits. fina| +4580|1|8|3|41|36941.00|0.00|0.07|R|F|1993-12-13|1994-01-31|1994-01-06|NONE|SHIP|requests. quickly silent asymptotes sle| +4580|178|8|4|5|5390.85|0.07|0.00|A|F|1994-01-28|1993-12-17|1994-02-22|NONE|TRUCK|o beans. f| +4580|189|10|5|39|42478.02|0.03|0.02|R|F|1993-12-28|1993-12-26|1994-01-23|NONE|RAIL|. fluffily final dolphins use furiously al| +4581|165|4|1|37|39410.92|0.01|0.04|A|F|1992-10-17|1992-11-05|1992-11-04|DELIVER IN PERSON|MAIL|e the blithely bold pearls ha| +4581|50|3|2|7|6650.35|0.01|0.02|A|F|1992-10-09|1992-10-20|1992-10-21|TAKE BACK RETURN|MAIL|express accounts d| +4581|21|10|3|46|42366.92|0.04|0.04|A|F|1992-09-09|1992-11-27|1992-09-26|NONE|REG AIR|nag toward the carefully final accounts. | +4582|192|5|1|17|18567.23|0.09|0.08|N|O|1996-08-17|1996-08-26|1996-08-20|COLLECT COD|REG AIR|ng packages. depo| +4583|141|2|1|17|17699.38|0.01|0.05|A|F|1994-11-08|1994-11-03|1994-11-29|COLLECT COD|MAIL|romise. reques| +4583|187|8|2|43|46748.74|0.04|0.04|A|F|1994-10-30|1994-12-17|1994-11-16|COLLECT COD|RAIL|fully after the speci| +4583|196|10|3|28|30693.32|0.00|0.07|A|F|1994-10-29|1994-11-21|1994-11-28|NONE|SHIP|to beans haggle sly| +4583|173|4|4|27|28975.59|0.08|0.03|R|F|1995-01-11|1994-12-24|1995-02-10|DELIVER IN PERSON|TRUCK| detect silent requests. furiously speci| +4583|184|5|5|36|39030.48|0.09|0.06|A|F|1995-01-06|1994-11-25|1995-01-29|DELIVER IN PERSON|RAIL|ar requests haggle after the furiously | +4583|122|7|6|14|14309.68|0.09|0.01|R|F|1994-11-17|1994-11-08|1994-11-21|DELIVER IN PERSON|AIR|detect. doggedly regular pi| +4583|87|8|7|32|31586.56|0.04|0.00|A|F|1995-01-13|1994-10-29|1995-02-08|TAKE BACK RETURN|RAIL|across the pinto beans-- quickly| +4608|173|1|1|30|32195.10|0.08|0.05|R|F|1994-10-08|1994-07-18|1994-10-25|DELIVER IN PERSON|SHIP|s cajole. slyly | +4608|47|8|2|50|47352.00|0.06|0.01|A|F|1994-07-25|1994-09-01|1994-08-10|NONE|FOB| theodolites| +4608|79|9|3|50|48953.50|0.03|0.01|A|F|1994-08-04|1994-09-10|1994-08-13|COLLECT COD|TRUCK| wake closely. even decoys haggle above| +4608|31|2|4|36|33517.08|0.05|0.06|R|F|1994-10-04|1994-08-02|1994-10-21|COLLECT COD|FOB|ages wake quickly slyly iron| +4609|47|6|1|28|26517.12|0.10|0.05|N|O|1997-02-02|1997-02-17|1997-03-02|DELIVER IN PERSON|REG AIR|ously. quickly final requests cajole fl| +4609|185|6|2|3|3255.54|0.09|0.03|N|O|1996-12-28|1997-02-06|1997-01-20|NONE|FOB|nstructions. furious instructions | +4609|23|4|3|46|42458.92|0.05|0.05|N|O|1997-02-11|1997-01-16|1997-03-07|NONE|FOB|r foxes. fluffily ironic ideas ha| +4610|87|8|1|21|20728.68|0.07|0.07|R|F|1993-08-10|1993-08-05|1993-08-27|NONE|REG AIR|ly special theodolites. even,| +4610|175|5|2|14|15052.38|0.00|0.07|R|F|1993-07-28|1993-07-25|1993-07-31|TAKE BACK RETURN|SHIP| ironic frays. dependencies detect blithel| +4610|159|1|3|44|46602.60|0.05|0.03|A|F|1993-08-05|1993-07-20|1993-08-19|COLLECT COD|TRUCK| final theodolites | +4610|75|3|4|26|25351.82|0.06|0.03|R|F|1993-07-01|1993-07-19|1993-07-19|NONE|MAIL| to the fluffily ironic requests h| +4610|147|8|5|29|30367.06|0.08|0.04|R|F|1993-08-09|1993-07-27|1993-08-16|DELIVER IN PERSON|AIR| foxes. special, express package| +4611|52|7|1|47|44746.35|0.09|0.06|A|F|1993-03-05|1993-03-01|1993-03-17|COLLECT COD|TRUCK|iously. furiously regular| +4611|35|6|2|31|28985.93|0.04|0.02|A|F|1993-01-28|1993-02-14|1993-01-29|TAKE BACK RETURN|AIR| final pinto beans. permanent, sp| +4611|82|3|3|50|49104.00|0.08|0.01|R|F|1993-01-22|1993-03-30|1993-02-16|TAKE BACK RETURN|AIR|l platelets. | +4611|71|9|4|48|46611.36|0.02|0.08|R|F|1993-02-28|1993-02-12|1993-03-01|COLLECT COD|AIR|ular accounts | +4612|6|9|1|20|18120.00|0.02|0.03|R|F|1993-09-24|1993-12-18|1993-10-22|NONE|AIR|beans sleep blithely iro| +4612|50|7|2|17|16150.85|0.10|0.06|A|F|1994-01-09|1993-11-08|1994-02-06|TAKE BACK RETURN|REG AIR|equests haggle carefully silent excus| +4612|137|8|3|40|41485.20|0.08|0.01|R|F|1993-10-08|1993-11-23|1993-10-24|DELIVER IN PERSON|RAIL|special platelets.| +4612|185|6|4|10|10851.80|0.10|0.06|A|F|1993-11-11|1993-11-19|1993-11-13|TAKE BACK RETURN|SHIP|unusual theodol| +4613|38|9|1|17|15946.51|0.09|0.07|N|O|1998-06-07|1998-05-11|1998-06-29|DELIVER IN PERSON|SHIP|liers cajole a| +4613|108|1|2|25|25202.50|0.05|0.04|N|O|1998-05-22|1998-04-11|1998-05-27|TAKE BACK RETURN|SHIP|y pending platelets x-ray ironically! pend| +4613|174|3|3|15|16112.55|0.10|0.02|N|O|1998-05-31|1998-04-16|1998-06-25|DELIVER IN PERSON|MAIL|against the quickly r| +4613|8|1|4|36|32688.00|0.04|0.01|N|O|1998-04-22|1998-05-05|1998-05-04|DELIVER IN PERSON|AIR|gainst the furiously ironic| +4613|111|8|5|35|35388.85|0.04|0.06|N|O|1998-06-04|1998-04-17|1998-06-20|COLLECT COD|MAIL|e blithely against the even, bold pi| +4613|196|8|6|47|51520.93|0.04|0.04|N|O|1998-07-03|1998-05-26|1998-07-09|NONE|FOB|uriously special requests wak| +4613|119|3|7|39|39745.29|0.09|0.05|N|O|1998-06-12|1998-06-01|1998-07-06|DELIVER IN PERSON|REG AIR|ously express| +4614|7|2|1|19|17233.00|0.09|0.08|N|O|1996-05-17|1996-06-21|1996-06-08|TAKE BACK RETURN|AIR|ix. carefully regular | +4614|65|6|2|3|2895.18|0.08|0.01|N|O|1996-07-22|1996-07-21|1996-08-07|NONE|MAIL|ions engage final, ironic | +4614|8|1|3|36|32688.00|0.10|0.04|N|O|1996-07-05|1996-06-26|1996-07-07|NONE|REG AIR|onic foxes affix furi| +4614|126|9|4|6|6156.72|0.09|0.01|N|O|1996-06-11|1996-05-30|1996-07-03|COLLECT COD|REG AIR|ake quickly quickly regular epitap| +4614|73|3|5|24|23353.68|0.07|0.06|N|O|1996-07-01|1996-06-24|1996-07-08|COLLECT COD|REG AIR|regular, even| +4614|34|5|6|32|29888.96|0.10|0.05|N|O|1996-08-21|1996-05-28|1996-09-16|NONE|REG AIR|ickly furio| +4614|128|1|7|41|42152.92|0.01|0.07|N|O|1996-07-31|1996-07-12|1996-08-16|COLLECT COD|REG AIR|ackages haggle carefully about the even, b| +4615|92|4|1|10|9920.90|0.02|0.08|A|F|1993-11-20|1993-10-05|1993-12-08|DELIVER IN PERSON|AIR|sits. slyly express deposits are| +4640|88|9|1|5|4940.40|0.03|0.08|N|O|1996-02-05|1996-02-14|1996-02-15|TAKE BACK RETURN|RAIL| warthogs against the regular| +4640|88|9|2|9|8892.72|0.03|0.05|N|O|1996-02-12|1996-02-14|1996-02-29|DELIVER IN PERSON|AIR| accounts. unu| +4640|27|10|3|18|16686.36|0.02|0.07|N|O|1996-02-28|1996-03-06|1996-03-28|DELIVER IN PERSON|RAIL|boost furiously accord| +4640|23|2|4|36|33228.72|0.06|0.08|N|O|1996-01-03|1996-03-09|1996-01-11|DELIVER IN PERSON|RAIL|iously furious accounts boost. carefully| +4640|156|1|5|15|15842.25|0.03|0.02|N|O|1996-03-19|1996-02-09|1996-04-11|TAKE BACK RETURN|FOB|y regular instructions doze furiously. reg| +4641|190|1|1|45|49058.55|0.07|0.03|R|F|1993-05-11|1993-04-19|1993-05-21|DELIVER IN PERSON|MAIL| about the close | +4641|95|7|2|39|38808.51|0.06|0.00|R|F|1993-02-10|1993-03-06|1993-02-15|TAKE BACK RETURN|REG AIR| the bold reque| +4641|36|7|3|15|14040.45|0.01|0.08|R|F|1993-01-25|1993-04-09|1993-02-05|TAKE BACK RETURN|AIR|s. carefully even exc| +4642|194|7|1|11|12036.09|0.04|0.07|A|F|1995-05-23|1995-04-26|1995-06-04|COLLECT COD|TRUCK|lithely express asympt| +4642|180|10|2|34|36726.12|0.04|0.07|R|F|1995-04-01|1995-05-11|1995-04-23|COLLECT COD|SHIP|theodolites detect among the ironically sp| +4642|21|2|3|10|9210.20|0.04|0.02|R|F|1995-04-16|1995-04-28|1995-04-24|COLLECT COD|RAIL|urts. even deposits nag beneath | +4642|94|7|4|18|17893.62|0.00|0.04|N|F|1995-06-16|1995-04-16|1995-06-21|NONE|TRUCK|ily pending accounts hag| +4642|179|10|5|41|44245.97|0.10|0.00|R|F|1995-04-08|1995-04-13|1995-05-01|DELIVER IN PERSON|MAIL|s are blithely. requests wake above the fur| +4643|185|6|1|50|54259.00|0.08|0.05|N|O|1995-09-11|1995-08-13|1995-09-30|DELIVER IN PERSON|SHIP|. ironic deposits cajo| +4644|177|7|1|4|4308.68|0.06|0.03|N|O|1998-05-06|1998-03-19|1998-05-28|NONE|MAIL|gular requests? pendi| +4644|97|8|2|16|15953.44|0.03|0.04|N|O|1998-03-13|1998-02-21|1998-04-03|COLLECT COD|SHIP|lar excuses across the | +4644|115|9|3|10|10151.10|0.02|0.02|N|O|1998-02-21|1998-02-28|1998-03-19|COLLECT COD|REG AIR|osits according to the| +4644|154|2|4|45|47436.75|0.10|0.07|N|O|1998-02-02|1998-04-08|1998-02-15|COLLECT COD|SHIP| carefully a| +4644|87|8|5|10|9870.80|0.08|0.08|N|O|1998-03-12|1998-03-11|1998-03-19|TAKE BACK RETURN|REG AIR| the slow, final fo| +4645|50|7|1|45|42752.25|0.09|0.05|A|F|1994-12-27|1994-11-02|1994-12-31|DELIVER IN PERSON|AIR|ular ideas. slyly| +4645|66|7|2|32|30913.92|0.10|0.08|A|F|1994-11-17|1994-10-30|1994-11-18|COLLECT COD|REG AIR| final accounts alongside| +4645|54|5|3|25|23851.25|0.03|0.00|R|F|1994-10-25|1994-12-11|1994-11-14|NONE|REG AIR|braids. ironic dependencies main| +4645|37|8|4|42|39355.26|0.10|0.02|R|F|1994-12-02|1994-12-18|1994-12-16|COLLECT COD|TRUCK|regular pinto beans amon| +4645|161|10|5|35|37140.60|0.03|0.07|A|F|1994-12-08|1994-11-25|1994-12-09|TAKE BACK RETURN|FOB|sias believe bl| +4645|42|9|6|27|25435.08|0.09|0.08|R|F|1994-11-26|1994-10-25|1994-12-04|NONE|SHIP|ously express pinto beans. ironic depos| +4645|31|2|7|42|39103.26|0.10|0.06|A|F|1994-12-31|1994-10-22|1995-01-28|DELIVER IN PERSON|AIR|e slyly regular pinto beans. thin| +4646|191|3|1|24|26188.56|0.02|0.05|N|O|1996-09-18|1996-08-09|1996-09-21|TAKE BACK RETURN|RAIL|ic platelets lose carefully. blithely unu| +4646|178|6|2|26|28032.42|0.07|0.00|N|O|1996-10-02|1996-08-25|1996-10-27|DELIVER IN PERSON|MAIL|ix according to the slyly spe| +4646|34|10|3|18|16812.54|0.01|0.00|N|O|1996-06-30|1996-08-10|1996-07-12|TAKE BACK RETURN|TRUCK|beans sleep car| +4646|40|1|4|38|35721.52|0.08|0.01|N|O|1996-09-01|1996-08-23|1996-09-27|COLLECT COD|SHIP|al platelets cajole. slyly final dol| +4646|26|1|5|22|20372.44|0.01|0.08|N|O|1996-07-14|1996-08-06|1996-07-29|DELIVER IN PERSON|MAIL|cies are blithely after the slyly reg| +4647|93|6|1|16|15889.44|0.09|0.07|R|F|1994-09-07|1994-07-15|1994-10-06|COLLECT COD|RAIL|o beans about the fluffily special the| +4647|129|2|2|34|34990.08|0.01|0.02|R|F|1994-05-20|1994-06-20|1994-05-29|COLLECT COD|TRUCK|ly sly accounts| +4647|147|8|3|27|28272.78|0.03|0.08|R|F|1994-05-20|1994-06-26|1994-05-30|NONE|FOB|ully even ti| +4647|139|10|4|2|2078.26|0.04|0.07|R|F|1994-07-03|1994-07-22|1994-07-22|TAKE BACK RETURN|RAIL|dolites wake furiously special pinto be| +4647|187|8|5|2|2174.36|0.07|0.06|A|F|1994-05-27|1994-08-05|1994-06-10|TAKE BACK RETURN|FOB| pinto beans believe furiously slyly silent| +4647|29|4|6|28|26012.56|0.02|0.03|A|F|1994-08-25|1994-08-06|1994-09-18|DELIVER IN PERSON|FOB| are above the fluffily fin| +4672|59|7|1|22|21099.10|0.01|0.07|N|O|1995-12-03|1995-12-08|1995-12-17|COLLECT COD|AIR|l instructions. blithely ironic packages | +4672|61|10|2|41|39403.46|0.00|0.00|N|O|1995-12-01|1995-12-15|1995-12-12|COLLECT COD|RAIL| slyly quie| +4672|163|10|3|24|25515.84|0.04|0.03|N|O|1995-11-11|1995-12-28|1995-12-04|NONE|REG AIR|y fluffily stealt| +4672|57|2|4|13|12441.65|0.10|0.03|N|O|1996-02-02|1995-12-13|1996-03-02|DELIVER IN PERSON|RAIL|ar requests? pending accounts against| +4672|55|10|5|45|42977.25|0.08|0.07|N|O|1996-02-07|1996-01-16|1996-02-14|DELIVER IN PERSON|MAIL| platelets use amon| +4672|141|8|6|20|20822.80|0.02|0.07|N|O|1995-12-08|1996-01-25|1995-12-19|COLLECT COD|REG AIR|s boost at the ca| +4672|72|10|7|38|36938.66|0.01|0.01|N|O|1995-11-28|1995-12-08|1995-12-13|COLLECT COD|SHIP|ests. idle, regular ex| +4673|17|8|1|8|7336.08|0.08|0.01|N|O|1996-10-12|1996-10-05|1996-11-04|TAKE BACK RETURN|FOB|lithely final re| +4673|101|2|2|44|44048.40|0.06|0.01|N|O|1996-12-11|1996-10-31|1997-01-08|DELIVER IN PERSON|RAIL| gifts cajole dari| +4673|123|2|3|9|9208.08|0.04|0.07|N|O|1996-10-15|1996-09-30|1996-10-30|DELIVER IN PERSON|MAIL|ages nag across | +4674|150|7|1|50|52507.50|0.07|0.08|A|F|1994-05-13|1994-06-15|1994-06-05|COLLECT COD|RAIL|haggle about the blithel| +4674|189|10|2|35|38121.30|0.02|0.05|A|F|1994-08-02|1994-06-04|1994-08-21|COLLECT COD|FOB|le quickly after the express sent| +4674|111|5|3|3|3033.33|0.01|0.05|A|F|1994-07-19|1994-05-28|1994-07-23|TAKE BACK RETURN|RAIL| regular requests na| +4674|13|7|4|21|19173.21|0.02|0.08|R|F|1994-05-08|1994-07-02|1994-06-04|COLLECT COD|RAIL|ent accounts sublate deposits. instruc| +4675|171|2|1|6|6427.02|0.00|0.05|R|F|1994-01-22|1994-01-06|1994-02-12|TAKE BACK RETURN|TRUCK| unusual ideas thrash bl| +4675|144|7|2|12|12529.68|0.00|0.04|A|F|1993-12-22|1994-01-12|1993-12-23|TAKE BACK RETURN|AIR|posits affix carefully| +4675|181|2|3|5|5405.90|0.05|0.05|A|F|1994-01-16|1994-01-05|1994-01-18|DELIVER IN PERSON|RAIL|lent pinto beans| +4675|34|10|4|26|24284.78|0.03|0.01|A|F|1993-12-16|1993-12-29|1993-12-23|DELIVER IN PERSON|SHIP|nts. express requests are quickly | +4675|81|2|5|18|17659.44|0.01|0.08|R|F|1994-02-23|1994-01-18|1994-03-05|TAKE BACK RETURN|FOB|cajole unusual dep| +4675|119|10|6|1|1019.11|0.10|0.06|R|F|1994-03-18|1994-02-14|1994-04-17|NONE|SHIP|unts. caref| +4676|165|2|1|47|50062.52|0.03|0.06|N|O|1995-12-20|1995-10-04|1996-01-09|NONE|AIR|lithely about the carefully special requ| +4676|6|1|2|33|29898.00|0.08|0.05|N|O|1995-12-29|1995-10-01|1996-01-18|TAKE BACK RETURN|FOB|yly express | +4676|146|3|3|4|4184.56|0.10|0.06|N|O|1995-12-12|1995-10-22|1995-12-13|TAKE BACK RETURN|TRUCK|detect above the ironic platelets. fluffily| +4676|111|2|4|50|50555.50|0.07|0.01|N|O|1995-09-20|1995-11-20|1995-10-18|TAKE BACK RETURN|AIR|r deposits boost boldly quickly quick asymp| +4676|122|7|5|29|29641.48|0.01|0.02|N|O|1995-12-29|1995-11-12|1996-01-06|TAKE BACK RETURN|RAIL|ly regular theodolites sleep.| +4676|46|7|6|8|7568.32|0.08|0.08|N|O|1995-12-05|1995-10-18|1996-01-02|COLLECT COD|AIR|cuses boost above| +4676|64|1|7|13|12532.78|0.05|0.07|N|O|1995-11-18|1995-11-07|1995-12-10|TAKE BACK RETURN|TRUCK| at the slyly bold attainments. silently e| +4677|128|3|1|25|25703.00|0.04|0.04|N|O|1998-04-11|1998-05-11|1998-04-18|TAKE BACK RETURN|REG AIR|unts doubt furiousl| +4678|58|6|1|35|33531.75|0.04|0.08|N|O|1998-11-27|1998-10-02|1998-12-17|TAKE BACK RETURN|AIR|he accounts. fluffily bold sheaves b| +4678|117|1|2|18|18307.98|0.03|0.06|N|O|1998-10-30|1998-09-22|1998-11-25|TAKE BACK RETURN|SHIP|usly ironic | +4678|96|9|3|13|12949.17|0.10|0.07|N|O|1998-11-03|1998-10-17|1998-11-06|TAKE BACK RETURN|SHIP|its. carefully final fr| +4678|22|1|4|23|21206.46|0.06|0.05|N|O|1998-09-03|1998-09-20|1998-09-04|DELIVER IN PERSON|SHIP|ily sly deposi| +4678|178|9|5|40|43126.80|0.03|0.07|N|O|1998-11-11|1998-10-27|1998-11-24|TAKE BACK RETURN|AIR|. final, unusual requests sleep thinl| +4679|190|1|1|7|7631.33|0.10|0.05|R|F|1993-05-11|1993-04-11|1993-05-16|NONE|TRUCK|kages. bold, regular packa| +4704|78|6|1|14|13692.98|0.04|0.04|N|O|1996-10-27|1996-11-02|1996-11-07|DELIVER IN PERSON|TRUCK| above the slyly final requests. quickly | +4704|28|3|2|7|6496.14|0.03|0.04|N|O|1996-12-04|1996-10-30|1996-12-23|DELIVER IN PERSON|SHIP|ers wake car| +4704|64|5|3|44|42418.64|0.02|0.05|N|O|1996-09-02|1996-10-07|1996-09-17|DELIVER IN PERSON|REG AIR|out the care| +4705|111|8|1|22|22244.42|0.04|0.04|R|F|1992-07-05|1992-05-11|1992-07-29|DELIVER IN PERSON|SHIP| fluffily pending accounts ca| +4705|31|7|2|14|13034.42|0.00|0.08|R|F|1992-07-14|1992-05-23|1992-07-25|DELIVER IN PERSON|TRUCK|ain carefully amon| +4705|56|1|3|16|15296.80|0.07|0.08|R|F|1992-07-02|1992-06-06|1992-07-06|DELIVER IN PERSON|RAIL|special ideas nag sl| +4705|130|3|4|31|31934.03|0.03|0.03|R|F|1992-04-03|1992-05-30|1992-04-05|COLLECT COD|TRUCK|furiously final accou| +4705|163|10|5|28|29768.48|0.10|0.01|A|F|1992-06-03|1992-06-07|1992-06-22|DELIVER IN PERSON|MAIL|tes wake according to the unusual plate| +4705|184|5|6|23|24936.14|0.06|0.03|R|F|1992-06-22|1992-06-11|1992-07-18|DELIVER IN PERSON|MAIL| above the furiously ev| +4705|89|10|7|40|39563.20|0.08|0.06|A|F|1992-04-19|1992-04-28|1992-05-07|COLLECT COD|TRUCK|blithely. sly| +4706|182|3|1|37|40040.66|0.02|0.06|A|F|1993-02-20|1993-03-05|1993-03-03|DELIVER IN PERSON|TRUCK|kly final deposits c| +4706|122|3|2|23|23508.76|0.03|0.01|A|F|1993-04-01|1993-03-13|1993-05-01|COLLECT COD|FOB|deas across t| +4706|68|5|3|6|5808.36|0.01|0.04|R|F|1993-01-20|1993-03-18|1993-01-26|NONE|MAIL|efully eve| +4706|116|10|4|5|5080.55|0.06|0.06|R|F|1993-02-14|1993-01-31|1993-02-26|NONE|REG AIR|ptotes haggle ca| +4706|50|7|5|27|25651.35|0.06|0.08|A|F|1993-04-04|1993-03-11|1993-04-09|COLLECT COD|REG AIR|into beans. finally special instruct| +4707|34|5|1|7|6538.21|0.02|0.05|R|F|1995-05-14|1995-04-06|1995-06-06|COLLECT COD|SHIP|ecial sheaves boost blithely accor| +4707|136|7|2|49|50770.37|0.00|0.07|N|F|1995-06-17|1995-05-16|1995-06-25|COLLECT COD|FOB| alongside of the slyly ironic instructio| +4708|191|4|1|18|19641.42|0.02|0.04|A|F|1994-11-11|1994-11-15|1994-11-26|NONE|REG AIR|special, eve| +4708|75|3|2|5|4875.35|0.05|0.05|A|F|1994-10-15|1994-12-02|1994-11-12|COLLECT COD|MAIL|ely. carefully sp| +4708|77|7|3|32|31266.24|0.04|0.07|A|F|1994-11-12|1994-11-14|1994-11-23|TAKE BACK RETURN|MAIL|the accounts. e| +4709|25|6|1|25|23125.50|0.03|0.05|N|O|1996-02-21|1996-02-11|1996-03-17|DELIVER IN PERSON|AIR|deposits grow. fluffily unusual accounts | +4709|177|5|2|25|26929.25|0.05|0.03|N|O|1996-01-22|1996-03-03|1996-02-21|DELIVER IN PERSON|REG AIR|inst the ironic, regul| +4710|183|4|1|40|43327.20|0.10|0.08|A|F|1995-03-09|1995-02-25|1995-03-29|TAKE BACK RETURN|AIR|cross the blithely bold packages. silen| +4710|128|3|2|47|48321.64|0.04|0.01|R|F|1995-02-22|1995-01-12|1995-02-28|NONE|RAIL|blithely express packages. even, ironic re| +4711|133|4|1|7|7231.91|0.03|0.01|N|O|1998-05-12|1998-06-24|1998-05-24|COLLECT COD|MAIL|ly. bold accounts use fluff| +4711|145|6|2|15|15677.10|0.08|0.07|N|O|1998-06-09|1998-07-30|1998-06-18|COLLECT COD|SHIP| beans wake. deposits could bo| +4711|150|1|3|22|23103.30|0.02|0.03|N|O|1998-06-21|1998-06-18|1998-07-19|TAKE BACK RETURN|REG AIR|along the quickly careful packages. bli| +4711|65|10|4|8|7720.48|0.07|0.00|N|O|1998-06-17|1998-06-13|1998-06-27|TAKE BACK RETURN|SHIP|g to the carefully ironic deposits. specia| +4711|49|2|5|15|14235.60|0.05|0.01|N|O|1998-09-03|1998-07-15|1998-09-13|TAKE BACK RETURN|SHIP|ld requests: furiously final inst| +4711|116|7|6|45|45724.95|0.05|0.06|N|O|1998-05-19|1998-07-14|1998-05-21|COLLECT COD|SHIP| ironic theodolites | +4711|46|5|7|18|17028.72|0.05|0.04|N|O|1998-07-03|1998-07-31|1998-07-23|DELIVER IN PERSON|RAIL| blithely. bold asymptote| +4736|196|10|1|26|28500.94|0.03|0.03|N|O|1996-02-02|1996-01-18|1996-02-09|DELIVER IN PERSON|AIR|efully speci| +4736|4|1|2|43|38872.00|0.06|0.07|N|O|1996-02-05|1995-12-21|1996-02-06|COLLECT COD|MAIL|quests. carefully | +4737|191|5|1|37|40374.03|0.03|0.04|R|F|1993-05-17|1993-04-10|1993-05-30|DELIVER IN PERSON|TRUCK|s. fluffily regular | +4737|69|8|2|22|21319.32|0.04|0.04|A|F|1993-03-29|1993-05-22|1993-04-16|TAKE BACK RETURN|RAIL| hang fluffily around t| +4738|187|8|1|9|9784.62|0.04|0.04|A|F|1992-06-01|1992-06-26|1992-06-02|COLLECT COD|TRUCK|posits serve slyly. unusual pint| +4738|173|3|2|16|17170.72|0.07|0.08|A|F|1992-06-17|1992-06-20|1992-06-21|NONE|MAIL|nic deposits are slyly! carefu| +4738|100|2|3|50|50005.00|0.04|0.02|A|F|1992-06-18|1992-07-04|1992-07-07|TAKE BACK RETURN|TRUCK|the blithely ironic braids sleep slyly| +4738|29|4|4|22|20438.44|0.02|0.08|A|F|1992-05-25|1992-05-19|1992-06-12|COLLECT COD|SHIP|ld, even packages. furio| +4738|187|8|5|13|14133.34|0.04|0.05|R|F|1992-05-30|1992-06-11|1992-06-26|COLLECT COD|AIR| wake. unusual platelets for the| +4738|159|1|6|10|10591.50|0.10|0.01|R|F|1992-07-10|1992-06-16|1992-07-25|TAKE BACK RETURN|SHIP|hins above the| +4738|83|4|7|28|27526.24|0.05|0.07|A|F|1992-06-09|1992-07-05|1992-06-25|NONE|AIR|e furiously ironic excuses. care| +4739|168|9|1|8|8545.28|0.07|0.07|R|F|1993-06-22|1993-05-10|1993-07-11|TAKE BACK RETURN|SHIP|cording to the | +4739|185|6|2|31|33640.58|0.09|0.06|R|F|1993-06-20|1993-05-18|1993-06-26|COLLECT COD|SHIP|blithely special pin| +4739|100|4|3|30|30003.00|0.09|0.00|A|F|1993-05-29|1993-04-12|1993-06-18|NONE|TRUCK|ly even packages use across th| +4740|3|4|1|22|19866.00|0.06|0.01|N|O|1996-10-04|1996-08-17|1996-10-05|TAKE BACK RETURN|RAIL|final dependencies nag | +4740|153|5|2|24|25275.60|0.08|0.02|N|O|1996-09-10|1996-09-27|1996-10-07|TAKE BACK RETURN|TRUCK|hely regular deposits| +4741|73|2|1|24|23353.68|0.00|0.01|A|F|1992-09-16|1992-09-19|1992-09-20|DELIVER IN PERSON|RAIL|deas boost furiously slyly regular id| +4741|113|4|2|16|16209.76|0.01|0.07|R|F|1992-08-25|1992-08-10|1992-08-29|TAKE BACK RETURN|FOB|final foxes haggle r| +4741|156|8|3|24|25347.60|0.05|0.08|A|F|1992-11-04|1992-08-14|1992-11-06|TAKE BACK RETURN|MAIL|even requests.| +4741|51|3|4|39|37090.95|0.09|0.06|R|F|1992-10-28|1992-10-03|1992-11-11|COLLECT COD|SHIP|t, regular requests| +4741|179|10|5|40|43166.80|0.09|0.03|R|F|1992-09-20|1992-09-23|1992-10-09|TAKE BACK RETURN|REG AIR| fluffily slow deposits. fluffily regu| +4741|157|5|6|34|35943.10|0.02|0.07|R|F|1992-08-25|1992-08-18|1992-09-20|DELIVER IN PERSON|RAIL|sly special packages after the furiously| +4742|156|4|1|32|33796.80|0.10|0.08|R|F|1995-04-04|1995-06-12|1995-04-19|COLLECT COD|RAIL|eposits boost blithely. carefully regular a| +4742|155|7|2|29|30599.35|0.02|0.03|N|F|1995-06-15|1995-05-05|1995-06-24|COLLECT COD|REG AIR|integrate closely among t| +4742|72|10|3|15|14581.05|0.06|0.04|N|O|1995-07-20|1995-05-26|1995-08-11|NONE|SHIP|terns are sl| +4742|188|9|4|31|33733.58|0.05|0.08|N|F|1995-06-13|1995-05-08|1995-06-24|COLLECT COD|REG AIR|ke slyly among the furiousl| +4742|100|1|5|45|45004.50|0.05|0.00|R|F|1995-05-12|1995-05-14|1995-06-07|TAKE BACK RETURN|RAIL|ke carefully. do| +4743|60|5|1|19|18241.14|0.04|0.07|A|F|1993-06-23|1993-05-03|1993-07-20|COLLECT COD|AIR|hely even accounts| +4743|159|4|2|3|3177.45|0.01|0.03|R|F|1993-04-14|1993-06-08|1993-05-09|NONE|TRUCK|al requests. express idea| +4743|73|2|3|21|20434.47|0.08|0.03|A|F|1993-07-02|1993-06-15|1993-07-26|DELIVER IN PERSON|RAIL|ake blithely against the packages. reg| +4743|34|5|4|27|25218.81|0.08|0.05|R|F|1993-07-26|1993-05-27|1993-08-24|DELIVER IN PERSON|AIR|aids use. express deposits| +4768|36|7|1|5|4680.15|0.00|0.03|R|F|1993-12-27|1994-02-09|1994-01-11|NONE|MAIL|egular accounts. bravely final fra| +4769|35|1|1|16|14960.48|0.08|0.05|N|O|1995-07-16|1995-07-05|1995-07-22|TAKE BACK RETURN|FOB| deposits. slyly even asymptote| +4769|63|8|2|34|32744.04|0.06|0.07|N|O|1995-07-26|1995-05-18|1995-08-03|COLLECT COD|REG AIR|ven instructions. ca| +4769|47|10|3|36|34093.44|0.10|0.03|N|O|1995-07-22|1995-06-16|1995-08-11|NONE|RAIL|. slyly even deposit| +4769|69|10|4|45|43607.70|0.08|0.06|R|F|1995-06-01|1995-07-13|1995-06-04|TAKE BACK RETURN|RAIL|accounts are. even accounts sleep| +4769|112|6|5|15|15181.65|0.07|0.08|N|F|1995-06-12|1995-07-07|1995-07-04|NONE|SHIP|egular platelets can cajole across the | +4770|32|8|1|41|38213.23|0.00|0.08|N|O|1995-09-04|1995-08-08|1995-09-10|COLLECT COD|FOB|ithely even packages sleep caref| +4770|157|5|2|30|31714.50|0.09|0.07|N|O|1995-08-25|1995-08-27|1995-09-07|COLLECT COD|SHIP|ffily carefully ironic ideas. ironic d| +4771|49|10|1|9|8541.36|0.01|0.00|R|F|1993-02-28|1993-02-19|1993-03-25|NONE|FOB|riously after the packages. fina| +4771|16|7|2|21|19236.21|0.09|0.01|R|F|1993-01-19|1993-02-10|1993-02-01|NONE|FOB|fluffily pendi| +4771|12|3|3|5|4560.05|0.06|0.08|R|F|1993-01-07|1993-01-19|1993-01-26|NONE|RAIL|ar, quiet accounts nag furiously express id| +4771|9|4|4|21|19089.00|0.05|0.04|A|F|1992-12-20|1993-01-22|1992-12-26|TAKE BACK RETURN|SHIP| carefully re| +4772|87|8|1|1|987.08|0.10|0.00|R|F|1994-11-13|1994-10-25|1994-11-15|DELIVER IN PERSON|AIR|ans. slyly even acc| +4772|146|9|2|16|16738.24|0.07|0.06|R|F|1994-10-27|1994-12-07|1994-10-29|TAKE BACK RETURN|MAIL|egular accounts wake s| +4772|95|6|3|31|30847.79|0.02|0.04|A|F|1994-10-02|1994-10-21|1994-10-13|TAKE BACK RETURN|FOB|ests are thinly. furiously unusua| +4772|71|10|4|15|14566.05|0.02|0.07|R|F|1994-09-19|1994-10-22|1994-09-26|COLLECT COD|TRUCK| requests. express, regular th| +4773|144|5|1|23|24015.22|0.00|0.08|N|O|1996-01-01|1996-03-19|1996-01-04|NONE|FOB|ly express grouches wak| +4773|197|9|2|36|39498.84|0.09|0.04|N|O|1996-04-08|1996-03-03|1996-05-01|COLLECT COD|REG AIR| dependencies. quickly| +4773|167|8|3|49|52290.84|0.05|0.02|N|O|1996-01-26|1996-02-29|1996-01-27|TAKE BACK RETURN|FOB|y final reque| +4773|20|10|4|49|45080.98|0.09|0.04|N|O|1996-01-12|1996-02-17|1996-02-05|TAKE BACK RETURN|TRUCK|ly pending theodolites cajole caref| +4773|150|3|5|20|21003.00|0.02|0.07|N|O|1995-12-28|1996-02-17|1996-01-15|COLLECT COD|TRUCK| blithely final deposits nag after t| +4773|190|1|6|11|11992.09|0.10|0.06|N|O|1996-01-02|1996-01-29|1996-01-24|DELIVER IN PERSON|REG AIR|en accounts. slyly b| +4773|158|3|7|6|6348.90|0.07|0.01|N|O|1996-03-09|1996-03-18|1996-03-27|NONE|AIR|latelets haggle s| +4774|84|5|1|45|44283.60|0.10|0.00|R|F|1993-07-07|1993-06-08|1993-07-31|COLLECT COD|TRUCK| haggle busily afte| +4774|39|5|2|4|3756.12|0.02|0.03|A|F|1993-08-03|1993-05-30|1993-08-19|COLLECT COD|FOB|xes according to the foxes wake above the f| +4774|173|4|3|47|50438.99|0.10|0.08|R|F|1993-06-13|1993-07-04|1993-07-09|TAKE BACK RETURN|FOB|regular dolphins above the furi| +4774|130|3|4|30|30903.90|0.05|0.08|A|F|1993-08-18|1993-06-08|1993-08-21|DELIVER IN PERSON|REG AIR|tions against the blithely final theodolit| +4775|74|4|1|1|974.07|0.10|0.02|N|O|1995-09-06|1995-09-28|1995-09-29|DELIVER IN PERSON|MAIL|furiously ironic theodolite| +4775|153|1|2|37|38966.55|0.02|0.01|N|O|1995-09-06|1995-09-28|1995-09-28|COLLECT COD|TRUCK|ts. pinto beans use according to th| +4775|153|5|3|34|35807.10|0.09|0.06|N|O|1995-09-14|1995-10-15|1995-09-21|DELIVER IN PERSON|MAIL|onic epitaphs. f| +4775|119|9|4|39|39745.29|0.07|0.04|N|O|1995-08-30|1995-10-12|1995-09-20|NONE|AIR|eep never with the slyly regular acc| +4800|97|10|1|11|10967.99|0.03|0.03|R|F|1992-01-27|1992-03-16|1992-02-19|TAKE BACK RETURN|RAIL|ic dependenc| +4800|26|5|2|1|926.02|0.06|0.06|A|F|1992-02-23|1992-03-16|1992-03-20|TAKE BACK RETURN|MAIL|nal accounts are blithely deposits. bol| +4800|11|8|3|21|19131.21|0.09|0.05|A|F|1992-02-14|1992-03-15|1992-02-26|NONE|SHIP|ithely according to | +4800|176|7|4|38|40894.46|0.10|0.08|R|F|1992-02-01|1992-02-28|1992-02-21|NONE|TRUCK|s sleep fluffily. furiou| +4800|53|4|5|24|22873.20|0.08|0.04|R|F|1992-01-14|1992-02-23|1992-01-25|NONE|TRUCK|ully carefully r| +4801|184|5|1|37|40114.66|0.10|0.02|N|O|1996-03-09|1996-02-29|1996-03-25|TAKE BACK RETURN|FOB|uests hinder blithely against the instr| +4801|26|1|2|34|31484.68|0.03|0.02|N|O|1996-02-05|1996-04-16|1996-02-23|NONE|SHIP|y final requests | +4801|110|1|3|4|4040.44|0.04|0.04|N|O|1996-03-23|1996-04-04|1996-03-25|COLLECT COD|RAIL|pitaphs. regular, reg| +4801|92|3|4|39|38691.51|0.07|0.01|N|O|1996-03-19|1996-03-21|1996-04-17|TAKE BACK RETURN|REG AIR|warhorses wake never for the care| +4802|40|1|1|6|5640.24|0.00|0.06|N|O|1997-04-16|1997-03-25|1997-04-21|TAKE BACK RETURN|SHIP|unusual accounts wake blithely. b| +4803|132|3|1|2|2064.26|0.08|0.03|N|O|1996-04-16|1996-03-20|1996-05-15|NONE|REG AIR|gular reque| +4803|176|4|2|47|50579.99|0.10|0.00|N|O|1996-03-14|1996-03-30|1996-03-15|DELIVER IN PERSON|FOB|ly final excuses. slyly express requ| +4803|196|8|3|42|46039.98|0.04|0.08|N|O|1996-04-27|1996-05-05|1996-05-17|NONE|TRUCK| accounts affix quickly ar| +4803|22|1|4|24|22128.48|0.10|0.04|N|O|1996-02-24|1996-04-02|1996-02-28|NONE|MAIL|t blithely slyly special decoys. | +4803|189|10|5|21|22872.78|0.03|0.06|N|O|1996-05-25|1996-03-15|1996-06-09|COLLECT COD|FOB| silent packages use. b| +4803|194|5|6|19|20789.61|0.07|0.00|N|O|1996-04-20|1996-03-25|1996-04-27|TAKE BACK RETURN|RAIL|sts. enticing, even| +4804|128|1|1|44|45237.28|0.06|0.08|A|F|1992-05-02|1992-03-24|1992-05-28|TAKE BACK RETURN|AIR|aggle quickly among the slyly fi| +4804|35|6|2|41|38336.23|0.10|0.02|R|F|1992-04-06|1992-04-12|1992-05-03|COLLECT COD|MAIL|. deposits haggle express tithes?| +4804|65|2|3|33|31846.98|0.09|0.05|A|F|1992-03-02|1992-04-14|1992-03-13|DELIVER IN PERSON|AIR|, thin excuses. | +4805|150|1|1|7|7351.05|0.09|0.03|A|F|1992-05-01|1992-07-09|1992-05-09|NONE|FOB| requests. regular deposit| +4805|189|10|2|45|49013.10|0.02|0.03|R|F|1992-06-16|1992-06-08|1992-07-03|NONE|TRUCK|the furiously sly t| +4805|154|6|3|44|46382.60|0.01|0.02|R|F|1992-05-14|1992-06-23|1992-05-25|DELIVER IN PERSON|SHIP|eposits sleep furiously qui| +4805|65|2|4|13|12545.78|0.04|0.04|R|F|1992-07-16|1992-06-07|1992-08-10|COLLECT COD|AIR|its serve about the accounts. slyly regu| +4805|9|10|5|42|38178.00|0.03|0.03|R|F|1992-08-17|1992-07-03|1992-09-14|NONE|REG AIR|the regular, fina| +4805|136|7|6|18|18650.34|0.06|0.04|A|F|1992-06-07|1992-07-10|1992-06-12|COLLECT COD|TRUCK|o use pending, unusu| +4806|16|7|1|26|23816.26|0.10|0.05|R|F|1993-05-28|1993-06-07|1993-05-29|DELIVER IN PERSON|SHIP| bold pearls sublate blithely. quickly pe| +4806|72|10|2|6|5832.42|0.01|0.06|A|F|1993-05-17|1993-07-19|1993-05-29|TAKE BACK RETURN|SHIP|even theodolites. packages sl| +4806|29|4|3|8|7432.16|0.09|0.00|A|F|1993-05-08|1993-07-16|1993-05-28|NONE|TRUCK|requests boost blithely. qui| +4807|122|1|1|9|9199.08|0.04|0.08|N|O|1997-04-23|1997-03-01|1997-05-15|TAKE BACK RETURN|TRUCK|may are blithely. carefully even pinto b| +4807|10|1|2|41|37310.41|0.07|0.08|N|O|1997-05-02|1997-03-31|1997-05-15|TAKE BACK RETURN|AIR| fluffily re| +4807|145|6|3|34|35534.76|0.06|0.02|N|O|1997-01-31|1997-03-13|1997-02-01|NONE|SHIP|ecial ideas. deposits according to the fin| +4807|190|1|4|32|34886.08|0.05|0.00|N|O|1997-04-04|1997-03-21|1997-04-16|NONE|RAIL|efully even dolphins slee| +4807|159|1|5|2|2118.30|0.02|0.05|N|O|1997-05-09|1997-04-03|1997-06-05|TAKE BACK RETURN|RAIL|deas wake bli| +4807|160|1|6|22|23323.52|0.09|0.06|N|O|1997-03-13|1997-02-23|1997-04-01|NONE|FOB|es use final excuses. furiously final| +4832|15|6|1|23|21045.23|0.03|0.01|N|O|1997-12-05|1998-01-05|1997-12-10|NONE|RAIL|y express depo| +4832|152|4|2|10|10521.50|0.00|0.06|N|O|1998-01-08|1998-02-01|1998-01-11|DELIVER IN PERSON|MAIL|ly. blithely bold pinto beans should have| +4832|149|6|3|4|4196.56|0.04|0.01|N|O|1998-01-16|1998-02-12|1998-02-08|TAKE BACK RETURN|AIR|ages. slyly express deposits cajole car| +4832|64|5|4|6|5784.36|0.02|0.01|N|O|1997-12-08|1998-02-03|1997-12-10|COLLECT COD|TRUCK|ages cajole after the bold requests. furi| +4832|138|4|5|43|44639.59|0.10|0.08|N|O|1997-12-31|1998-02-20|1998-01-26|COLLECT COD|RAIL|oze according to the accou| +4833|107|10|1|31|31220.10|0.08|0.04|N|O|1996-06-24|1996-07-15|1996-07-02|NONE|SHIP|ven instructions cajole against the caref| +4833|117|7|2|11|11188.21|0.03|0.01|N|O|1996-08-24|1996-07-26|1996-09-19|NONE|REG AIR|s nag above the busily sile| +4833|18|9|3|26|23868.26|0.08|0.04|N|O|1996-05-13|1996-07-12|1996-05-31|NONE|SHIP|s packages. even gif| +4833|36|7|4|19|17784.57|0.07|0.07|N|O|1996-08-21|1996-07-09|1996-09-10|TAKE BACK RETURN|AIR|y quick theodolit| +4833|35|1|5|4|3740.12|0.10|0.02|N|O|1996-08-16|1996-06-29|1996-08-22|NONE|AIR|y pending packages sleep blithely regular r| +4834|183|4|1|27|29245.86|0.06|0.02|N|O|1997-01-09|1996-10-27|1997-01-27|DELIVER IN PERSON|RAIL|es nag blithe| +4834|71|1|2|26|25247.82|0.01|0.00|N|O|1996-10-04|1996-10-21|1996-10-10|DELIVER IN PERSON|TRUCK|ages dazzle carefully. slyly daring foxes| +4834|23|2|3|34|31382.68|0.03|0.01|N|O|1996-12-09|1996-11-26|1996-12-10|NONE|MAIL|ounts haggle bo| +4834|143|10|4|38|39639.32|0.03|0.06|N|O|1997-01-10|1996-12-06|1997-01-22|COLLECT COD|FOB|alongside of the carefully even plate| +4835|179|10|1|18|19425.06|0.00|0.03|R|F|1995-02-17|1994-12-14|1995-03-17|DELIVER IN PERSON|MAIL|eat furiously against the slyly | +4835|91|3|2|3|2973.27|0.09|0.06|R|F|1995-01-24|1995-01-12|1995-02-16|COLLECT COD|AIR|etimes final pac| +4835|86|7|3|27|26624.16|0.05|0.00|A|F|1994-12-10|1994-12-13|1995-01-02|DELIVER IN PERSON|REG AIR| accounts after the car| +4835|102|7|4|23|23048.30|0.08|0.07|A|F|1995-02-05|1995-01-04|1995-02-28|NONE|SHIP|e carefully regular foxes. deposits are sly| +4836|162|1|1|22|23367.52|0.01|0.03|N|O|1997-03-03|1997-02-23|1997-03-04|NONE|SHIP|al pinto beans. care| +4836|48|5|2|16|15168.64|0.07|0.08|N|O|1997-01-14|1997-03-05|1997-01-30|COLLECT COD|MAIL|gular packages against the express reque| +4836|76|4|3|14|13664.98|0.03|0.08|N|O|1997-02-21|1997-02-06|1997-03-08|COLLECT COD|MAIL|lites. unusual, bold dolphins ar| +4836|106|1|4|15|15091.50|0.10|0.00|N|O|1997-03-08|1997-03-14|1997-03-30|TAKE BACK RETURN|TRUCK|eep slyly. even requests cajole| +4836|51|6|5|12|11412.60|0.01|0.04|N|O|1997-02-02|1997-02-10|1997-02-03|COLLECT COD|TRUCK|sly ironic accoun| +4837|42|1|1|16|15072.64|0.09|0.04|N|O|1998-08-12|1998-06-06|1998-08-26|COLLECT COD|FOB|ing requests are blithely regular instructi| +4837|193|5|2|16|17491.04|0.01|0.02|N|O|1998-08-19|1998-06-18|1998-08-26|NONE|RAIL|counts cajole slyly furiou| +4837|68|5|3|42|40658.52|0.10|0.00|N|O|1998-06-19|1998-07-06|1998-06-23|COLLECT COD|MAIL|o the furiously final theodolites boost| +4838|122|3|1|35|35774.20|0.01|0.00|R|F|1992-10-30|1992-10-23|1992-11-21|TAKE BACK RETURN|RAIL|ly blithely unusual foxes. even package| +4838|148|5|2|2|2096.28|0.03|0.08|R|F|1992-08-11|1992-09-16|1992-08-26|COLLECT COD|MAIL|hely final notornis are furiously blithe| +4838|52|3|3|26|24753.30|0.06|0.04|R|F|1992-09-03|1992-10-25|1992-09-11|TAKE BACK RETURN|FOB|ular requests boost about the packages. r| +4839|60|2|1|5|4800.30|0.10|0.07|A|F|1994-09-07|1994-07-15|1994-10-05|DELIVER IN PERSON|FOB|ses integrate. regular deposits are about | +4839|10|1|2|25|22750.25|0.02|0.02|R|F|1994-05-20|1994-07-08|1994-05-30|NONE|REG AIR|regular packages ab| +4839|60|1|3|18|17281.08|0.06|0.01|R|F|1994-05-18|1994-06-13|1994-06-09|TAKE BACK RETURN|FOB|blithely ironic theodolites use along| +4839|100|1|4|19|19001.90|0.07|0.08|R|F|1994-05-20|1994-07-14|1994-05-30|NONE|REG AIR| deposits sublate furiously ir| +4839|71|10|5|9|8739.63|0.05|0.01|R|F|1994-06-17|1994-06-18|1994-07-10|NONE|SHIP|ounts haggle carefully above| +4864|150|9|1|28|29404.20|0.06|0.08|A|F|1993-02-06|1992-12-15|1993-02-10|COLLECT COD|REG AIR|thely around the bli| +4864|38|4|2|38|35645.14|0.10|0.02|R|F|1992-12-20|1993-01-07|1993-01-06|TAKE BACK RETURN|SHIP|ording to the ironic, ir| +4864|133|4|3|45|46490.85|0.02|0.01|A|F|1992-11-17|1993-01-02|1992-11-26|COLLECT COD|SHIP|round the furiously careful pa| +4864|31|2|4|46|42827.38|0.07|0.03|A|F|1993-02-24|1993-01-02|1993-03-17|TAKE BACK RETURN|RAIL|sts use carefully across the carefull| +4865|162|7|1|16|16994.56|0.07|0.05|N|O|1997-10-02|1997-08-20|1997-10-04|COLLECT COD|TRUCK|osits haggle. fur| +4865|137|8|2|4|4148.52|0.07|0.01|N|O|1997-07-24|1997-07-25|1997-08-07|TAKE BACK RETURN|FOB|sts. blithely special instruction| +4865|68|3|3|44|42594.64|0.10|0.08|N|O|1997-07-25|1997-08-20|1997-08-22|COLLECT COD|FOB|even deposits sleep against the quickly r| +4865|50|3|4|21|19951.05|0.04|0.02|N|O|1997-07-17|1997-08-10|1997-07-21|NONE|RAIL|eposits detect sly| +4865|54|9|5|33|31483.65|0.00|0.05|N|O|1997-07-17|1997-08-16|1997-07-30|TAKE BACK RETURN|FOB|y pending notornis ab| +4865|65|2|6|47|45357.82|0.00|0.05|N|O|1997-08-26|1997-08-07|1997-08-31|NONE|RAIL|y unusual packages. packages| +4866|11|8|1|9|8199.09|0.01|0.05|N|O|1997-08-30|1997-09-18|1997-09-24|TAKE BACK RETURN|MAIL|ven dependencies x-ray. quic| +4866|102|3|2|1|1002.10|0.06|0.00|N|O|1997-10-15|1997-10-01|1997-11-14|TAKE BACK RETURN|AIR|latelets nag. q| +4866|131|7|3|17|17529.21|0.07|0.00|N|O|1997-11-26|1997-10-11|1997-12-12|COLLECT COD|TRUCK|ess packages doubt. even somas wake f| +4867|82|3|1|7|6874.56|0.09|0.03|A|F|1992-07-17|1992-08-17|1992-07-22|COLLECT COD|FOB|e carefully even packages. slyly ironic i| +4867|160|8|2|3|3180.48|0.04|0.08|R|F|1992-07-04|1992-07-15|1992-07-21|NONE|AIR|yly silent deposits| +4868|73|3|1|47|45734.29|0.03|0.03|N|O|1997-04-29|1997-04-27|1997-05-11|DELIVER IN PERSON|SHIP|gle unusual, fluffy packages. foxes cajol| +4868|180|1|2|8|8641.44|0.10|0.08|N|O|1997-03-26|1997-05-09|1997-04-16|NONE|RAIL|ly special th| +4868|191|2|3|49|53468.31|0.09|0.03|N|O|1997-04-23|1997-05-07|1997-04-26|NONE|SHIP|ys engage. th| +4868|80|1|4|34|33322.72|0.04|0.02|N|O|1997-05-19|1997-04-27|1997-06-15|NONE|RAIL|en instructions about th| +4868|122|3|5|22|22486.64|0.07|0.06|N|O|1997-04-26|1997-05-16|1997-05-01|DELIVER IN PERSON|FOB|osits. final foxes boost regular,| +4869|41|8|1|31|29172.24|0.10|0.01|A|F|1995-01-17|1994-11-30|1995-02-02|NONE|SHIP|ins. always unusual ideas across the ir| +4869|58|3|2|24|22993.20|0.09|0.06|A|F|1994-11-17|1994-11-07|1994-11-27|COLLECT COD|MAIL|olites cajole after the ideas. special t| +4869|157|8|3|25|26428.75|0.00|0.05|R|F|1994-11-25|1994-11-14|1994-12-19|DELIVER IN PERSON|AIR|e according t| +4869|103|8|4|24|24074.40|0.10|0.07|R|F|1994-11-23|1994-11-18|1994-12-11|DELIVER IN PERSON|MAIL|se deposits above the sly, q| +4869|173|2|5|42|45073.14|0.07|0.04|R|F|1994-10-16|1994-12-10|1994-11-07|TAKE BACK RETURN|REG AIR| slyly even instructions. | +4869|122|5|6|30|30663.60|0.00|0.05|A|F|1995-01-09|1994-11-20|1995-02-02|COLLECT COD|RAIL|gedly even requests. s| +4870|48|5|1|49|46453.96|0.05|0.05|R|F|1994-11-14|1994-10-24|1994-12-12|TAKE BACK RETURN|SHIP| regular packages | +4870|127|10|2|6|6162.72|0.06|0.08|A|F|1994-09-09|1994-10-16|1994-09-21|DELIVER IN PERSON|TRUCK|ress requests. bold, silent pinto bea| +4870|31|2|3|5|4655.15|0.05|0.00|R|F|1994-10-11|1994-10-07|1994-10-24|NONE|AIR|s haggle furiously. slyly ironic dinos| +4870|6|9|4|4|3624.00|0.03|0.08|A|F|1994-10-23|1994-09-16|1994-11-04|COLLECT COD|RAIL|its wake quickly. slyly quick| +4870|71|1|5|36|34958.52|0.09|0.06|A|F|1994-09-06|1994-09-17|1994-10-01|COLLECT COD|REG AIR| instructions. carefully pending pac| +4871|177|5|1|14|15080.38|0.07|0.03|N|O|1995-09-30|1995-07-29|1995-10-18|TAKE BACK RETURN|REG AIR|inst the never ironic | +4871|161|6|2|17|18039.72|0.07|0.03|N|O|1995-09-09|1995-09-01|1995-10-02|DELIVER IN PERSON|AIR|es. carefully ev| +4871|63|4|3|3|2889.18|0.03|0.06|N|O|1995-10-03|1995-08-10|1995-10-06|DELIVER IN PERSON|TRUCK|y special packages wak| +4871|149|8|4|35|36719.90|0.08|0.07|N|O|1995-08-11|1995-07-18|1995-08-29|DELIVER IN PERSON|TRUCK|ackages sle| +4871|152|3|5|10|10521.50|0.09|0.02|N|O|1995-09-12|1995-09-02|1995-10-05|TAKE BACK RETURN|AIR|s integrate after the a| +4871|136|2|6|36|37300.68|0.02|0.08|N|O|1995-09-18|1995-08-29|1995-10-05|TAKE BACK RETURN|AIR|ely according| +4871|140|6|7|10|10401.40|0.10|0.02|N|O|1995-07-13|1995-08-19|1995-07-29|NONE|REG AIR|p ironic theodolites. slyly even platel| +4896|41|2|1|19|17879.76|0.09|0.05|A|F|1992-12-13|1992-11-13|1993-01-09|NONE|AIR|nusual requ| +4896|140|1|2|44|45766.16|0.04|0.03|A|F|1992-11-24|1992-11-15|1992-12-18|COLLECT COD|MAIL|e after the slowly f| +4896|58|10|3|6|5748.30|0.04|0.04|A|F|1992-10-30|1992-11-12|1992-11-28|DELIVER IN PERSON|TRUCK|usly regular deposits| +4896|23|4|4|5|4615.10|0.08|0.02|R|F|1992-12-02|1992-11-11|1992-12-19|COLLECT COD|SHIP|eposits hang carefully. sly| +4896|86|7|5|21|20707.68|0.07|0.08|R|F|1992-11-18|1992-11-18|1992-11-29|DELIVER IN PERSON|TRUCK|ly express deposits. carefully pending depo| +4897|55|6|1|26|24831.30|0.01|0.01|R|F|1992-12-22|1992-10-25|1992-12-27|DELIVER IN PERSON|TRUCK|. carefully ironic dep| +4897|143|6|2|34|35466.76|0.02|0.00|R|F|1992-12-31|1992-11-11|1993-01-30|COLLECT COD|AIR|ts. special dependencies use fluffily | +4897|55|7|3|42|40112.10|0.09|0.03|A|F|1992-09-23|1992-10-28|1992-10-02|DELIVER IN PERSON|FOB|sts. blithely regular deposits will have| +4897|104|5|4|19|19077.90|0.03|0.00|A|F|1992-11-08|1992-12-14|1992-12-03|DELIVER IN PERSON|FOB|! ironic, pending dependencies doze furiou| +4898|72|1|1|44|42771.08|0.07|0.02|A|F|1994-09-13|1994-08-18|1994-09-16|NONE|FOB|y regular grouches about| +4899|34|10|1|14|13076.42|0.06|0.00|R|F|1993-11-10|1994-01-10|1993-11-20|NONE|REG AIR| foxes eat| +4900|116|3|1|40|40644.40|0.10|0.03|A|F|1992-09-02|1992-09-25|1992-09-21|COLLECT COD|TRUCK|heodolites. request| +4900|77|8|2|33|32243.31|0.06|0.06|R|F|1992-08-18|1992-09-20|1992-08-19|COLLECT COD|MAIL|nto beans nag slyly reg| +4900|103|8|3|48|48148.80|0.02|0.00|R|F|1992-09-18|1992-08-14|1992-09-28|TAKE BACK RETURN|MAIL|uickly ironic ideas kindle s| +4900|32|3|4|20|18640.60|0.05|0.00|R|F|1992-09-22|1992-09-23|1992-09-27|TAKE BACK RETURN|MAIL|yers. accounts affix somet| +4900|105|8|5|40|40204.00|0.03|0.02|R|F|1992-07-14|1992-09-05|1992-07-20|NONE|REG AIR|luffily final dol| +4900|103|6|6|46|46142.60|0.06|0.08|R|F|1992-07-11|1992-09-19|1992-07-16|TAKE BACK RETURN|SHIP|ly final acco| +4901|141|10|1|37|38522.18|0.00|0.04|N|O|1998-01-26|1998-02-20|1998-01-31|DELIVER IN PERSON|TRUCK| furiously ev| +4901|165|4|2|12|12781.92|0.00|0.04|N|O|1998-01-12|1998-02-06|1998-02-03|COLLECT COD|REG AIR|y unusual deposits prom| +4901|120|4|3|16|16321.92|0.05|0.08|N|O|1998-04-19|1998-03-18|1998-04-21|NONE|AIR|deposits. blithely fin| +4901|36|7|4|41|38377.23|0.03|0.00|N|O|1998-03-18|1998-02-18|1998-04-14|TAKE BACK RETURN|AIR|efully bold packages affix carefully eve| +4901|116|7|5|40|40644.40|0.06|0.02|N|O|1998-01-08|1998-01-30|1998-01-15|DELIVER IN PERSON|MAIL|ect across the furiou| +4902|196|10|1|22|24116.18|0.00|0.04|N|O|1998-10-17|1998-08-10|1998-10-21|COLLECT COD|RAIL|r the furiously final fox| +4902|83|4|2|1|983.08|0.09|0.04|N|O|1998-10-12|1998-08-20|1998-11-08|NONE|RAIL|daring foxes? even, bold requests wake f| +4903|121|2|1|1|1021.12|0.06|0.03|R|F|1992-04-23|1992-06-13|1992-05-03|NONE|SHIP|nusual requests| +4903|165|6|2|6|6390.96|0.09|0.07|R|F|1992-04-01|1992-05-16|1992-04-11|DELIVER IN PERSON|SHIP|azzle quickly along the blithely final pla| +4903|120|10|3|27|27543.24|0.07|0.06|A|F|1992-06-29|1992-06-09|1992-07-08|COLLECT COD|RAIL|pinto beans are; | +4928|100|1|1|4|4000.40|0.04|0.02|R|F|1993-10-25|1993-12-24|1993-11-16|TAKE BACK RETURN|REG AIR|bout the slyly final accounts. carefull| +4928|93|4|2|20|19861.80|0.03|0.08|A|F|1994-01-19|1993-11-29|1994-02-13|DELIVER IN PERSON|SHIP|quiet theodolites ca| +4928|149|8|3|34|35670.76|0.06|0.05|A|F|1993-10-12|1993-12-31|1993-10-14|DELIVER IN PERSON|AIR|, regular depos| +4929|14|1|1|20|18280.20|0.00|0.04|N|O|1996-03-12|1996-05-23|1996-03-20|COLLECT COD|REG AIR| final pinto beans detect. final,| +4929|79|7|2|40|39162.80|0.08|0.03|N|O|1996-05-30|1996-04-13|1996-06-22|TAKE BACK RETURN|AIR|unts against | +4929|77|7|3|32|31266.24|0.08|0.02|N|O|1996-04-28|1996-05-23|1996-04-30|COLLECT COD|TRUCK|usly at the blithely pending pl| +4929|109|4|4|26|26236.60|0.00|0.05|N|O|1996-06-10|1996-05-29|1996-06-26|DELIVER IN PERSON|RAIL| slyly. fl| +4929|67|8|5|24|23209.44|0.09|0.05|N|O|1996-04-15|1996-04-30|1996-05-09|NONE|MAIL| accounts boost| +4930|187|8|1|35|38051.30|0.03|0.01|A|F|1994-07-09|1994-07-30|1994-07-15|NONE|RAIL|lose slyly regular dependencies. fur| +4930|115|5|2|20|20302.20|0.02|0.04|A|F|1994-08-21|1994-06-17|1994-08-24|COLLECT COD|FOB|he carefully| +4930|168|7|3|28|29908.48|0.00|0.08|R|F|1994-08-27|1994-06-27|1994-09-18|COLLECT COD|TRUCK|e ironic, unusual courts. regula| +4930|166|7|4|42|44778.72|0.00|0.00|A|F|1994-06-18|1994-06-22|1994-07-10|COLLECT COD|AIR|ions haggle. furiously regular ideas use | +4930|190|1|5|38|41427.22|0.02|0.03|A|F|1994-06-06|1994-06-18|1994-07-03|TAKE BACK RETURN|AIR|bold requests sleep never| +4931|194|7|1|1|1094.19|0.08|0.06|A|F|1995-01-24|1994-12-19|1995-02-07|DELIVER IN PERSON|SHIP| furiously | +4931|151|3|2|8|8409.20|0.06|0.02|R|F|1994-12-15|1995-01-14|1995-01-06|NONE|SHIP|ts boost. packages wake sly| +4931|144|5|3|20|20882.80|0.09|0.00|A|F|1995-01-25|1994-12-21|1995-02-06|DELIVER IN PERSON|MAIL|the furious| +4931|200|4|4|50|55010.00|0.04|0.01|A|F|1994-12-15|1994-12-18|1994-12-23|COLLECT COD|REG AIR|s haggle al| +4931|150|7|5|25|26253.75|0.05|0.05|R|F|1994-12-19|1995-01-05|1994-12-21|COLLECT COD|FOB|aggle bravely according to the quic| +4931|103|6|6|8|8024.80|0.02|0.03|A|F|1995-02-16|1994-12-30|1995-03-15|DELIVER IN PERSON|SHIP|dependencies are slyly| +4932|51|3|1|13|12363.65|0.04|0.03|A|F|1993-09-13|1993-10-16|1993-09-20|DELIVER IN PERSON|SHIP|slyly according to the furiously fin| +4932|103|10|2|15|15046.50|0.01|0.02|R|F|1993-11-15|1993-10-25|1993-11-29|NONE|REG AIR|yly. unusu| +4932|87|8|3|5|4935.40|0.06|0.06|A|F|1993-10-01|1993-09-13|1993-10-04|NONE|MAIL| haggle furiously. slyly ironic packages sl| +4932|98|1|4|11|10978.99|0.09|0.06|A|F|1993-09-21|1993-09-30|1993-09-23|COLLECT COD|SHIP|as. special depende| +4933|32|8|1|48|44737.44|0.08|0.00|N|O|1995-10-10|1995-10-03|1995-11-04|COLLECT COD|SHIP|ideas. sly| +4933|82|3|2|2|1964.16|0.09|0.00|N|O|1995-10-01|1995-09-29|1995-10-19|DELIVER IN PERSON|MAIL|ctions nag final instructions. accou| +4934|97|10|1|48|47860.32|0.00|0.01|N|O|1997-05-20|1997-04-22|1997-06-02|TAKE BACK RETURN|SHIP| ideas cajol| +4934|110|1|2|41|41414.51|0.06|0.06|N|O|1997-06-04|1997-04-11|1997-06-25|TAKE BACK RETURN|FOB|wake final, ironic f| +4934|140|1|3|8|8321.12|0.03|0.06|N|O|1997-05-20|1997-04-30|1997-05-27|TAKE BACK RETURN|MAIL|arefully express pains cajo| +4934|148|5|4|9|9433.26|0.06|0.08|N|O|1997-06-10|1997-04-09|1997-06-12|TAKE BACK RETURN|REG AIR| haggle alongside of the| +4934|138|9|5|29|30105.77|0.09|0.03|N|O|1997-04-10|1997-05-05|1997-05-04|DELIVER IN PERSON|AIR|aggle furiously among the busily final re| +4934|52|3|6|42|39986.10|0.00|0.07|N|O|1997-03-19|1997-05-05|1997-03-25|NONE|MAIL|ven, ironic ideas| +4934|11|5|7|2|1822.02|0.10|0.06|N|O|1997-06-05|1997-03-26|1997-06-09|COLLECT COD|MAIL|ongside of the brave, regula| +4935|161|2|1|13|13795.08|0.09|0.01|A|F|1993-06-20|1993-08-13|1993-06-27|COLLECT COD|REG AIR|ly requests. final deposits might | +4935|40|6|2|37|34781.48|0.01|0.05|R|F|1993-08-30|1993-07-23|1993-09-07|TAKE BACK RETURN|RAIL|y even dependencies nag a| +4935|11|8|3|24|21864.24|0.06|0.04|A|F|1993-05-29|1993-08-17|1993-06-22|NONE|RAIL|ly quickly s| +4935|45|6|4|49|46306.96|0.06|0.01|A|F|1993-09-16|1993-08-21|1993-10-12|COLLECT COD|TRUCK|ffily after the furiou| +4935|10|1|5|14|12740.14|0.08|0.08|A|F|1993-05-30|1993-07-25|1993-05-31|COLLECT COD|FOB|slowly. blith| +4935|188|9|6|36|39174.48|0.10|0.00|R|F|1993-07-11|1993-07-04|1993-08-01|DELIVER IN PERSON|RAIL|requests across the quick| +4960|18|5|1|36|33048.36|0.01|0.05|R|F|1995-03-06|1995-05-04|1995-04-05|TAKE BACK RETURN|RAIL|c, unusual accou| +4960|45|8|2|6|5670.24|0.03|0.08|R|F|1995-03-21|1995-05-13|1995-04-14|TAKE BACK RETURN|SHIP|ual package| +4960|149|8|3|9|9442.26|0.01|0.03|A|F|1995-03-20|1995-05-05|1995-04-17|COLLECT COD|RAIL|e blithely carefully fina| +4960|120|7|4|14|14281.68|0.00|0.06|A|F|1995-04-03|1995-04-17|1995-04-07|NONE|RAIL|accounts. warhorses are. grouches | +4960|98|1|5|8|7984.72|0.07|0.04|R|F|1995-03-14|1995-04-18|1995-04-09|NONE|FOB|as. busily regular packages nag. | +4960|146|7|6|37|38707.18|0.10|0.04|R|F|1995-05-23|1995-04-12|1995-06-01|DELIVER IN PERSON|MAIL|ending theodolites w| +4960|170|1|7|42|44947.14|0.08|0.07|A|F|1995-04-19|1995-04-11|1995-05-08|NONE|SHIP|s requests cajole. | +4961|44|7|1|38|35873.52|0.10|0.07|N|O|1998-07-09|1998-06-03|1998-07-11|TAKE BACK RETURN|FOB|e on the blithely bold accounts. unu| +4961|60|5|2|1|960.06|0.08|0.08|N|O|1998-07-08|1998-05-25|1998-07-12|DELIVER IN PERSON|MAIL|s affix carefully silent dependen| +4961|162|3|3|41|43548.56|0.02|0.02|N|O|1998-07-15|1998-06-15|1998-08-05|TAKE BACK RETURN|REG AIR|ily against the n| +4961|100|3|4|10|10001.00|0.02|0.04|N|O|1998-04-15|1998-07-03|1998-04-18|DELIVER IN PERSON|MAIL|quests. regular, ironic ideas at the ironi| +4962|19|6|1|46|42274.46|0.01|0.07|R|F|1993-08-23|1993-09-04|1993-08-27|COLLECT COD|REG AIR| pinto beans grow about the sl| +4963|168|5|1|38|40590.08|0.08|0.02|N|O|1996-12-25|1996-12-12|1997-01-02|COLLECT COD|AIR|tegrate daringly accou| +4963|76|4|2|16|15617.12|0.00|0.03|N|O|1996-11-20|1997-01-13|1996-12-06|COLLECT COD|MAIL| carefully slyly u| +4964|133|9|1|29|29960.77|0.04|0.01|N|O|1997-10-18|1997-08-30|1997-11-01|NONE|AIR|k accounts nag carefully-- ironic, fin| +4964|148|5|2|46|48214.44|0.06|0.06|N|O|1997-10-05|1997-09-12|1997-10-11|NONE|TRUCK|althy deposits| +4964|143|4|3|18|18776.52|0.00|0.06|N|O|1997-10-13|1997-09-01|1997-11-10|DELIVER IN PERSON|AIR| platelets. furio| +4964|180|10|4|12|12962.16|0.08|0.01|N|O|1997-09-03|1997-10-25|1997-09-15|NONE|TRUCK|ully silent instructions ca| +4964|41|10|5|42|39523.68|0.06|0.04|N|O|1997-09-04|1997-08-28|1997-10-02|TAKE BACK RETURN|AIR| hinder. idly even| +4964|193|7|6|22|24050.18|0.04|0.08|N|O|1997-09-11|1997-10-06|1997-09-29|NONE|AIR|equests doubt quickly. caref| +4964|173|4|7|28|30048.76|0.00|0.05|N|O|1997-08-30|1997-09-15|1997-09-18|COLLECT COD|RAIL|among the carefully regula| +4965|131|2|1|28|28871.64|0.05|0.03|A|F|1994-01-02|1993-11-20|1994-01-04|TAKE BACK RETURN|REG AIR| deposits. requests sublate quickly | +4965|13|10|2|25|22825.25|0.10|0.02|R|F|1994-02-05|1993-12-15|1994-02-24|TAKE BACK RETURN|MAIL|wake at the carefully speci| +4965|101|8|3|27|27029.70|0.05|0.06|R|F|1993-11-06|1993-12-24|1993-11-30|TAKE BACK RETURN|SHIP|efully final foxes| +4965|138|9|4|33|34258.29|0.04|0.04|A|F|1993-12-31|1993-11-29|1994-01-27|DELIVER IN PERSON|REG AIR|iously slyly| +4966|76|6|1|10|9760.70|0.06|0.03|N|O|1996-09-23|1996-11-02|1996-10-07|TAKE BACK RETURN|SHIP| requests. carefully pending requests| +4966|194|6|2|6|6565.14|0.02|0.01|N|O|1996-12-09|1996-11-29|1996-12-30|NONE|AIR|d deposits are sly excuses. slyly iro| +4966|165|6|3|7|7456.12|0.00|0.01|N|O|1996-12-08|1996-10-09|1997-01-06|COLLECT COD|MAIL|ckly ironic tithe| +4966|16|6|4|26|23816.26|0.08|0.03|N|O|1996-11-14|1996-11-29|1996-12-05|COLLECT COD|REG AIR|nt pearls haggle carefully slyly even | +4966|144|1|5|12|12529.68|0.02|0.07|N|O|1996-12-07|1996-11-23|1996-12-20|DELIVER IN PERSON|RAIL|eodolites. ironic requests across the exp| +4967|71|1|1|50|48553.50|0.07|0.01|N|O|1997-05-27|1997-05-13|1997-06-12|NONE|REG AIR|kages. final, unusual accounts c| +4967|53|5|2|43|40981.15|0.00|0.07|N|O|1997-05-28|1997-04-10|1997-06-09|NONE|TRUCK|ons. slyly ironic requests| +4967|50|1|3|15|14250.75|0.08|0.02|N|O|1997-04-16|1997-04-12|1997-05-08|TAKE BACK RETURN|MAIL|y. blithel| +4967|123|2|4|1|1023.12|0.10|0.07|N|O|1997-06-04|1997-03-29|1997-06-23|NONE|FOB|osits. unusual frets thrash furiously| +4992|184|5|1|42|45535.56|0.07|0.01|R|F|1992-07-19|1992-06-16|1992-08-17|TAKE BACK RETURN|RAIL|foxes about the quickly final platele| +4992|147|4|2|47|49215.58|0.10|0.08|A|F|1992-09-04|1992-08-05|1992-09-21|COLLECT COD|MAIL|atterns use fluffily.| +4992|144|7|3|17|17750.38|0.03|0.03|A|F|1992-07-05|1992-07-19|1992-07-30|TAKE BACK RETURN|FOB|s along the perma| +4992|70|7|4|25|24251.75|0.04|0.06|R|F|1992-08-06|1992-07-11|1992-08-20|NONE|SHIP|ly about the never ironic requests. pe| +4992|139|5|5|23|23899.99|0.01|0.08|R|F|1992-06-28|1992-07-15|1992-07-12|DELIVER IN PERSON|MAIL|uickly regul| +4992|163|8|6|44|46779.04|0.05|0.02|A|F|1992-06-01|1992-07-22|1992-06-03|NONE|RAIL|rmanent, sly packages print slyly. regula| +4993|38|4|1|34|31893.02|0.05|0.00|R|F|1994-09-21|1994-10-31|1994-09-24|TAKE BACK RETURN|REG AIR|ular, pending packages at the even packa| +4993|129|4|2|39|40135.68|0.03|0.08|R|F|1994-09-10|1994-09-04|1994-09-26|COLLECT COD|SHIP|pending, regular requests solve caref| +4993|166|1|3|42|44778.72|0.06|0.00|A|F|1994-08-27|1994-09-24|1994-09-05|NONE|MAIL| final packages at the q| +4993|158|6|4|31|32802.65|0.10|0.06|A|F|1994-10-02|1994-10-29|1994-10-15|NONE|AIR|nwind thinly platelets. a| +4994|156|8|1|36|38021.40|0.00|0.06|N|O|1996-09-29|1996-07-30|1996-10-03|TAKE BACK RETURN|TRUCK|ess ideas. blithely silent brai| +4994|80|9|2|47|46063.76|0.04|0.05|N|O|1996-09-20|1996-08-04|1996-10-15|COLLECT COD|TRUCK|sts. blithely close ideas sleep quic| +4994|183|4|3|29|31412.22|0.08|0.01|N|O|1996-08-26|1996-09-27|1996-09-25|DELIVER IN PERSON|RAIL|ptotes boost carefully| +4994|39|10|4|40|37561.20|0.01|0.06|N|O|1996-08-25|1996-08-16|1996-09-07|TAKE BACK RETURN|REG AIR|eposits. regula| +4994|42|9|5|24|22608.96|0.01|0.07|N|O|1996-08-19|1996-09-24|1996-08-25|TAKE BACK RETURN|FOB|s. slyly ironic deposits cajole f| +4994|73|4|6|6|5838.42|0.01|0.02|N|O|1996-09-05|1996-08-04|1996-09-30|TAKE BACK RETURN|FOB|grate carefully around th| +4994|130|1|7|31|31934.03|0.07|0.04|N|O|1996-10-14|1996-09-23|1996-11-08|TAKE BACK RETURN|RAIL|lar decoys cajole fluffil| +4995|65|4|1|16|15440.96|0.02|0.05|N|O|1996-02-27|1996-04-03|1996-02-29|DELIVER IN PERSON|MAIL|egular, bold packages. accou| +4995|81|2|2|43|42186.44|0.00|0.06|N|O|1996-02-24|1996-02-20|1996-03-07|NONE|AIR|ts. blithely silent ideas after t| +4995|156|7|3|22|23235.30|0.03|0.06|N|O|1996-03-17|1996-03-12|1996-04-01|DELIVER IN PERSON|MAIL|s wake furious, express dependencies.| +4995|40|1|4|9|8460.36|0.07|0.07|N|O|1996-03-07|1996-03-17|1996-03-11|DELIVER IN PERSON|FOB| ironic packages cajole across t| +4995|148|7|5|48|50310.72|0.08|0.07|N|O|1996-03-22|1996-04-01|1996-04-07|NONE|SHIP|t blithely. requests affix blithely. | +4995|110|5|6|48|48485.28|0.09|0.07|N|O|1996-04-14|1996-04-04|1996-05-07|DELIVER IN PERSON|RAIL|nstructions. carefully final depos| +4996|56|1|1|35|33461.75|0.07|0.01|A|F|1992-10-30|1992-10-27|1992-11-05|TAKE BACK RETURN|SHIP|s. unusual, regular dolphins integrate care| +4996|156|7|2|39|41189.85|0.02|0.07|A|F|1992-09-19|1992-10-19|1992-10-06|COLLECT COD|FOB|equests are carefully final| +4996|128|7|3|12|12337.44|0.04|0.06|R|F|1993-01-09|1992-11-22|1993-02-04|DELIVER IN PERSON|SHIP|usly bold requests sleep dogge| +4996|144|3|4|13|13573.82|0.00|0.00|A|F|1992-09-17|1992-12-02|1992-10-07|DELIVER IN PERSON|TRUCK|o beans use about the furious| +4997|79|7|1|44|43079.08|0.02|0.05|N|O|1998-06-09|1998-06-12|1998-07-07|NONE|RAIL|r escapades ca| +4997|17|7|2|5|4585.05|0.02|0.04|N|O|1998-05-16|1998-06-05|1998-06-07|COLLECT COD|REG AIR|cuses are furiously unusual asymptotes| +4997|58|9|3|24|22993.20|0.04|0.06|N|O|1998-04-20|1998-04-23|1998-05-16|NONE|AIR|xpress, bo| +4997|40|6|4|5|4700.20|0.10|0.03|N|O|1998-06-12|1998-04-24|1998-06-13|DELIVER IN PERSON|TRUCK|aggle slyly alongside of the slyly i| +4997|22|7|5|46|42412.92|0.00|0.04|N|O|1998-04-28|1998-06-04|1998-05-08|TAKE BACK RETURN|SHIP|ecial courts are carefully| +4997|29|2|6|2|1858.04|0.07|0.01|N|O|1998-07-09|1998-06-10|1998-07-21|TAKE BACK RETURN|REG AIR|counts. slyl| +4998|154|2|1|12|12649.80|0.04|0.03|A|F|1992-02-20|1992-03-06|1992-03-01|TAKE BACK RETURN|RAIL| sleep slyly furiously final accounts. ins| +4998|183|4|2|15|16247.70|0.06|0.00|R|F|1992-04-24|1992-03-21|1992-05-02|NONE|REG AIR|heodolites sleep quickly.| +4998|59|10|3|27|25894.35|0.06|0.02|R|F|1992-03-17|1992-02-26|1992-04-05|DELIVER IN PERSON|MAIL|the blithely ironic | +4998|63|10|4|47|45263.82|0.10|0.04|A|F|1992-02-07|1992-03-07|1992-02-19|DELIVER IN PERSON|TRUCK|mong the careful| +4998|145|4|5|24|25083.36|0.01|0.04|R|F|1992-01-25|1992-03-16|1992-01-27|COLLECT COD|REG AIR| unwind about| +4998|99|1|6|8|7992.72|0.03|0.07|A|F|1992-05-01|1992-03-03|1992-05-24|TAKE BACK RETURN|AIR|ions nag quickly according to the theodolit| +4999|153|8|1|30|31594.50|0.00|0.02|A|F|1993-08-20|1993-08-15|1993-08-30|NONE|AIR|ades cajole carefully unusual ide| +4999|10|1|2|44|40040.44|0.03|0.01|A|F|1993-08-01|1993-08-04|1993-08-17|COLLECT COD|REG AIR|ependencies. slowly regu| +4999|86|7|3|30|29582.40|0.09|0.01|R|F|1993-07-21|1993-08-11|1993-08-20|DELIVER IN PERSON|RAIL|s cajole among the blithel| +5024|166|3|1|17|18124.72|0.10|0.02|N|O|1996-11-24|1997-01-10|1996-12-04|NONE|AIR| to the expre| +5024|58|6|2|41|39280.05|0.06|0.01|N|O|1996-11-09|1996-12-03|1996-12-01|COLLECT COD|REG AIR|osits hinder carefully | +5024|112|6|3|18|18217.98|0.04|0.03|N|O|1996-12-02|1997-01-16|1996-12-05|NONE|MAIL|zle carefully sauternes. quickly| +5024|123|8|4|42|42971.04|0.03|0.06|N|O|1996-12-02|1996-12-08|1996-12-04|DELIVER IN PERSON|RAIL|tegrate. busily spec| +5025|30|9|1|11|10230.33|0.00|0.04|N|O|1997-02-21|1997-04-16|1997-03-14|COLLECT COD|SHIP|the carefully final esc| +5025|78|7|2|10|9780.70|0.07|0.04|N|O|1997-06-04|1997-04-29|1997-06-28|COLLECT COD|RAIL|lly silent deposits boost busily again| +5026|96|8|1|13|12949.17|0.02|0.04|N|O|1997-12-23|1997-11-02|1998-01-03|TAKE BACK RETURN|SHIP|endencies sleep carefully alongs| +5027|98|2|1|6|5988.54|0.04|0.05|N|O|1997-09-28|1997-11-24|1997-10-25|NONE|FOB|ar, ironic deposi| +5027|62|3|2|39|37520.34|0.06|0.01|N|O|1997-09-09|1997-11-13|1997-09-21|TAKE BACK RETURN|FOB|ess requests! quickly regular pac| +5027|126|5|3|32|32835.84|0.00|0.01|N|O|1997-11-13|1997-10-29|1997-11-18|TAKE BACK RETURN|RAIL|cording to| +5027|26|7|4|37|34262.74|0.02|0.00|N|O|1997-10-05|1997-10-30|1997-10-26|NONE|REG AIR|ost slyly fluffily| +5027|143|4|5|3|3129.42|0.03|0.06|N|O|1997-09-30|1997-11-26|1997-10-05|DELIVER IN PERSON|AIR|t the even mu| +5027|87|8|6|25|24677.00|0.06|0.00|N|O|1997-09-16|1997-11-25|1997-10-08|TAKE BACK RETURN|RAIL|ic ideas. requests sleep fluffily am| +5027|81|2|7|50|49054.00|0.07|0.02|N|O|1997-09-18|1997-11-07|1997-10-05|DELIVER IN PERSON|MAIL| beans dazzle according to the fluffi| +5028|14|1|1|15|13710.15|0.07|0.07|R|F|1992-07-17|1992-07-16|1992-08-05|COLLECT COD|REG AIR|es are quickly final pains. furiously pend| +5028|199|10|2|15|16487.85|0.03|0.07|R|F|1992-08-02|1992-07-09|1992-08-30|NONE|REG AIR|gular, bold pinto bea| +5029|154|5|1|17|17920.55|0.02|0.01|A|F|1993-03-12|1992-12-18|1993-04-02|DELIVER IN PERSON|FOB|! packages boost blithely. furious| +5029|97|9|2|2|1994.18|0.00|0.04|A|F|1992-11-25|1993-01-04|1992-12-20|DELIVER IN PERSON|MAIL|packages. furiously ironi| +5030|102|3|1|22|22046.20|0.04|0.06|N|O|1998-09-01|1998-08-15|1998-09-30|TAKE BACK RETURN|TRUCK|. quickly regular foxes believe| +5030|80|9|2|50|49004.00|0.05|0.06|N|O|1998-08-22|1998-07-25|1998-09-18|TAKE BACK RETURN|FOB|ss excuses serve bli| +5031|50|1|1|15|14250.75|0.02|0.05|R|F|1995-04-01|1995-02-24|1995-04-12|DELIVER IN PERSON|AIR|yly pending theodolites.| +5031|161|6|2|40|42446.40|0.10|0.04|A|F|1994-12-04|1995-01-27|1995-01-01|NONE|TRUCK|ns hang blithely across th| +5031|154|6|3|4|4216.60|0.01|0.07|R|F|1994-12-26|1995-02-24|1995-01-11|NONE|RAIL|after the even frays: ironic, unusual th| +5031|181|2|4|31|33516.58|0.10|0.08|R|F|1995-01-15|1995-01-08|1995-02-09|COLLECT COD|MAIL|ts across the even requests doze furiously| +5056|48|7|1|7|6636.28|0.09|0.01|N|O|1997-04-28|1997-04-07|1997-05-15|DELIVER IN PERSON|TRUCK|rouches after the pending instruc| +5056|197|1|2|19|20846.61|0.04|0.00|N|O|1997-03-24|1997-05-05|1997-04-23|DELIVER IN PERSON|AIR|c theodolites. ironic a| +5056|90|1|3|23|22772.07|0.02|0.05|N|O|1997-05-12|1997-04-28|1997-05-25|NONE|SHIP|ickly regular requests cajole. depos| +5056|87|8|4|14|13819.12|0.08|0.00|N|O|1997-06-09|1997-04-13|1997-07-06|COLLECT COD|SHIP|sts haggle carefully along the slyl| +5057|37|3|1|38|35607.14|0.02|0.03|N|O|1997-10-24|1997-09-07|1997-10-30|TAKE BACK RETURN|MAIL|packages. stealthily bold wa| +5057|8|1|2|45|40860.00|0.08|0.07|N|O|1997-09-20|1997-10-02|1997-10-20|NONE|FOB| asymptotes wake slyl| +5058|193|5|1|16|17491.04|0.09|0.07|N|O|1998-07-12|1998-06-09|1998-07-15|DELIVER IN PERSON|SHIP| the special foxes | +5059|70|5|1|5|4850.35|0.03|0.08|R|F|1993-12-23|1994-01-12|1993-12-24|TAKE BACK RETURN|FOB|ts affix slyly accordi| +5059|123|2|2|19|19439.28|0.06|0.04|R|F|1994-03-02|1993-12-26|1994-03-14|TAKE BACK RETURN|MAIL| special ideas poach blithely qu| +5059|77|7|3|45|43968.15|0.02|0.00|A|F|1994-01-28|1994-01-08|1994-02-18|DELIVER IN PERSON|MAIL|enly. requests doze. express, close pa| +5060|25|8|1|27|24975.54|0.10|0.07|R|F|1992-07-23|1992-09-05|1992-08-07|COLLECT COD|SHIP|s. ironic | +5060|32|8|2|28|26096.84|0.04|0.04|R|F|1992-09-25|1992-08-11|1992-10-09|NONE|REG AIR|c requests| +5060|161|2|3|15|15917.40|0.06|0.01|A|F|1992-08-28|1992-08-20|1992-09-01|DELIVER IN PERSON|AIR|ular deposits sl| +5061|165|2|1|18|19172.88|0.03|0.00|A|F|1993-10-20|1993-10-05|1993-10-28|TAKE BACK RETURN|SHIP|atelets among the ca| +5061|198|1|2|8|8785.52|0.01|0.02|R|F|1993-09-07|1993-10-31|1993-10-04|DELIVER IN PERSON|REG AIR|regular foxes. ir| +5061|24|5|3|26|24024.52|0.02|0.05|A|F|1993-11-07|1993-09-13|1993-11-13|NONE|REG AIR| cajole slyly. carefully spe| +5062|101|4|1|9|9009.90|0.08|0.00|R|F|1993-01-02|1992-12-01|1993-01-20|TAKE BACK RETURN|MAIL| silent theodolites wake. c| +5062|75|6|2|4|3900.28|0.02|0.02|R|F|1993-02-06|1992-12-14|1993-03-03|DELIVER IN PERSON|AIR|ke furiously express theodolites. | +5062|159|10|3|50|52957.50|0.09|0.07|A|F|1992-12-25|1992-12-13|1992-12-29|TAKE BACK RETURN|MAIL| the regular, unusual pains. specia| +5062|161|10|4|18|19100.88|0.03|0.07|R|F|1992-11-04|1992-12-25|1992-11-05|NONE|SHIP|furiously pending requests are ruthles| +5062|194|8|5|25|27354.75|0.08|0.02|R|F|1992-12-15|1992-11-17|1993-01-01|NONE|TRUCK|uthless excuses ag| +5063|129|10|1|31|31902.72|0.08|0.01|N|O|1997-06-02|1997-06-20|1997-06-27|NONE|RAIL|kages. ironic, ironic courts wake. carefu| +5063|174|2|2|43|46189.31|0.04|0.08|N|O|1997-09-14|1997-07-05|1997-10-05|TAKE BACK RETURN|TRUCK|latelets might nod blithely regular requ| +5063|167|4|3|2|2134.32|0.02|0.03|N|O|1997-06-17|1997-07-27|1997-06-24|COLLECT COD|SHIP|kly regular i| +5063|135|6|4|18|18632.34|0.08|0.05|N|O|1997-06-02|1997-06-18|1997-06-06|TAKE BACK RETURN|RAIL|refully quiet reques| +5063|161|8|5|1|1061.16|0.06|0.07|N|O|1997-09-03|1997-06-26|1997-10-03|NONE|FOB|ously special | +5088|78|6|1|23|22495.61|0.06|0.06|R|F|1993-03-03|1993-03-07|1993-03-08|NONE|FOB|cording to the fluffily expr| +5088|51|3|2|41|38993.05|0.09|0.00|R|F|1993-01-22|1993-03-07|1993-02-09|TAKE BACK RETURN|TRUCK|ing requests. | +5088|86|7|3|36|35498.88|0.10|0.05|A|F|1993-04-16|1993-04-03|1993-05-14|NONE|TRUCK|the furiously final deposits. furiously re| +5088|109|6|4|10|10091.00|0.04|0.05|R|F|1993-04-07|1993-02-06|1993-04-26|NONE|FOB|beans. special requests af| +5089|158|6|1|4|4232.60|0.05|0.06|R|F|1992-09-18|1992-09-28|1992-10-13|DELIVER IN PERSON|TRUCK|nts sleep blithely | +5089|162|3|2|20|21243.20|0.00|0.07|R|F|1992-10-10|1992-10-07|1992-11-06|COLLECT COD|RAIL| ironic accounts| +5089|124|7|3|46|47109.52|0.03|0.04|A|F|1992-11-09|1992-10-13|1992-11-10|TAKE BACK RETURN|RAIL|above the express accounts. exc| +5089|34|10|4|38|35493.14|0.05|0.03|R|F|1992-11-23|1992-09-11|1992-12-22|TAKE BACK RETURN|SHIP|regular instructions are| +5090|22|3|1|22|20284.44|0.07|0.00|N|O|1997-05-10|1997-05-25|1997-05-24|TAKE BACK RETURN|TRUCK|ets integrate ironic, regul| +5090|129|10|2|46|47339.52|0.05|0.00|N|O|1997-04-05|1997-04-14|1997-05-01|COLLECT COD|REG AIR|lose theodolites sleep blit| +5090|2|9|3|22|19844.00|0.09|0.05|N|O|1997-07-03|1997-04-12|1997-07-26|NONE|REG AIR|ular requests su| +5090|114|8|4|2|2028.22|0.03|0.06|N|O|1997-04-07|1997-04-23|1997-05-01|TAKE BACK RETURN|AIR|tes. slowly iro| +5090|48|9|5|21|19908.84|0.10|0.02|N|O|1997-03-29|1997-04-24|1997-04-25|TAKE BACK RETURN|FOB|ly express accounts. slyly even r| +5090|80|9|6|30|29402.40|0.02|0.03|N|O|1997-05-04|1997-04-14|1997-05-30|COLLECT COD|MAIL|osits nag slyly. fluffily ex| +5091|78|6|1|50|48903.50|0.05|0.03|N|O|1998-07-21|1998-06-22|1998-07-26|COLLECT COD|REG AIR|al dependencies. r| +5092|164|1|1|30|31924.80|0.06|0.00|N|O|1995-12-27|1995-12-08|1996-01-09|DELIVER IN PERSON|MAIL|ss, ironic deposits. furiously stea| +5092|45|4|2|34|32131.36|0.04|0.02|N|O|1995-12-09|1995-12-26|1995-12-21|TAKE BACK RETURN|AIR|ckages nag | +5092|140|6|3|13|13521.82|0.06|0.01|N|O|1995-11-21|1996-01-05|1995-12-19|TAKE BACK RETURN|SHIP|es detect sly| +5092|180|1|4|14|15122.52|0.04|0.00|N|O|1996-02-20|1995-11-30|1996-03-20|DELIVER IN PERSON|REG AIR| deposits cajole furiously against the sly| +5092|186|7|5|42|45619.56|0.01|0.02|N|O|1995-11-06|1996-01-01|1995-12-06|DELIVER IN PERSON|AIR|s use along t| +5092|178|6|6|11|11859.87|0.03|0.03|N|O|1995-12-02|1995-12-27|1995-12-11|COLLECT COD|MAIL|ly against the slyly silen| +5092|159|10|7|50|52957.50|0.10|0.03|N|O|1995-11-30|1996-01-14|1995-12-19|NONE|REG AIR|r platelets maintain car| +5093|168|9|1|40|42726.40|0.05|0.01|R|F|1993-09-16|1993-11-04|1993-10-05|TAKE BACK RETURN|REG AIR|ing pinto beans. quickly bold dependenci| +5093|74|2|2|15|14611.05|0.01|0.04|A|F|1993-12-02|1993-11-18|1994-01-01|DELIVER IN PERSON|FOB|ly among the unusual foxe| +5093|151|9|3|31|32585.65|0.00|0.02|R|F|1993-09-22|1993-11-14|1993-09-26|TAKE BACK RETURN|REG AIR| against the| +5093|156|1|4|37|39077.55|0.04|0.00|A|F|1993-10-26|1993-12-02|1993-10-27|NONE|TRUCK|courts. qui| +5093|115|2|5|30|30453.30|0.06|0.05|A|F|1993-11-22|1993-11-27|1993-12-14|DELIVER IN PERSON|TRUCK|ithely ironic sheaves use fluff| +5093|121|6|6|31|31654.72|0.01|0.08|A|F|1993-12-17|1993-11-14|1994-01-02|NONE|SHIP|he final foxes. fluffily ironic | +5094|143|10|1|19|19819.66|0.03|0.03|R|F|1993-03-31|1993-06-12|1993-04-04|NONE|AIR|ronic foxes. furi| +5094|108|5|2|23|23186.30|0.05|0.07|R|F|1993-06-13|1993-05-19|1993-07-06|NONE|MAIL|st furiously above the fluffily care| +5094|92|6|3|11|10912.99|0.04|0.08|A|F|1993-06-25|1993-06-24|1993-07-18|TAKE BACK RETURN|MAIL|s cajole quickly against the furiously ex| +5094|79|10|4|21|20560.47|0.09|0.08|R|F|1993-07-26|1993-05-03|1993-08-16|NONE|MAIL| blithely furiously final re| +5095|65|10|1|46|44392.76|0.07|0.01|A|F|1992-06-26|1992-06-25|1992-07-05|COLLECT COD|RAIL|egular instruction| +5095|106|3|2|2|2012.20|0.07|0.08|A|F|1992-07-09|1992-05-25|1992-07-21|DELIVER IN PERSON|REG AIR|detect car| +5095|123|8|3|28|28647.36|0.01|0.04|A|F|1992-06-20|1992-06-27|1992-06-22|DELIVER IN PERSON|AIR| into the final courts. ca| +5095|178|7|4|42|45283.14|0.08|0.08|R|F|1992-05-23|1992-06-01|1992-06-18|COLLECT COD|TRUCK|ccounts. packages could have t| +5095|166|7|5|9|9595.44|0.10|0.07|R|F|1992-08-14|1992-06-23|1992-08-16|TAKE BACK RETURN|REG AIR|bold theodolites wake about the expr| +5095|97|8|6|15|14956.35|0.01|0.06|A|F|1992-07-11|1992-07-12|1992-08-09|COLLECT COD|AIR| to the packages wake sly| +5095|169|10|7|40|42766.40|0.05|0.02|A|F|1992-07-11|1992-06-07|1992-07-26|DELIVER IN PERSON|MAIL|carefully unusual plat| +5120|133|4|1|28|28927.64|0.06|0.03|N|O|1996-07-20|1996-08-31|1996-08-06|NONE|RAIL| across the silent requests. caref| +5121|184|5|1|23|24936.14|0.06|0.01|A|F|1992-05-18|1992-06-20|1992-06-02|TAKE BACK RETURN|REG AIR|even courts are blithely ironically | +5121|111|1|2|45|45499.95|0.08|0.04|A|F|1992-08-13|1992-07-27|1992-09-12|NONE|TRUCK|pecial accounts cajole ca| +5121|97|10|3|27|26921.43|0.08|0.07|R|F|1992-06-17|1992-06-11|1992-06-19|NONE|MAIL|ly silent theodolit| +5121|68|7|4|10|9680.60|0.04|0.05|R|F|1992-06-08|1992-07-10|1992-07-02|TAKE BACK RETURN|FOB|e quickly according | +5121|89|10|5|46|45497.68|0.03|0.02|R|F|1992-05-27|1992-07-19|1992-05-28|TAKE BACK RETURN|FOB|use express foxes. slyly | +5121|1|8|6|2|1802.00|0.04|0.07|R|F|1992-08-10|1992-06-28|1992-08-11|NONE|FOB| final, regular account| +5122|183|4|1|28|30329.04|0.03|0.00|N|O|1996-04-20|1996-03-29|1996-04-29|DELIVER IN PERSON|FOB|g the busily ironic accounts boos| +5122|82|3|2|43|42229.44|0.09|0.03|N|O|1996-05-31|1996-04-12|1996-06-13|NONE|MAIL|ut the carefully special foxes. idle,| +5122|45|6|3|12|11340.48|0.07|0.03|N|O|1996-04-02|1996-04-27|1996-04-10|DELIVER IN PERSON|AIR|lar instructions | +5123|26|7|1|13|12038.26|0.08|0.07|N|O|1998-05-17|1998-03-23|1998-06-02|COLLECT COD|MAIL|regular pearls| +5124|55|7|1|43|41067.15|0.00|0.02|N|O|1997-07-10|1997-05-13|1997-07-31|COLLECT COD|AIR|onic package| +5124|6|3|2|41|37146.00|0.02|0.06|N|O|1997-07-05|1997-06-29|1997-07-23|DELIVER IN PERSON|RAIL|wake across the| +5124|125|6|3|44|45105.28|0.03|0.03|N|O|1997-07-13|1997-06-26|1997-08-01|DELIVER IN PERSON|RAIL|equests. carefully unusual d| +5124|70|9|4|36|34922.52|0.10|0.07|N|O|1997-04-20|1997-07-03|1997-05-04|TAKE BACK RETURN|AIR|r deposits ab| +5125|6|9|1|38|34428.00|0.09|0.05|N|O|1998-03-20|1998-04-14|1998-03-22|COLLECT COD|MAIL|ily even deposits w| +5125|160|1|2|5|5300.80|0.08|0.06|N|O|1998-04-07|1998-04-14|1998-04-29|COLLECT COD|RAIL| thinly even pack| +5126|24|3|1|33|30492.66|0.02|0.02|R|F|1993-02-04|1992-12-23|1993-02-14|NONE|RAIL|ipliers promise furiously whithout the | +5126|101|6|2|43|43047.30|0.09|0.04|R|F|1993-01-07|1992-12-19|1993-01-16|COLLECT COD|MAIL|e silently. ironic, unusual accounts| +5126|78|8|3|23|22495.61|0.08|0.01|R|F|1993-01-02|1993-01-02|1993-01-05|COLLECT COD|TRUCK|egular, blithe packages.| +5127|19|3|1|33|30327.33|0.08|0.04|N|O|1997-03-25|1997-03-02|1997-04-04|NONE|SHIP| bold deposits use carefully a| +5127|32|8|2|20|18640.60|0.01|0.03|N|O|1997-05-11|1997-02-26|1997-05-12|TAKE BACK RETURN|SHIP|dolites about the final platelets w| +5152|105|2|1|9|9045.90|0.04|0.03|N|O|1997-04-11|1997-02-11|1997-04-18|COLLECT COD|AIR| cajole furiously alongside of the bo| +5152|134|10|2|50|51706.50|0.04|0.04|N|O|1997-03-10|1997-02-04|1997-03-15|COLLECT COD|FOB| the final deposits. slyly ironic warth| +5153|35|1|1|42|39271.26|0.03|0.01|N|O|1995-10-03|1995-11-09|1995-10-11|COLLECT COD|RAIL|re thinly. ironic| +5153|53|5|2|14|13342.70|0.05|0.05|N|O|1995-11-29|1995-10-21|1995-12-08|TAKE BACK RETURN|TRUCK| slyly daring pinto beans lose blithely fi| +5153|68|7|3|30|29041.80|0.09|0.01|N|O|1995-11-10|1995-11-14|1995-11-16|DELIVER IN PERSON|AIR|beans sleep bl| +5153|173|2|4|32|34341.44|0.10|0.08|N|O|1995-12-05|1995-09-25|1996-01-03|DELIVER IN PERSON|MAIL|egular deposits. ironi| +5153|112|2|5|36|36435.96|0.01|0.03|N|O|1995-12-15|1995-11-08|1995-12-30|COLLECT COD|TRUCK| ironic instru| +5153|136|2|6|42|43517.46|0.00|0.03|N|O|1995-10-19|1995-11-23|1995-11-06|TAKE BACK RETURN|RAIL|ickly even deposi| +5154|190|1|1|11|11992.09|0.02|0.05|N|O|1997-08-06|1997-06-30|1997-09-04|NONE|RAIL|luffily bold foxes. final| +5154|144|5|2|15|15662.10|0.07|0.08|N|O|1997-06-23|1997-07-11|1997-07-11|NONE|AIR|even packages. packages use| +5155|48|9|1|1|948.04|0.00|0.00|A|F|1994-07-03|1994-08-11|1994-07-29|COLLECT COD|FOB|oze slyly after the silent, regular idea| +5155|188|9|2|5|5440.90|0.08|0.02|A|F|1994-06-30|1994-08-13|1994-07-15|TAKE BACK RETURN|AIR|ole blithely slyly ironic | +5155|106|3|3|28|28170.80|0.05|0.02|R|F|1994-07-01|1994-07-19|1994-07-18|COLLECT COD|REG AIR|s cajole. accounts wake. thinly quiet pla| +5155|79|7|4|39|38183.73|0.09|0.06|A|F|1994-08-25|1994-09-01|1994-09-18|COLLECT COD|TRUCK|l dolphins nag caref| +5156|117|4|1|21|21359.31|0.06|0.03|N|O|1997-01-01|1997-01-30|1997-01-11|TAKE BACK RETURN|TRUCK|ts detect against the furiously reg| +5156|148|1|2|36|37733.04|0.04|0.07|N|O|1997-02-12|1996-12-10|1997-03-13|TAKE BACK RETURN|REG AIR| slyly even orbi| +5157|55|7|1|35|33426.75|0.06|0.08|N|O|1997-07-28|1997-09-30|1997-08-15|TAKE BACK RETURN|REG AIR|to the furiously sil| +5157|138|9|2|18|18686.34|0.10|0.04|N|O|1997-09-06|1997-10-03|1997-09-19|COLLECT COD|MAIL|y bold deposits nag blithely. final reque| +5157|167|8|3|15|16007.40|0.09|0.00|N|O|1997-07-27|1997-08-30|1997-08-08|DELIVER IN PERSON|RAIL|cajole. spec| +5157|59|7|4|25|23976.25|0.00|0.03|N|O|1997-08-24|1997-09-23|1997-08-28|COLLECT COD|REG AIR| packages detect. even requests against th| +5157|149|8|5|40|41965.60|0.09|0.06|N|O|1997-08-11|1997-08-28|1997-09-01|TAKE BACK RETURN|FOB|ial packages according to | +5157|150|9|6|26|27303.90|0.10|0.01|N|O|1997-07-28|1997-08-22|1997-08-22|NONE|FOB|nto beans cajole car| +5157|49|8|7|12|11388.48|0.10|0.08|N|O|1997-10-19|1997-08-07|1997-10-26|NONE|FOB|es. busily | +5158|45|4|1|43|40636.72|0.10|0.04|N|O|1997-04-10|1997-03-06|1997-04-15|DELIVER IN PERSON|AIR|nusual platelets. slyly even foxes cajole | +5158|85|6|2|18|17731.44|0.04|0.04|N|O|1997-04-30|1997-03-28|1997-05-12|COLLECT COD|REG AIR|hely regular pa| +5158|142|9|3|41|42727.74|0.05|0.05|N|O|1997-02-25|1997-03-19|1997-03-03|COLLECT COD|AIR|deposits. quickly special | +5158|131|7|4|49|50525.37|0.05|0.01|N|O|1997-04-10|1997-03-21|1997-04-30|NONE|REG AIR|r requests sleep q| +5158|119|9|5|20|20382.20|0.01|0.04|N|O|1997-02-03|1997-02-20|1997-02-08|TAKE BACK RETURN|AIR|latelets use accordin| +5158|88|9|6|39|38535.12|0.08|0.04|N|O|1997-05-15|1997-04-04|1997-06-02|DELIVER IN PERSON|FOB|lithely fina| +5158|91|5|7|38|37661.42|0.10|0.05|N|O|1997-05-09|1997-03-03|1997-06-04|NONE|SHIP|uffily regular ac| +5159|124|7|1|39|39940.68|0.06|0.07|N|O|1996-12-17|1996-12-08|1997-01-10|COLLECT COD|MAIL|re furiously after the pending dolphin| +5159|17|1|2|46|42182.46|0.01|0.01|N|O|1996-12-15|1996-12-07|1996-12-30|DELIVER IN PERSON|SHIP|s kindle slyly carefully regular| +5159|152|4|3|22|23147.30|0.01|0.02|N|O|1996-11-06|1996-11-04|1996-11-15|TAKE BACK RETURN|SHIP|he furiously sile| +5159|52|3|4|5|4760.25|0.10|0.00|N|O|1996-11-25|1996-12-19|1996-12-25|TAKE BACK RETURN|FOB|nal deposits. pending, ironic ideas grow| +5159|198|10|5|36|39534.84|0.06|0.01|N|O|1997-01-24|1996-11-07|1997-02-08|NONE|REG AIR|packages wake.| +5184|153|8|1|33|34753.95|0.07|0.04|N|O|1998-08-17|1998-10-16|1998-08-24|TAKE BACK RETURN|AIR|posits. carefully express asympto| +5184|16|6|2|47|43052.47|0.05|0.01|N|O|1998-11-02|1998-08-19|1998-11-07|COLLECT COD|TRUCK|se. carefully express pinto beans x| +5184|88|9|3|39|38535.12|0.03|0.06|N|O|1998-10-27|1998-10-17|1998-11-19|DELIVER IN PERSON|FOB|es above the care| +5184|176|7|4|26|27980.42|0.05|0.08|N|O|1998-11-11|1998-08-26|1998-12-01|TAKE BACK RETURN|TRUCK| packages are| +5184|124|9|5|19|19458.28|0.06|0.03|N|O|1998-11-15|1998-10-12|1998-11-21|COLLECT COD|REG AIR|refully express platelets sleep carefull| +5184|80|9|6|49|48023.92|0.02|0.00|N|O|1998-09-18|1998-08-28|1998-10-14|COLLECT COD|FOB|thlessly closely even reque| +5185|197|1|1|37|40596.03|0.00|0.04|N|O|1997-08-08|1997-09-08|1997-08-14|COLLECT COD|SHIP|gainst the courts dazzle care| +5185|25|8|2|32|29600.64|0.06|0.00|N|O|1997-08-17|1997-09-30|1997-08-24|TAKE BACK RETURN|REG AIR|ackages. slyly even requests| +5185|196|9|3|41|44943.79|0.00|0.05|N|O|1997-10-15|1997-10-11|1997-11-02|COLLECT COD|REG AIR|ly blithe deposits. furi| +5185|96|7|4|30|29882.70|0.09|0.04|N|O|1997-10-17|1997-09-16|1997-10-23|TAKE BACK RETURN|SHIP|ress packages are furiously| +5185|128|9|5|8|8224.96|0.04|0.00|N|O|1997-08-30|1997-09-02|1997-09-22|COLLECT COD|REG AIR|sts around the slyly perma| +5185|146|9|6|50|52307.00|0.03|0.04|N|O|1997-10-15|1997-10-19|1997-11-06|TAKE BACK RETURN|FOB|final platelets. ideas sleep careful| +5186|55|10|1|38|36291.90|0.06|0.02|N|O|1996-11-23|1996-09-21|1996-12-11|DELIVER IN PERSON|MAIL|y ruthless foxes. fluffily | +5186|91|2|2|31|30723.79|0.09|0.03|N|O|1996-10-19|1996-09-26|1996-10-25|TAKE BACK RETURN|REG AIR| accounts use furiously slyly spe| +5186|89|10|3|26|25716.08|0.03|0.02|N|O|1996-08-08|1996-10-05|1996-08-21|DELIVER IN PERSON|FOB|capades. accounts sublate. pinto| +5186|90|1|4|8|7920.72|0.10|0.05|N|O|1996-09-23|1996-09-29|1996-09-30|COLLECT COD|RAIL|y regular notornis k| +5186|18|2|5|28|25704.28|0.09|0.03|N|O|1996-10-05|1996-10-27|1996-10-19|TAKE BACK RETURN|RAIL|al decoys. blit| +5186|82|3|6|35|34372.80|0.00|0.05|N|O|1996-10-20|1996-10-12|1996-11-12|TAKE BACK RETURN|RAIL|sly silent pack| +5186|198|10|7|44|48320.36|0.00|0.08|N|O|1996-09-23|1996-10-14|1996-10-01|NONE|TRUCK|old, final accounts cajole sl| +5187|11|1|1|49|44639.49|0.04|0.06|N|O|1997-10-20|1997-10-12|1997-10-26|DELIVER IN PERSON|AIR|l, regular platelets instead of the foxes w| +5187|83|4|2|1|983.08|0.10|0.08|N|O|1997-08-08|1997-08-24|1997-08-22|DELIVER IN PERSON|REG AIR|aggle never bold | +5188|118|2|1|18|18325.98|0.04|0.03|N|O|1995-06-19|1995-05-19|1995-06-25|DELIVER IN PERSON|AIR|p according to the sometimes regu| +5188|194|8|2|36|39390.84|0.04|0.02|A|F|1995-03-09|1995-05-16|1995-03-19|NONE|TRUCK|packages? blithely s| +5188|148|1|3|9|9433.26|0.06|0.08|A|F|1995-05-09|1995-05-22|1995-05-19|TAKE BACK RETURN|REG AIR|r attainments are across the | +5189|138|9|1|44|45677.72|0.02|0.06|A|F|1994-01-13|1994-02-07|1994-01-21|DELIVER IN PERSON|MAIL|y finally pendin| +5189|16|3|2|38|34808.38|0.06|0.00|A|F|1994-03-26|1994-01-28|1994-04-20|DELIVER IN PERSON|REG AIR|ideas. idle, final deposits de| +5189|110|5|3|4|4040.44|0.09|0.02|A|F|1993-12-21|1994-02-23|1994-01-09|DELIVER IN PERSON|REG AIR|. blithely exp| +5189|94|7|4|49|48710.41|0.05|0.01|R|F|1994-01-22|1994-01-19|1994-02-04|TAKE BACK RETURN|SHIP| requests | +5189|123|2|5|14|14323.68|0.02|0.03|A|F|1994-01-23|1994-01-05|1994-02-12|DELIVER IN PERSON|REG AIR|unusual packag| +5189|17|8|6|41|37597.41|0.02|0.06|R|F|1993-12-12|1994-02-05|1994-01-09|DELIVER IN PERSON|RAIL|ial theodolites cajole slyly. slyly unus| +5190|56|1|1|43|41110.15|0.09|0.06|A|F|1992-08-19|1992-06-10|1992-09-01|DELIVER IN PERSON|FOB|encies use fluffily unusual requests? hoc| +5190|132|3|2|6|6192.78|0.10|0.08|A|F|1992-08-08|1992-07-14|1992-08-22|COLLECT COD|RAIL|furiously regular pinto beans. furiously i| +5190|89|10|3|45|44508.60|0.04|0.03|A|F|1992-07-23|1992-06-16|1992-08-04|NONE|FOB|y carefully final ideas. f| +5191|115|6|1|41|41619.51|0.00|0.08|A|F|1995-02-05|1995-02-27|1995-02-15|DELIVER IN PERSON|AIR|uests! ironic theodolites cajole care| +5191|168|7|2|40|42726.40|0.02|0.01|A|F|1995-03-31|1995-02-21|1995-04-02|NONE|AIR|nes haggle sometimes. requests eng| +5191|43|4|3|27|25462.08|0.07|0.05|A|F|1994-12-26|1995-01-24|1995-01-14|DELIVER IN PERSON|RAIL|tructions nag bravely within the re| +5191|183|4|4|7|7582.26|0.01|0.04|A|F|1995-03-24|1995-01-30|1995-03-30|NONE|RAIL|eposits. express| +5216|69|10|1|17|16474.02|0.04|0.06|N|O|1997-08-20|1997-11-07|1997-09-14|COLLECT COD|FOB|s according to the accounts bo| +5217|80|1|1|50|49004.00|0.05|0.02|N|O|1995-12-26|1995-11-21|1996-01-24|DELIVER IN PERSON|MAIL|s. express, express accounts c| +5217|16|7|2|23|21068.23|0.06|0.07|N|O|1996-01-18|1995-12-24|1996-02-10|COLLECT COD|RAIL|ven ideas. requests amo| +5217|102|7|3|23|23048.30|0.03|0.02|N|O|1995-11-15|1995-12-17|1995-11-27|DELIVER IN PERSON|FOB|pending packages cajole ne| +5217|81|2|4|47|46110.76|0.04|0.00|N|O|1995-11-24|1995-12-25|1995-11-25|COLLECT COD|AIR|ronic packages i| +5218|83|4|1|43|42272.44|0.05|0.04|A|F|1992-08-04|1992-09-12|1992-08-17|DELIVER IN PERSON|SHIP|k theodolites. express, even id| +5218|125|4|2|33|33828.96|0.06|0.01|R|F|1992-09-16|1992-09-30|1992-09-27|NONE|TRUCK|ronic instructi| +5219|135|6|1|2|2070.26|0.08|0.00|N|O|1997-06-26|1997-04-29|1997-07-08|TAKE BACK RETURN|FOB| blithely according to the stea| +5219|119|9|2|20|20382.20|0.05|0.00|N|O|1997-04-20|1997-05-26|1997-05-13|COLLECT COD|FOB|e along the ironic,| +5220|83|4|1|27|26543.16|0.10|0.04|R|F|1992-09-21|1992-08-29|1992-10-16|DELIVER IN PERSON|RAIL|s cajole blithely furiously iron| +5221|104|9|1|24|24098.40|0.07|0.03|N|O|1995-10-04|1995-08-11|1995-10-30|COLLECT COD|REG AIR|s pinto beans sleep. sly| +5221|9|10|2|34|30906.00|0.01|0.05|N|O|1995-09-11|1995-07-17|1995-10-10|COLLECT COD|SHIP|eans. furio| +5221|180|10|3|16|17282.88|0.04|0.01|N|O|1995-08-29|1995-09-06|1995-09-12|TAKE BACK RETURN|TRUCK|ending request| +5222|151|3|1|1|1051.15|0.00|0.00|A|F|1994-08-19|1994-07-16|1994-09-08|TAKE BACK RETURN|FOB|idle requests. carefully pending pinto bean| +5223|45|4|1|24|22680.96|0.00|0.00|A|F|1994-10-03|1994-09-20|1994-10-11|TAKE BACK RETURN|TRUCK|refully bold courts besides the regular,| +5223|124|9|2|25|25603.00|0.09|0.02|R|F|1994-07-12|1994-08-13|1994-08-01|NONE|FOB|y express ideas impress| +5223|6|3|3|19|17214.00|0.04|0.01|R|F|1994-10-28|1994-08-26|1994-10-31|COLLECT COD|REG AIR|ntly. furiously even excuses a| +5223|130|9|4|40|41205.20|0.01|0.04|R|F|1994-10-01|1994-09-18|1994-10-28|COLLECT COD|SHIP|kly pending | +5248|81|2|1|39|38262.12|0.05|0.03|N|O|1995-08-10|1995-07-04|1995-09-09|TAKE BACK RETURN|MAIL|yly even accounts. spe| +5248|138|9|2|45|46715.85|0.00|0.06|A|F|1995-05-09|1995-07-12|1995-05-27|DELIVER IN PERSON|FOB|. bold, pending foxes h| +5249|50|9|1|31|29451.55|0.07|0.03|A|F|1994-11-21|1994-11-19|1994-12-08|NONE|REG AIR|f the excuses. furiously fin| +5249|31|7|2|44|40965.32|0.05|0.00|A|F|1994-12-28|1994-11-29|1994-12-29|TAKE BACK RETURN|MAIL|ole furiousl| +5249|32|8|3|13|12116.39|0.09|0.00|R|F|1994-09-27|1994-10-20|1994-10-05|DELIVER IN PERSON|SHIP|ites. finally exp| +5249|146|3|4|29|30338.06|0.00|0.05|A|F|1994-09-16|1994-11-03|1994-10-06|NONE|TRUCK| players. f| +5249|158|6|5|12|12697.80|0.01|0.08|R|F|1994-12-28|1994-11-07|1995-01-15|COLLECT COD|MAIL|press depths could have to sleep carefu| +5250|44|3|1|2|1888.08|0.08|0.04|N|O|1995-08-09|1995-10-10|1995-08-13|COLLECT COD|AIR|its. final pinto| +5250|192|6|2|27|29489.13|0.10|0.05|N|O|1995-10-24|1995-09-03|1995-11-18|COLLECT COD|TRUCK|l forges are. furiously unusual pin| +5251|139|10|1|36|37408.68|0.10|0.01|N|O|1995-07-16|1995-07-05|1995-07-28|DELIVER IN PERSON|FOB|slowly! bli| +5252|141|10|1|13|13534.82|0.02|0.01|N|O|1996-03-02|1996-05-10|1996-03-11|NONE|FOB|boost fluffily across | +5252|139|5|2|39|40526.07|0.06|0.05|N|O|1996-05-17|1996-04-23|1996-05-23|COLLECT COD|AIR|gular requests.| +5252|195|9|3|9|9856.71|0.09|0.03|N|O|1996-05-30|1996-05-03|1996-06-26|TAKE BACK RETURN|RAIL|x. slyly special depos| +5252|87|8|4|48|47379.84|0.01|0.06|N|O|1996-04-17|1996-03-19|1996-05-03|COLLECT COD|AIR|bold requests. furious| +5252|68|5|5|24|23233.44|0.04|0.05|N|O|1996-05-11|1996-04-17|1996-05-12|COLLECT COD|REG AIR|posits after the fluffi| +5252|3|10|6|41|37023.00|0.02|0.03|N|O|1996-03-16|1996-04-18|1996-03-17|NONE|TRUCK|ording to the blithely express somas sho| +5253|31|2|1|35|32586.05|0.02|0.00|N|O|1995-07-23|1995-06-12|1995-08-03|DELIVER IN PERSON|AIR|ven deposits. careful| +5253|150|7|2|38|39905.70|0.02|0.06|N|O|1995-08-03|1995-06-14|1995-08-27|DELIVER IN PERSON|REG AIR|onic dependencies are furiou| +5253|14|5|3|9|8226.09|0.03|0.08|N|F|1995-06-08|1995-05-12|1995-06-23|DELIVER IN PERSON|REG AIR|lyly express deposits use furiou| +5253|166|1|4|25|26654.00|0.04|0.03|A|F|1995-05-21|1995-06-13|1995-06-09|COLLECT COD|TRUCK|urts. even theodoli| +5254|111|2|1|35|35388.85|0.01|0.07|A|F|1992-07-28|1992-09-05|1992-08-07|COLLECT COD|REG AIR|ntegrate carefully among the pending| +5254|135|6|2|10|10351.30|0.05|0.04|A|F|1992-11-19|1992-10-20|1992-12-15|COLLECT COD|SHIP| accounts. silent deposit| +5254|192|5|3|32|34950.08|0.00|0.08|A|F|1992-08-10|1992-09-21|1992-08-16|NONE|RAIL|ts impress closely furi| +5254|163|2|4|45|47842.20|0.05|0.06|A|F|1992-11-11|1992-09-01|1992-12-07|COLLECT COD|REG AIR| wake. blithely silent excuse| +5254|29|8|5|23|21367.46|0.02|0.06|A|F|1992-08-16|1992-09-05|1992-09-15|COLLECT COD|RAIL|lyly regular accounts. furiously pendin| +5254|158|3|6|34|35977.10|0.09|0.02|R|F|1992-08-29|1992-10-16|1992-09-20|TAKE BACK RETURN|RAIL| furiously above the furiously | +5254|20|7|7|9|8280.18|0.09|0.03|R|F|1992-07-29|1992-10-15|1992-08-20|TAKE BACK RETURN|REG AIR| wake blithely fluff| +5255|131|7|1|2|2062.26|0.04|0.08|N|O|1996-09-27|1996-10-04|1996-10-04|DELIVER IN PERSON|RAIL|ajole blithely fluf| +5255|172|10|2|30|32165.10|0.04|0.08|N|O|1996-09-20|1996-08-18|1996-10-09|TAKE BACK RETURN|AIR| to the silent requests cajole b| +5255|130|3|3|41|42235.33|0.09|0.03|N|O|1996-08-21|1996-09-24|1996-09-05|COLLECT COD|FOB|tect blithely against t| +5280|97|9|1|16|15953.44|0.02|0.03|N|O|1998-03-29|1998-01-28|1998-04-03|TAKE BACK RETURN|SHIP| foxes are furiously. theodoli| +5280|176|5|2|46|49503.82|0.01|0.06|N|O|1998-01-04|1998-01-21|1998-02-03|TAKE BACK RETURN|FOB|efully carefully pen| +5281|114|1|1|37|37522.07|0.05|0.02|N|O|1995-11-10|1996-01-31|1995-11-22|DELIVER IN PERSON|MAIL|ronic dependencies. fluffily final p| +5281|105|2|2|38|38193.80|0.00|0.05|N|O|1996-02-17|1995-12-19|1996-02-29|NONE|RAIL|n asymptotes could wake about th| +5281|127|2|3|23|23623.76|0.08|0.00|N|O|1995-12-30|1996-01-26|1996-01-23|COLLECT COD|REG AIR|. final theodolites cajole. ironic p| +5281|87|8|4|48|47379.84|0.03|0.05|N|O|1996-01-31|1995-12-23|1996-02-08|TAKE BACK RETURN|REG AIR|ss the furiously | +5281|43|10|5|33|31120.32|0.01|0.07|N|O|1996-03-01|1995-12-28|1996-03-05|COLLECT COD|RAIL|ly brave foxes. bold deposits above the | +5282|118|2|1|36|36651.96|0.05|0.02|N|O|1998-05-20|1998-04-10|1998-06-14|DELIVER IN PERSON|TRUCK|re slyly accor| +5282|52|10|2|32|30465.60|0.02|0.05|N|O|1998-03-01|1998-03-31|1998-03-03|NONE|FOB|onic deposits; furiou| +5282|58|10|3|28|26825.40|0.03|0.06|N|O|1998-05-06|1998-04-24|1998-05-30|COLLECT COD|SHIP|fily final instruc| +5283|5|2|1|20|18100.00|0.05|0.02|A|F|1994-09-16|1994-08-03|1994-10-15|TAKE BACK RETURN|TRUCK|al deposits? blithely even pinto beans| +5283|186|7|2|1|1086.18|0.10|0.08|R|F|1994-06-20|1994-08-03|1994-07-01|COLLECT COD|FOB|deposits within the furio| +5284|173|1|1|16|17170.72|0.04|0.02|N|O|1995-08-17|1995-08-23|1995-08-26|DELIVER IN PERSON|TRUCK|unts detect furiously even d| +5284|44|7|2|24|22656.96|0.03|0.08|N|O|1995-10-21|1995-08-23|1995-10-27|COLLECT COD|AIR| haggle according | +5285|193|5|1|31|33888.89|0.08|0.00|A|F|1994-04-17|1994-04-05|1994-05-09|NONE|RAIL|ubt. quickly blithe | +5285|31|2|2|37|34448.11|0.09|0.02|R|F|1994-02-26|1994-02-18|1994-03-27|NONE|SHIP|uffily regu| +5285|34|10|3|24|22416.72|0.02|0.04|A|F|1994-04-19|1994-04-03|1994-04-25|DELIVER IN PERSON|FOB|ess packages. quick, even deposits snooze b| +5285|43|2|4|12|11316.48|0.05|0.06|A|F|1994-04-22|1994-04-07|1994-05-19|NONE|AIR| deposits-- quickly bold requests hag| +5285|71|2|5|1|971.07|0.03|0.05|R|F|1994-03-14|1994-02-20|1994-04-10|COLLECT COD|TRUCK|e fluffily about the slyly special pa| +5285|146|7|6|1|1046.14|0.06|0.01|R|F|1994-02-08|1994-04-02|1994-02-17|COLLECT COD|SHIP|ing deposits integra| +5286|199|1|1|1|1099.19|0.01|0.07|N|O|1997-11-25|1997-11-07|1997-12-17|COLLECT COD|REG AIR|ly! furiously final pack| +5286|97|1|2|7|6979.63|0.06|0.05|N|O|1997-10-23|1997-12-10|1997-11-20|TAKE BACK RETURN|RAIL|y express instructions sleep carefull| +5286|16|10|3|3|2748.03|0.06|0.08|N|O|1997-12-04|1997-11-06|1997-12-09|COLLECT COD|MAIL|re fluffily| +5286|40|6|4|6|5640.24|0.04|0.03|N|O|1997-10-15|1997-12-05|1997-11-12|COLLECT COD|RAIL|y special a| +5286|186|7|5|38|41274.84|0.07|0.05|N|O|1997-11-29|1997-11-26|1997-12-15|TAKE BACK RETURN|SHIP|fluffily. special, ironic deposit| +5286|138|9|6|24|24915.12|0.08|0.00|N|O|1997-09-27|1997-12-21|1997-09-30|COLLECT COD|TRUCK|s. express foxes of the| +5287|39|10|1|32|30048.96|0.01|0.01|A|F|1994-01-29|1994-01-27|1994-02-08|NONE|RAIL|heodolites haggle caref| +5312|61|6|1|27|25948.62|0.04|0.08|A|F|1995-04-20|1995-04-09|1995-04-25|COLLECT COD|SHIP|tructions cajol| +5312|2|5|2|43|38786.00|0.05|0.08|A|F|1995-03-24|1995-05-07|1995-03-28|NONE|TRUCK|ly unusual| +5313|17|1|1|34|31178.34|0.10|0.02|N|O|1997-08-07|1997-08-12|1997-08-24|DELIVER IN PERSON|FOB|ccording to the blithely final account| +5313|13|10|2|17|15521.17|0.00|0.02|N|O|1997-09-02|1997-08-20|1997-09-07|NONE|SHIP|uests wake| +5313|112|9|3|47|47569.17|0.06|0.08|N|O|1997-08-12|1997-08-18|1997-08-13|TAKE BACK RETURN|RAIL|pinto beans across the | +5313|197|1|4|16|17555.04|0.08|0.00|N|O|1997-10-04|1997-08-02|1997-10-25|COLLECT COD|REG AIR|ckages wake carefully aga| +5313|72|1|5|30|29162.10|0.06|0.08|N|O|1997-06-27|1997-07-18|1997-06-30|NONE|SHIP|nding packages use| +5313|120|7|6|21|21422.52|0.05|0.05|N|O|1997-09-26|1997-09-02|1997-10-18|COLLECT COD|FOB|he blithely regular packages. quickly| +5314|118|9|1|10|10181.10|0.07|0.05|N|O|1995-09-26|1995-07-24|1995-10-19|DELIVER IN PERSON|RAIL|latelets haggle final| +5314|125|6|2|16|16401.92|0.00|0.04|N|O|1995-09-25|1995-07-08|1995-10-17|COLLECT COD|SHIP|hely unusual packages acc| +5315|35|1|1|12|11220.36|0.08|0.06|R|F|1992-12-18|1993-01-16|1993-01-10|NONE|AIR|ccounts. furiously ironi| +5315|179|10|2|39|42087.63|0.00|0.06|R|F|1992-11-09|1992-12-29|1992-12-07|NONE|SHIP|ly alongside of the ca| +5316|108|1|1|29|29234.90|0.10|0.05|R|F|1994-03-28|1994-04-29|1994-04-09|DELIVER IN PERSON|REG AIR|ckly unusual foxes bo| +5316|136|7|2|31|32120.03|0.00|0.08|A|F|1994-04-01|1994-04-21|1994-04-12|DELIVER IN PERSON|MAIL|s. deposits cajole around t| +5317|82|3|1|29|28480.32|0.02|0.06|A|F|1994-11-28|1994-11-27|1994-12-16|COLLECT COD|FOB|oss the carefull| +5317|171|2|2|18|19281.06|0.06|0.06|A|F|1995-01-02|1994-10-29|1995-01-16|NONE|RAIL|g to the blithely p| +5317|120|4|3|37|37744.44|0.09|0.00|R|F|1994-09-15|1994-10-24|1994-09-23|NONE|TRUCK|totes nag theodolites. pend| +5317|67|6|4|50|48353.00|0.09|0.01|A|F|1994-10-17|1994-10-25|1994-11-03|NONE|REG AIR|cajole furiously. accounts use quick| +5317|95|8|5|19|18906.71|0.07|0.07|R|F|1994-12-15|1994-10-18|1994-12-27|NONE|MAIL|onic requests boost bli| +5317|115|9|6|48|48725.28|0.01|0.03|A|F|1994-09-19|1994-11-25|1994-10-03|COLLECT COD|MAIL|ts about the packages cajole furio| +5317|169|4|7|30|32074.80|0.07|0.07|A|F|1994-10-13|1994-10-31|1994-10-28|NONE|AIR|cross the attainments. slyly | +5318|61|6|1|13|12493.78|0.10|0.04|R|F|1993-07-15|1993-06-25|1993-08-13|COLLECT COD|REG AIR|ly silent ideas. ideas haggle among the | +5318|180|1|2|26|28084.68|0.00|0.04|R|F|1993-07-07|1993-05-23|1993-07-28|COLLECT COD|SHIP|al, express foxes. bold requests sleep alwa| +5318|7|10|3|37|33559.00|0.07|0.05|A|F|1993-07-09|1993-06-22|1993-07-21|COLLECT COD|SHIP|ickly final deposi| +5318|142|5|4|31|32306.34|0.01|0.04|R|F|1993-07-28|1993-05-06|1993-08-06|DELIVER IN PERSON|REG AIR|requests must sleep slyly quickly| +5319|150|9|1|31|32554.65|0.04|0.07|N|O|1996-03-26|1996-03-07|1996-04-24|COLLECT COD|TRUCK|d carefully about the courts. fluffily spe| +5319|44|3|2|39|36817.56|0.09|0.05|N|O|1996-05-17|1996-03-14|1996-06-11|NONE|TRUCK|unts. furiously silent| +5344|19|3|1|6|5514.06|0.07|0.01|N|O|1998-08-04|1998-09-03|1998-08-11|TAKE BACK RETURN|REG AIR|ithely about the pending plate| +5344|79|9|2|37|36225.59|0.03|0.07|N|O|1998-10-09|1998-07-26|1998-11-08|NONE|TRUCK|thely express packages| +5344|67|8|3|26|25143.56|0.02|0.06|N|O|1998-08-27|1998-08-22|1998-09-24|NONE|AIR|furiously pending, silent multipliers.| +5344|39|10|4|21|19719.63|0.03|0.01|N|O|1998-08-31|1998-09-06|1998-09-02|NONE|MAIL|xes. furiously even pinto beans sleep f| +5345|83|4|1|3|2949.24|0.05|0.01|N|O|1997-12-10|1997-10-03|1998-01-05|COLLECT COD|SHIP|ites wake carefully unusual | +5345|146|5|2|2|2092.28|0.10|0.02|N|O|1997-11-18|1997-10-12|1997-12-08|NONE|MAIL|ut the slyly specia| +5345|192|5|3|46|50240.74|0.06|0.04|N|O|1997-10-06|1997-09-27|1997-10-18|COLLECT COD|REG AIR|slyly special deposits. fin| +5345|114|4|4|37|37522.07|0.01|0.01|N|O|1997-11-01|1997-10-09|1997-11-26|DELIVER IN PERSON|AIR| along the ironically fina| +5345|34|10|5|22|20548.66|0.02|0.02|N|O|1997-08-27|1997-11-22|1997-09-10|TAKE BACK RETURN|MAIL|leep slyly regular fox| +5346|149|8|1|21|22031.94|0.07|0.08|R|F|1994-03-11|1994-03-07|1994-04-04|DELIVER IN PERSON|RAIL|integrate blithely a| +5346|192|5|2|13|14198.47|0.04|0.04|A|F|1994-02-03|1994-02-05|1994-02-09|COLLECT COD|TRUCK|y. fluffily bold accounts grow. furio| +5346|109|2|3|7|7063.70|0.08|0.05|A|F|1994-01-30|1994-03-26|1994-01-31|DELIVER IN PERSON|SHIP|equests use carefully care| +5346|162|3|4|35|37175.60|0.06|0.02|A|F|1994-02-09|1994-03-01|1994-02-14|TAKE BACK RETURN|FOB|nic excuses cajole entic| +5346|121|2|5|25|25528.00|0.05|0.06|R|F|1993-12-28|1994-03-19|1994-01-09|TAKE BACK RETURN|REG AIR|he ironic ideas are boldly slyly ironi| +5346|33|9|6|6|5598.18|0.08|0.04|R|F|1994-03-01|1994-02-04|1994-03-09|NONE|REG AIR|escapades sleep furiously beside the | +5346|80|9|7|41|40183.28|0.05|0.04|R|F|1994-01-10|1994-02-15|1994-01-26|TAKE BACK RETURN|REG AIR|fully close instructi| +5347|83|4|1|48|47187.84|0.04|0.08|A|F|1995-02-25|1995-04-26|1995-03-26|NONE|SHIP|equests are slyly. blithely regu| +5347|124|3|2|47|48133.64|0.02|0.01|N|F|1995-06-05|1995-03-29|1995-06-28|COLLECT COD|AIR|across the slyly bol| +5347|23|2|3|34|31382.68|0.06|0.00|A|F|1995-05-18|1995-04-04|1995-06-02|DELIVER IN PERSON|SHIP| pending deposits. fluffily regular senti| +5347|40|1|4|4|3760.16|0.06|0.03|A|F|1995-03-24|1995-04-03|1995-04-01|NONE|SHIP|ldly pending asymptotes ki| +5347|131|2|5|21|21653.73|0.08|0.04|R|F|1995-04-01|1995-04-16|1995-04-23|NONE|SHIP|sly slyly final requests. careful| +5347|56|1|6|6|5736.30|0.06|0.02|A|F|1995-04-11|1995-04-14|1995-05-02|NONE|TRUCK|lly unusual ideas. sl| +5347|50|7|7|18|17100.90|0.01|0.01|N|F|1995-05-24|1995-05-07|1995-06-19|NONE|FOB|he ideas among the requests | +5348|69|4|1|21|20350.26|0.10|0.04|N|O|1997-12-11|1997-12-24|1997-12-28|NONE|REG AIR| regular theodolites haggle car| +5348|156|1|2|31|32740.65|0.07|0.02|N|O|1998-01-04|1997-12-09|1998-01-17|COLLECT COD|RAIL|are finally| +5348|17|8|3|16|14672.16|0.06|0.08|N|O|1998-02-28|1997-12-25|1998-03-12|DELIVER IN PERSON|AIR|uriously thin pinto beans | +5348|20|4|4|7|6440.14|0.04|0.00|N|O|1998-01-29|1997-12-20|1998-02-10|DELIVER IN PERSON|RAIL|even foxes. epitap| +5348|2|5|5|37|33374.00|0.06|0.07|N|O|1997-12-01|1998-02-02|1997-12-07|NONE|FOB|y according to the carefully pending acco| +5348|143|10|6|14|14603.96|0.06|0.05|N|O|1997-12-16|1998-01-12|1997-12-24|COLLECT COD|FOB|en pinto beans. somas cajo| +5349|156|7|1|19|20066.85|0.06|0.01|N|O|1996-09-11|1996-11-18|1996-09-22|TAKE BACK RETURN|FOB|endencies use whithout the special | +5349|168|3|2|14|14954.24|0.06|0.00|N|O|1996-11-07|1996-11-17|1996-11-20|TAKE BACK RETURN|TRUCK|fully regular | +5349|4|5|3|6|5424.00|0.10|0.01|N|O|1996-12-30|1996-10-08|1997-01-01|DELIVER IN PERSON|MAIL|inal deposits affix carefully| +5350|122|3|1|19|19420.28|0.02|0.06|R|F|1993-10-20|1993-11-15|1993-11-17|DELIVER IN PERSON|RAIL|romise slyly alongsi| +5350|191|4|2|44|48012.36|0.04|0.06|R|F|1993-10-30|1993-11-23|1993-11-25|DELIVER IN PERSON|AIR|p above the ironic, pending dep| +5350|54|9|3|12|11448.60|0.10|0.04|A|F|1994-01-30|1993-11-21|1994-02-15|COLLECT COD|REG AIR| cajole. even instructions haggle. blithe| +5350|155|10|4|7|7386.05|0.08|0.00|R|F|1993-10-19|1993-12-28|1993-11-04|NONE|SHIP|alongside of th| +5350|129|10|5|27|27786.24|0.07|0.04|A|F|1993-11-25|1993-12-27|1993-12-08|COLLECT COD|TRUCK|es. blithe theodolites haggl| +5351|7|2|1|36|32652.00|0.06|0.05|N|O|1998-07-27|1998-07-06|1998-08-25|NONE|MAIL|ss the ironic, regular asymptotes cajole | +5351|33|9|2|47|43852.41|0.04|0.01|N|O|1998-05-30|1998-08-08|1998-06-23|DELIVER IN PERSON|REG AIR|s. grouches cajole. sile| +5351|106|3|3|2|2012.20|0.00|0.02|N|O|1998-05-12|1998-07-15|1998-05-24|NONE|TRUCK|g accounts wake furiously slyly even dolph| +5376|61|6|1|42|40364.52|0.10|0.04|A|F|1994-09-20|1994-08-30|1994-09-29|TAKE BACK RETURN|REG AIR|y even asymptotes. courts are unusual pa| +5376|91|4|2|44|43607.96|0.05|0.02|R|F|1994-08-30|1994-08-05|1994-09-07|COLLECT COD|AIR|ithe packages detect final theodolites. f| +5376|65|6|3|18|17371.08|0.02|0.08|A|F|1994-10-29|1994-09-13|1994-11-01|COLLECT COD|MAIL| accounts boo| +5377|79|8|1|40|39162.80|0.00|0.04|N|O|1997-05-21|1997-06-15|1997-05-26|DELIVER IN PERSON|AIR|lithely ironic theodolites are care| +5377|30|3|2|17|15810.51|0.09|0.00|N|O|1997-07-05|1997-05-25|1997-07-22|COLLECT COD|RAIL|dencies. carefully regular re| +5377|103|8|3|23|23071.30|0.07|0.08|N|O|1997-06-26|1997-07-13|1997-07-08|COLLECT COD|RAIL| silent wa| +5377|104|7|4|12|12049.20|0.05|0.07|N|O|1997-05-08|1997-06-15|1997-05-15|DELIVER IN PERSON|MAIL| ironic, final| +5377|173|3|5|27|28975.59|0.08|0.02|N|O|1997-07-11|1997-06-12|1997-08-08|TAKE BACK RETURN|MAIL|press theodolites. e| +5378|155|3|1|39|41150.85|0.07|0.04|R|F|1992-11-25|1992-12-22|1992-12-02|COLLECT COD|AIR|ts are quickly around the| +5378|62|9|2|46|44254.76|0.01|0.04|A|F|1993-02-17|1993-01-20|1993-02-26|COLLECT COD|REG AIR|into beans sleep. fu| +5378|10|7|3|18|16380.18|0.02|0.03|R|F|1992-11-25|1992-12-21|1992-12-10|COLLECT COD|FOB|onic accounts was bold, | +5379|199|1|1|40|43967.60|0.01|0.08|N|O|1995-10-01|1995-10-19|1995-10-30|COLLECT COD|MAIL|carefully final accounts haggle blithely. | +5380|182|3|1|14|15150.52|0.10|0.01|N|O|1997-12-18|1997-12-03|1998-01-06|NONE|RAIL|final platelets.| +5380|147|6|2|10|10471.40|0.09|0.05|N|O|1997-11-24|1998-01-10|1997-12-21|COLLECT COD|AIR|refully pending deposits. special, even t| +5380|184|5|3|40|43367.20|0.02|0.08|N|O|1997-12-30|1997-11-27|1998-01-09|DELIVER IN PERSON|SHIP|ar asymptotes. blithely r| +5380|66|3|4|6|5796.36|0.09|0.05|N|O|1997-11-15|1998-01-08|1997-12-11|COLLECT COD|MAIL|es. fluffily brave accounts across t| +5380|107|8|5|48|48340.80|0.04|0.03|N|O|1997-12-01|1997-12-28|1997-12-05|DELIVER IN PERSON|FOB|encies haggle car| +5381|188|9|1|37|40262.66|0.04|0.01|A|F|1993-04-08|1993-04-07|1993-04-12|DELIVER IN PERSON|SHIP|ly final deposits print carefully. unusua| +5381|111|8|2|48|48533.28|0.04|0.03|R|F|1993-04-22|1993-04-17|1993-05-14|TAKE BACK RETURN|FOB|luffily spec| +5381|192|3|3|13|14198.47|0.08|0.03|R|F|1993-05-09|1993-04-26|1993-05-25|NONE|FOB|s after the f| +5381|168|3|4|17|18158.72|0.05|0.05|R|F|1993-05-25|1993-04-14|1993-06-17|NONE|MAIL|ckly final requests haggle qui| +5381|63|8|5|49|47189.94|0.06|0.02|R|F|1993-05-08|1993-04-07|1993-06-03|NONE|FOB| accounts. regular, regula| +5381|132|3|6|33|34060.29|0.10|0.00|A|F|1993-04-09|1993-04-03|1993-04-22|DELIVER IN PERSON|SHIP|ly special deposits | +5381|44|3|7|31|29265.24|0.04|0.05|A|F|1993-04-10|1993-03-22|1993-04-13|TAKE BACK RETURN|MAIL|the carefully expre| +5382|153|8|1|34|35807.10|0.03|0.03|R|F|1992-02-22|1992-02-18|1992-03-02|DELIVER IN PERSON|FOB|gular accounts. even accounts integrate| +5382|55|3|2|13|12415.65|0.09|0.06|A|F|1992-01-16|1992-03-12|1992-02-06|NONE|MAIL|eodolites. final foxes | +5382|149|10|3|3|3147.42|0.10|0.06|A|F|1992-03-22|1992-03-06|1992-04-19|TAKE BACK RETURN|AIR|efully unusua| +5382|62|9|4|20|19241.20|0.08|0.02|A|F|1992-03-26|1992-02-17|1992-04-15|DELIVER IN PERSON|FOB|carefully regular accounts. slyly ev| +5382|177|8|5|14|15080.38|0.02|0.02|A|F|1992-04-05|1992-04-05|1992-05-04|TAKE BACK RETURN|FOB| brave platelets. ev| +5382|180|9|6|6|6481.08|0.02|0.01|A|F|1992-03-07|1992-04-02|1992-03-18|TAKE BACK RETURN|FOB|y final foxes by the sl| +5382|105|2|7|48|48244.80|0.05|0.05|A|F|1992-02-14|1992-03-19|1992-02-25|DELIVER IN PERSON|REG AIR|nts integrate quickly ca| +5383|96|7|1|12|11953.08|0.04|0.00|N|O|1995-07-02|1995-08-16|1995-08-01|TAKE BACK RETURN|AIR|y regular instructi| +5408|102|7|1|2|2004.20|0.07|0.04|R|F|1992-08-21|1992-10-03|1992-08-28|DELIVER IN PERSON|MAIL|cross the dolphins h| +5408|118|2|2|35|35633.85|0.04|0.05|R|F|1992-10-02|1992-10-17|1992-10-13|TAKE BACK RETURN|AIR|thely ironic requests alongside of the sl| +5408|76|6|3|34|33186.38|0.10|0.02|A|F|1992-10-22|1992-08-25|1992-11-16|DELIVER IN PERSON|TRUCK|requests detect blithely a| +5408|54|2|4|48|45794.40|0.04|0.05|R|F|1992-09-30|1992-08-27|1992-10-27|NONE|TRUCK|. furiously regular | +5408|183|4|5|8|8665.44|0.03|0.07|A|F|1992-10-24|1992-09-06|1992-11-03|NONE|AIR|thely regular hocke| +5409|194|8|1|27|29543.13|0.01|0.02|A|F|1992-02-14|1992-03-18|1992-02-23|DELIVER IN PERSON|AIR|eodolites | +5409|104|5|2|38|38155.80|0.01|0.02|A|F|1992-03-17|1992-03-29|1992-04-13|NONE|REG AIR|onic, regular accounts! blithely even| +5409|141|10|3|17|17699.38|0.07|0.00|A|F|1992-01-13|1992-04-05|1992-01-20|DELIVER IN PERSON|AIR|cross the sil| +5409|1|8|4|9|8109.00|0.07|0.03|A|F|1992-02-15|1992-04-02|1992-02-28|DELIVER IN PERSON|AIR| unusual, unusual reques| +5409|159|10|5|37|39188.55|0.06|0.04|R|F|1992-05-07|1992-02-10|1992-05-20|DELIVER IN PERSON|FOB|ously regular packages. packages| +5409|64|3|6|14|13496.84|0.03|0.08|R|F|1992-02-14|1992-03-26|1992-02-29|DELIVER IN PERSON|AIR|osits cajole furiously| +5410|117|8|1|48|48821.28|0.04|0.08|N|O|1998-09-27|1998-09-11|1998-10-01|TAKE BACK RETURN|AIR| about the slyly even courts. quickly regul| +5410|105|8|2|41|41209.10|0.01|0.07|N|O|1998-08-25|1998-10-20|1998-09-01|DELIVER IN PERSON|REG AIR|sly. slyly ironic theodolites| +5410|29|4|3|40|37160.80|0.07|0.08|N|O|1998-11-17|1998-10-02|1998-11-27|COLLECT COD|TRUCK|iously special accounts are along th| +5410|50|7|4|8|7600.40|0.05|0.04|N|O|1998-09-12|1998-10-22|1998-09-22|DELIVER IN PERSON|TRUCK|ly. fluffily ironic platelets alon| +5411|96|9|1|17|16933.53|0.05|0.01|N|O|1997-07-22|1997-07-14|1997-07-30|TAKE BACK RETURN|REG AIR| slyly slyly even deposits. carefully b| +5411|113|7|2|10|10131.10|0.08|0.01|N|O|1997-07-19|1997-08-04|1997-07-26|TAKE BACK RETURN|MAIL|nding, special foxes unw| +5411|56|7|3|5|4780.25|0.10|0.01|N|O|1997-09-12|1997-08-03|1997-09-23|DELIVER IN PERSON|FOB| bold, ironic theodo| +5411|129|8|4|15|15436.80|0.08|0.05|N|O|1997-07-01|1997-07-15|1997-07-07|COLLECT COD|RAIL|attainments sleep slyly ironic| +5411|4|5|5|19|17176.00|0.05|0.08|N|O|1997-05-25|1997-07-30|1997-06-19|COLLECT COD|RAIL|ial accounts according to the f| +5412|54|9|1|2|1908.10|0.03|0.07|N|O|1998-04-14|1998-04-02|1998-04-19|TAKE BACK RETURN|REG AIR| sleep above the furiou| +5412|66|1|2|48|46370.88|0.01|0.08|N|O|1998-02-22|1998-03-28|1998-03-18|TAKE BACK RETURN|TRUCK|s. slyly final packages cajole blithe| +5412|74|2|3|31|30196.17|0.05|0.08|N|O|1998-03-23|1998-04-17|1998-04-10|NONE|SHIP|t the accounts detect slyly about the c| +5412|97|10|4|26|25924.34|0.02|0.08|N|O|1998-01-22|1998-04-19|1998-02-17|NONE|AIR| the blithel| +5413|126|7|1|48|49253.76|0.02|0.08|N|O|1998-01-25|1997-11-20|1998-02-22|COLLECT COD|SHIP| theodolites. furiously ironic instr| +5413|142|9|2|37|38559.18|0.02|0.07|N|O|1997-12-08|1998-01-01|1997-12-13|COLLECT COD|TRUCK|usly bold instructions affix idly unusual, | +5413|111|8|3|36|36399.96|0.02|0.07|N|O|1997-12-12|1997-11-28|1997-12-25|NONE|TRUCK|ular, regular ideas mold! final requests| +5413|110|3|4|22|22222.42|0.02|0.08|N|O|1997-11-10|1997-11-24|1997-11-22|DELIVER IN PERSON|FOB|posits. quick| +5413|189|10|5|5|5445.90|0.10|0.01|N|O|1997-11-28|1997-11-24|1997-12-05|NONE|RAIL|tes are al| +5413|190|1|6|32|34886.08|0.02|0.03|N|O|1997-10-28|1998-01-03|1997-11-10|NONE|TRUCK|refully special package| +5413|31|7|7|32|29792.96|0.06|0.07|N|O|1997-10-23|1997-12-09|1997-11-17|NONE|TRUCK|he quickly ironic ideas. slyly ironic ide| +5414|68|9|1|40|38722.40|0.07|0.06|R|F|1993-04-07|1993-05-18|1993-04-23|COLLECT COD|AIR|ts are evenly across| +5414|123|8|2|48|49109.76|0.06|0.07|R|F|1993-06-08|1993-05-14|1993-07-06|DELIVER IN PERSON|FOB| silent dolphins; fluffily regular tithe| +5414|35|1|3|23|21505.69|0.10|0.00|A|F|1993-07-22|1993-05-26|1993-08-08|COLLECT COD|MAIL|e bold, express dolphins. spec| +5414|133|4|4|15|15496.95|0.06|0.08|R|F|1993-05-18|1993-06-09|1993-05-27|DELIVER IN PERSON|REG AIR|e slyly about the carefully regula| +5414|9|2|5|19|17271.00|0.01|0.05|R|F|1993-04-06|1993-05-12|1993-05-02|DELIVER IN PERSON|RAIL|ffily silent theodolites na| +5414|98|1|6|28|27946.52|0.10|0.05|A|F|1993-03-27|1993-06-04|1993-04-07|TAKE BACK RETURN|SHIP|ts sleep sl| +5415|102|5|1|44|44092.40|0.00|0.06|A|F|1992-08-19|1992-10-26|1992-09-17|TAKE BACK RETURN|TRUCK| requests. unusual theodolites sleep agains| +5415|31|7|2|16|14896.48|0.08|0.00|A|F|1992-09-29|1992-09-12|1992-10-10|NONE|AIR|pinto beans haggle furiously| +5415|102|7|3|6|6012.60|0.10|0.03|A|F|1992-10-28|1992-09-09|1992-11-20|COLLECT COD|RAIL|ges around the fur| +5415|16|7|4|43|39388.43|0.01|0.02|R|F|1992-11-17|1992-09-14|1992-12-14|DELIVER IN PERSON|SHIP|yly blithely stealthy deposits. carefu| +5415|161|6|5|11|11672.76|0.00|0.01|R|F|1992-11-22|1992-10-19|1992-12-10|DELIVER IN PERSON|SHIP|gle among t| +5415|144|1|6|46|48030.44|0.03|0.03|R|F|1992-08-25|1992-09-10|1992-09-22|DELIVER IN PERSON|REG AIR|ve the fluffily | +5415|153|4|7|11|11584.65|0.08|0.06|A|F|1992-08-21|1992-09-04|1992-08-23|NONE|TRUCK|unts maintain carefully unusual| +5440|115|2|1|3|3045.33|0.02|0.08|N|O|1997-02-18|1997-02-28|1997-03-15|NONE|SHIP|y. accounts haggle along the blit| +5441|164|1|1|3|3192.48|0.00|0.02|R|F|1994-08-12|1994-10-14|1994-09-01|TAKE BACK RETURN|REG AIR|are. unusual, | +5441|131|2|2|49|50525.37|0.02|0.03|A|F|1994-09-23|1994-09-22|1994-10-22|NONE|FOB|ording to the furio| +5441|144|3|3|33|34456.62|0.09|0.02|R|F|1994-10-09|1994-10-06|1994-10-30|DELIVER IN PERSON|TRUCK|ges. final instruction| +5441|67|4|4|47|45451.82|0.07|0.08|R|F|1994-11-19|1994-10-16|1994-12-16|TAKE BACK RETURN|FOB|ounts wake slyly about the express instr| +5442|42|5|1|16|15072.64|0.00|0.00|N|O|1998-04-12|1998-03-03|1998-05-04|TAKE BACK RETURN|RAIL|r packages. accounts haggle dependencies. f| +5442|88|9|2|45|44463.60|0.08|0.01|N|O|1998-03-30|1998-02-24|1998-04-18|TAKE BACK RETURN|AIR|old slyly after | +5442|61|8|3|12|11532.72|0.01|0.08|N|O|1998-04-15|1998-03-18|1998-05-05|DELIVER IN PERSON|TRUCK|fully final| +5442|158|9|4|21|22221.15|0.07|0.06|N|O|1998-03-13|1998-02-19|1998-04-06|COLLECT COD|MAIL|ffily furiously ironic theodolites. furio| +5442|16|7|5|25|22900.25|0.04|0.00|N|O|1998-03-29|1998-02-13|1998-04-13|TAKE BACK RETURN|REG AIR|ake furiously. slyly express th| +5442|144|3|6|26|27147.64|0.08|0.07|N|O|1998-03-21|1998-03-21|1998-03-25|TAKE BACK RETURN|AIR|have to sleep furiously bold ideas. blith| +5443|178|9|1|14|15094.38|0.02|0.00|N|O|1996-10-27|1996-11-11|1996-11-21|DELIVER IN PERSON|RAIL|s after the regular, regular deposits hag| +5443|72|3|2|39|37910.73|0.03|0.07|N|O|1996-11-01|1996-11-30|1996-11-19|NONE|RAIL|gage carefully across the furiously| +5443|160|5|3|25|26504.00|0.05|0.00|N|O|1996-12-07|1997-01-08|1997-01-05|NONE|FOB|use carefully above the pinto bea| +5443|191|4|4|6|6547.14|0.05|0.02|N|O|1996-11-17|1996-12-03|1996-11-30|TAKE BACK RETURN|AIR|p fluffily foxe| +5443|83|4|5|40|39323.20|0.03|0.03|N|O|1997-01-28|1996-12-10|1997-02-13|NONE|FOB|n courts. special re| +5444|186|7|1|21|22809.78|0.01|0.07|A|F|1995-04-11|1995-04-25|1995-04-21|DELIVER IN PERSON|RAIL|ar packages haggle above th| +5444|43|6|2|40|37721.60|0.05|0.08|N|O|1995-07-09|1995-04-25|1995-07-19|COLLECT COD|TRUCK|ously bold ideas. instructions wake slyl| +5444|150|9|3|40|42006.00|0.08|0.01|A|F|1995-04-06|1995-05-08|1995-05-06|DELIVER IN PERSON|AIR| even packages.| +5444|59|4|4|33|31648.65|0.05|0.04|N|O|1995-06-24|1995-04-24|1995-07-13|DELIVER IN PERSON|SHIP|ut the courts cajole blithely excuses| +5444|171|9|5|21|22494.57|0.04|0.00|R|F|1995-05-05|1995-05-25|1995-05-29|TAKE BACK RETURN|REG AIR|aves serve sly| +5444|20|7|6|21|19320.42|0.07|0.01|A|F|1995-03-30|1995-05-01|1995-03-31|COLLECT COD|AIR|furiously even theodolites.| +5445|90|1|1|33|32672.97|0.08|0.07|A|F|1993-10-21|1993-10-14|1993-10-28|DELIVER IN PERSON|REG AIR|ests. final instructions| +5445|131|2|2|12|12373.56|0.09|0.08|R|F|1993-11-02|1993-09-05|1993-11-26|COLLECT COD|FOB| slyly pending pinto beans was slyly al| +5445|103|8|3|46|46142.60|0.04|0.07|A|F|1993-10-06|1993-09-15|1993-10-28|DELIVER IN PERSON|RAIL|old depend| +5445|149|10|4|10|10491.40|0.08|0.06|A|F|1993-09-16|1993-10-05|1993-10-01|NONE|TRUCK|ncies abou| +5445|13|10|5|14|12782.14|0.00|0.02|R|F|1993-11-19|1993-10-18|1993-12-07|NONE|RAIL| requests. bravely i| +5446|190|1|1|27|29435.13|0.05|0.07|R|F|1994-07-21|1994-08-25|1994-08-17|TAKE BACK RETURN|RAIL|ously across the quic| +5447|99|1|1|31|30971.79|0.09|0.03|N|O|1996-07-14|1996-05-07|1996-07-17|COLLECT COD|SHIP| foxes sleep. blithely unusual accounts det| +5472|59|10|1|27|25894.35|0.09|0.06|A|F|1993-08-04|1993-07-07|1993-09-03|COLLECT COD|TRUCK|fily pending attainments. unus| +5472|68|3|2|28|27105.68|0.00|0.03|A|F|1993-07-28|1993-05-28|1993-08-11|TAKE BACK RETURN|FOB|ffily pendin| +5472|178|7|3|45|48517.65|0.06|0.02|R|F|1993-06-05|1993-05-14|1993-06-10|NONE|TRUCK| idle packages. furi| +5472|184|5|4|37|40114.66|0.07|0.05|R|F|1993-06-15|1993-07-03|1993-07-09|DELIVER IN PERSON|RAIL|egrate carefully dependencies. | +5472|75|6|5|40|39002.80|0.02|0.05|A|F|1993-04-13|1993-07-04|1993-05-04|NONE|REG AIR|e requests detect furiously. ruthlessly un| +5472|167|2|6|39|41619.24|0.02|0.03|R|F|1993-04-18|1993-07-10|1993-05-12|TAKE BACK RETURN|MAIL|uriously carefully | +5472|15|5|7|1|915.01|0.03|0.02|A|F|1993-04-14|1993-06-28|1993-04-16|NONE|RAIL|s use furiou| +5473|48|5|1|9|8532.36|0.03|0.07|R|F|1992-06-03|1992-05-30|1992-06-09|TAKE BACK RETURN|AIR| excuses sleep blithely! regular dep| +5473|70|1|2|27|26191.89|0.01|0.03|A|F|1992-04-06|1992-04-26|1992-04-29|TAKE BACK RETURN|MAIL|the deposits. warthogs wake fur| +5473|15|5|3|33|30195.33|0.09|0.00|R|F|1992-05-18|1992-06-10|1992-06-13|TAKE BACK RETURN|MAIL|efully above the even, | +5474|184|5|1|38|41198.84|0.01|0.08|A|F|1992-07-15|1992-07-16|1992-07-20|NONE|REG AIR| slyly beneath | +5474|94|8|2|10|9940.90|0.06|0.00|R|F|1992-08-08|1992-08-10|1992-08-24|TAKE BACK RETURN|TRUCK|pinto bean| +5474|48|1|3|31|29389.24|0.00|0.08|R|F|1992-08-02|1992-07-12|1992-08-04|NONE|TRUCK|the furiously express ideas. speci| +5474|90|1|4|46|45544.14|0.03|0.04|A|F|1992-06-07|1992-07-11|1992-06-22|NONE|TRUCK|nstructions. furio| +5475|183|4|1|10|10831.80|0.09|0.08|N|O|1996-07-19|1996-08-22|1996-07-23|COLLECT COD|AIR|ding to the deposits wake fina| +5476|48|1|1|13|12324.52|0.01|0.04|N|O|1997-12-27|1997-12-08|1997-12-29|COLLECT COD|TRUCK|iously special ac| +5476|20|4|2|17|15640.34|0.10|0.01|N|O|1998-02-02|1998-01-28|1998-02-14|COLLECT COD|FOB|ng dependencies until the f| +5477|80|8|1|20|19601.60|0.03|0.01|N|O|1998-03-21|1998-02-09|1998-04-07|TAKE BACK RETURN|SHIP|platelets about the ironic| +5477|77|7|2|21|20518.47|0.03|0.00|N|O|1998-01-28|1998-02-15|1998-02-24|TAKE BACK RETURN|SHIP|blate slyly. silent| +5477|134|5|3|31|32058.03|0.04|0.01|N|O|1998-01-11|1998-01-30|1998-02-04|DELIVER IN PERSON|MAIL| special Tiresias cajole furiously. pending| +5477|193|6|4|16|17491.04|0.00|0.01|N|O|1998-03-07|1998-03-12|1998-04-06|COLLECT COD|RAIL|regular, s| +5477|96|9|5|23|22910.07|0.00|0.06|N|O|1998-01-04|1998-02-23|1998-01-24|NONE|REG AIR|telets wake blithely ab| +5477|121|6|6|19|19401.28|0.10|0.03|N|O|1998-02-03|1998-01-30|1998-03-04|TAKE BACK RETURN|MAIL|ost carefully packages.| +5478|8|9|1|39|35412.00|0.09|0.06|N|O|1996-08-19|1996-06-25|1996-09-08|DELIVER IN PERSON|SHIP|s. furiously | +5478|2|5|2|47|42394.00|0.10|0.01|N|O|1996-08-15|1996-07-12|1996-08-31|NONE|RAIL| instructions; slyly even accounts hagg| +5478|119|3|3|25|25477.75|0.09|0.07|N|O|1996-06-08|1996-07-12|1996-07-07|NONE|TRUCK|unusual, pending requests haggle accoun| +5479|138|4|1|50|51906.50|0.02|0.02|A|F|1993-12-24|1994-02-14|1994-01-18|DELIVER IN PERSON|MAIL|ironic gifts. even dependencies sno| +5479|104|5|2|19|19077.90|0.05|0.03|A|F|1994-01-22|1994-03-07|1994-02-11|DELIVER IN PERSON|SHIP|arefully bo| +5504|68|5|1|4|3872.24|0.10|0.07|A|F|1993-04-30|1993-03-01|1993-05-22|DELIVER IN PERSON|AIR|into beans boost. | +5504|177|8|2|7|7540.19|0.03|0.05|R|F|1993-04-25|1993-03-15|1993-05-06|NONE|TRUCK|packages detect furiously express reques| +5504|160|2|3|29|30744.64|0.05|0.03|A|F|1993-01-28|1993-02-13|1993-02-27|NONE|SHIP|ajole carefully. care| +5505|25|8|1|43|39775.86|0.07|0.01|N|O|1997-12-30|1997-11-28|1998-01-09|TAKE BACK RETURN|TRUCK|y alongside of the special requests.| +5505|182|3|2|33|35711.94|0.05|0.08|N|O|1998-01-11|1997-11-11|1998-01-30|TAKE BACK RETURN|AIR|ithely unusual excuses integrat| +5505|155|10|3|10|10551.50|0.06|0.01|N|O|1997-10-28|1997-11-27|1997-10-29|DELIVER IN PERSON|AIR| furiously special asym| +5505|40|1|4|18|16920.72|0.04|0.04|N|O|1997-10-25|1997-12-12|1997-10-30|TAKE BACK RETURN|RAIL| to the quickly express pac| +5505|162|9|5|46|48859.36|0.05|0.00|N|O|1998-01-06|1997-11-04|1998-02-04|TAKE BACK RETURN|SHIP|usly ironic dependencies haggle across | +5506|140|1|1|2|2080.28|0.00|0.03|R|F|1994-02-04|1994-01-13|1994-02-17|COLLECT COD|MAIL|onic theodolites are fluffil| +5506|160|1|2|6|6360.96|0.07|0.06|R|F|1994-02-21|1994-01-30|1994-02-27|DELIVER IN PERSON|MAIL|hely according to the furiously unusua| +5507|10|5|1|23|20930.23|0.05|0.04|N|O|1998-09-04|1998-07-04|1998-09-18|TAKE BACK RETURN|AIR|ously slow packages poach whithout the| +5507|138|9|2|48|49830.24|0.03|0.01|N|O|1998-08-03|1998-08-10|1998-08-24|DELIVER IN PERSON|AIR|yly idle deposits. final, final fox| +5507|45|2|3|4|3780.16|0.04|0.06|N|O|1998-06-06|1998-07-02|1998-06-27|TAKE BACK RETURN|RAIL|into beans are| +5507|67|6|4|22|21275.32|0.07|0.01|N|O|1998-07-08|1998-08-10|1998-07-22|DELIVER IN PERSON|TRUCK|gular ideas. carefully unu| +5507|132|3|5|48|49542.24|0.06|0.01|N|O|1998-07-21|1998-07-15|1998-07-31|DELIVER IN PERSON|SHIP|uriously regular acc| +5508|117|7|1|4|4068.44|0.10|0.04|N|O|1996-09-01|1996-08-02|1996-09-17|COLLECT COD|AIR|fluffily about the even | +5509|197|10|1|3|3291.57|0.03|0.02|A|F|1994-06-14|1994-05-11|1994-06-17|NONE|SHIP| quickly fin| +5509|99|3|2|17|16984.53|0.03|0.07|R|F|1994-07-01|1994-06-30|1994-07-31|COLLECT COD|AIR|ccounts wake ar| +5509|93|7|3|30|29792.70|0.04|0.04|A|F|1994-07-23|1994-06-01|1994-08-08|NONE|AIR|counts haggle pinto beans. furiously | +5509|100|3|4|45|45004.50|0.00|0.07|A|F|1994-07-24|1994-05-28|1994-08-20|COLLECT COD|AIR|counts sleep. f| +5509|156|8|5|35|36965.25|0.04|0.03|A|F|1994-04-17|1994-06-29|1994-04-24|COLLECT COD|RAIL|c accounts. ca| +5510|16|6|1|8|7328.08|0.01|0.01|A|F|1993-03-16|1993-03-29|1993-03-24|DELIVER IN PERSON|FOB|n packages boost sly| +5510|20|10|2|46|42320.92|0.02|0.07|A|F|1993-03-12|1993-02-09|1993-03-19|NONE|TRUCK|silent packages cajole doggedly regular | +5510|162|3|3|47|49921.52|0.03|0.01|A|F|1993-01-20|1993-03-25|1993-02-15|DELIVER IN PERSON|SHIP|riously even requests. slyly bold accou| +5510|24|7|4|29|26796.58|0.09|0.08|A|F|1993-02-28|1993-03-28|1993-03-12|COLLECT COD|AIR|lithely fluffily ironic req| +5511|165|4|1|16|17042.56|0.10|0.05|A|F|1995-02-02|1995-01-06|1995-02-19|TAKE BACK RETURN|RAIL|thely bold theodolites | +5511|165|10|2|31|33019.96|0.09|0.01|A|F|1995-02-23|1995-01-21|1995-03-02|COLLECT COD|REG AIR|gular excuses. fluffily even pinto beans c| +5511|128|3|3|49|50377.88|0.05|0.05|R|F|1994-12-21|1995-01-27|1994-12-26|NONE|REG AIR|bout the requests. theodolites | +5511|122|7|4|4|4088.48|0.08|0.02|R|F|1994-12-28|1995-01-16|1995-01-24|TAKE BACK RETURN|RAIL|lphins. carefully blithe de| +5511|9|2|5|23|20907.00|0.10|0.07|A|F|1995-03-11|1995-01-21|1995-03-27|TAKE BACK RETURN|TRUCK|ing dugouts | +5511|188|9|6|5|5440.90|0.08|0.05|R|F|1994-12-29|1995-01-16|1995-01-24|DELIVER IN PERSON|MAIL|al theodolites. blithely final de| +5511|143|2|7|23|23992.22|0.02|0.07|R|F|1995-02-03|1995-01-05|1995-02-18|COLLECT COD|REG AIR|ully deposits. warthogs hagg| +5536|90|1|1|14|13861.26|0.08|0.06|N|O|1998-05-18|1998-05-08|1998-06-05|COLLECT COD|MAIL|instructions sleep | +5536|62|1|2|20|19241.20|0.08|0.04|N|O|1998-05-08|1998-05-10|1998-05-31|DELIVER IN PERSON|REG AIR|equests mo| +5536|197|9|3|35|38401.65|0.07|0.02|N|O|1998-05-19|1998-06-08|1998-06-05|NONE|MAIL|c, final theo| +5536|9|10|4|30|27270.00|0.05|0.07|N|O|1998-04-15|1998-05-23|1998-05-03|NONE|FOB|arefully regular theodolites according| +5536|141|2|5|11|11452.54|0.02|0.08|N|O|1998-03-18|1998-05-12|1998-03-28|TAKE BACK RETURN|FOB| snooze furio| +5537|45|8|1|10|9450.40|0.05|0.08|N|O|1997-01-13|1996-12-25|1997-01-28|TAKE BACK RETURN|AIR| sleep carefully slyly bold depos| +5537|150|9|2|15|15752.25|0.07|0.04|N|O|1997-01-13|1996-12-25|1997-01-27|COLLECT COD|AIR|eposits. permanently pending packag| +5537|151|6|3|39|40994.85|0.03|0.00|N|O|1996-12-17|1996-11-08|1997-01-15|COLLECT COD|REG AIR| slyly bold packages are. qu| +5537|97|1|4|38|37889.42|0.01|0.00|N|O|1996-11-06|1996-11-23|1996-11-12|TAKE BACK RETURN|MAIL|s above the carefully ironic deposits | +5538|154|9|1|42|44274.30|0.05|0.00|A|F|1994-04-08|1994-03-17|1994-05-05|DELIVER IN PERSON|REG AIR|vely ironic accounts. furiously unusual acc| +5538|121|2|2|4|4084.48|0.02|0.03|R|F|1994-03-21|1994-02-17|1994-04-11|TAKE BACK RETURN|REG AIR|ithely along the c| +5538|19|3|3|38|34922.38|0.03|0.06|R|F|1994-03-17|1994-02-11|1994-04-10|TAKE BACK RETURN|FOB|ular pinto beans. silent ideas above | +5538|78|6|4|9|8802.63|0.00|0.01|R|F|1993-12-26|1994-01-31|1994-01-03|TAKE BACK RETURN|REG AIR|encies across the blithely fina| +5539|65|10|1|42|40532.52|0.10|0.08|A|F|1994-09-29|1994-09-17|1994-10-20|DELIVER IN PERSON|RAIL|ons across the carefully si| +5540|181|2|1|42|45409.56|0.02|0.08|N|O|1996-11-12|1996-12-18|1996-12-05|TAKE BACK RETURN|RAIL|ss dolphins haggle | +5540|102|3|2|2|2004.20|0.06|0.02|N|O|1996-12-12|1997-01-09|1996-12-25|DELIVER IN PERSON|MAIL|nic asymptotes could hav| +5540|64|3|3|19|18317.14|0.01|0.03|N|O|1997-02-06|1996-11-18|1997-02-20|DELIVER IN PERSON|SHIP| slyly slyl| +5540|72|10|4|24|23329.68|0.10|0.05|N|O|1997-01-09|1996-12-02|1997-01-23|COLLECT COD|FOB|deposits! ironic depths may engage-- b| +5541|96|8|1|39|38847.51|0.08|0.05|N|O|1997-11-17|1997-12-27|1997-12-11|TAKE BACK RETURN|RAIL|ding theodolites haggle against the slyly | +5542|189|10|1|6|6535.08|0.03|0.01|N|O|1996-06-14|1996-05-28|1996-07-11|DELIVER IN PERSON|TRUCK| foxes doubt. theodolites ca| +5543|143|10|1|14|14603.96|0.02|0.03|R|F|1993-10-09|1993-12-09|1993-10-21|NONE|SHIP|ecial reque| +5543|162|7|2|22|23367.52|0.04|0.00|A|F|1993-11-06|1993-11-02|1993-12-02|DELIVER IN PERSON|SHIP|instructions. deposits use quickly. ir| +5543|67|6|3|3|2901.18|0.08|0.05|R|F|1993-11-18|1993-11-05|1993-12-17|NONE|FOB|ress, even | +5543|147|10|4|8|8377.12|0.05|0.01|R|F|1993-10-28|1993-11-18|1993-11-07|NONE|SHIP|totes? iron| +5543|80|1|5|32|31362.56|0.03|0.03|R|F|1993-10-04|1993-11-14|1993-11-03|DELIVER IN PERSON|AIR|ully around the | +5543|184|5|6|1|1084.18|0.03|0.07|A|F|1993-10-29|1993-11-11|1993-11-23|TAKE BACK RETURN|FOB|uriously. slyly| +5543|129|8|7|39|40135.68|0.06|0.00|R|F|1993-10-07|1993-11-15|1993-10-28|TAKE BACK RETURN|MAIL|l excuses are furiously. slyly unusual requ| +5568|166|5|1|50|53308.00|0.05|0.05|N|O|1995-07-14|1995-09-04|1995-08-03|COLLECT COD|TRUCK|furious ide| +5568|44|5|2|18|16992.72|0.01|0.08|N|O|1995-08-19|1995-08-18|1995-08-24|DELIVER IN PERSON|SHIP|structions haggle. carefully regular | +5568|89|10|3|35|34617.80|0.08|0.07|N|O|1995-09-17|1995-09-04|1995-10-14|NONE|SHIP|lyly. blit| +5569|29|4|1|25|23225.50|0.10|0.03|R|F|1993-06-29|1993-07-18|1993-07-05|TAKE BACK RETURN|TRUCK| deposits cajole above| +5569|58|10|2|26|24909.30|0.09|0.06|A|F|1993-08-21|1993-07-22|1993-09-09|DELIVER IN PERSON|MAIL|pitaphs. ironic req| +5569|55|3|3|48|45842.40|0.02|0.03|R|F|1993-06-16|1993-06-15|1993-07-09|COLLECT COD|SHIP|the fluffily| +5569|147|10|4|19|19895.66|0.10|0.08|R|F|1993-07-30|1993-06-21|1993-08-13|TAKE BACK RETURN|FOB| detect ca| +5569|59|1|5|15|14385.75|0.02|0.06|A|F|1993-06-29|1993-07-06|1993-07-05|DELIVER IN PERSON|MAIL|lithely bold requests boost fur| +5570|161|6|1|37|39262.92|0.08|0.02|N|O|1996-08-29|1996-10-23|1996-09-11|NONE|RAIL|y ironic pin| +5570|39|10|2|15|14085.45|0.09|0.02|N|O|1996-10-04|1996-10-05|1996-10-28|TAKE BACK RETURN|REG AIR|beans nag slyly special, regular pack| +5570|60|1|3|29|27841.74|0.02|0.05|N|O|1996-10-12|1996-10-20|1996-11-08|TAKE BACK RETURN|SHIP|he silent, enticing requests.| +5571|154|2|1|32|33732.80|0.05|0.01|R|F|1992-12-25|1993-03-01|1993-01-23|NONE|FOB| the blithely even packages nag q| +5571|94|8|2|31|30816.79|0.09|0.07|R|F|1993-01-05|1993-01-18|1993-02-04|DELIVER IN PERSON|SHIP|uffily even accounts. quickly re| +5571|92|6|3|18|17857.62|0.10|0.05|R|F|1993-03-11|1993-02-28|1993-04-03|COLLECT COD|REG AIR|uests haggle furiously pending d| +5572|22|1|1|24|22128.48|0.08|0.08|R|F|1994-10-30|1994-10-02|1994-11-27|TAKE BACK RETURN|MAIL|ests cajole. evenly ironic exc| +5572|172|10|2|27|28948.59|0.03|0.04|A|F|1994-08-29|1994-09-10|1994-08-30|TAKE BACK RETURN|SHIP| accounts. carefully final accoun| +5572|87|8|3|19|18754.52|0.10|0.00|A|F|1994-08-12|1994-10-07|1994-09-01|DELIVER IN PERSON|RAIL|es. final, final requests wake blithely ag| +5572|135|1|4|46|47615.98|0.02|0.01|R|F|1994-09-08|1994-10-14|1994-10-01|NONE|REG AIR|ully regular platelet| +5572|24|3|5|34|31416.68|0.10|0.08|R|F|1994-10-22|1994-08-16|1994-11-08|NONE|TRUCK|asymptotes integrate. s| +5572|101|2|6|14|14015.40|0.04|0.05|A|F|1994-11-02|1994-09-20|1994-11-03|COLLECT COD|RAIL|he fluffily express packages. fluffily fina| +5572|26|1|7|24|22224.48|0.01|0.05|R|F|1994-09-26|1994-09-04|1994-10-22|DELIVER IN PERSON|FOB| beans. foxes sleep fluffily across th| +5573|21|6|1|32|29472.64|0.05|0.07|N|O|1996-09-30|1996-10-25|1996-10-15|DELIVER IN PERSON|RAIL|egular depths haggl| +5573|50|3|2|2|1900.10|0.01|0.07|N|O|1996-08-26|1996-09-29|1996-09-04|COLLECT COD|TRUCK| even foxes. specia| +5573|11|8|3|46|41906.46|0.06|0.01|N|O|1996-11-04|1996-10-02|1996-11-15|DELIVER IN PERSON|MAIL|s haggle qu| +5573|169|4|4|43|45973.88|0.10|0.03|N|O|1996-10-22|1996-11-03|1996-11-02|TAKE BACK RETURN|FOB| furiously pending packages against | +5573|138|9|5|43|44639.59|0.05|0.04|N|O|1996-09-09|1996-09-24|1996-09-28|COLLECT COD|AIR| bold package| +5574|185|6|1|46|49918.28|0.02|0.07|A|F|1992-06-20|1992-04-19|1992-07-11|NONE|FOB|arefully express requests wake furiousl| +5574|33|4|2|21|19593.63|0.05|0.08|A|F|1992-03-22|1992-04-26|1992-04-16|TAKE BACK RETURN|TRUCK|fully final dugouts. express foxes nag | +5574|119|6|3|27|27515.97|0.10|0.06|R|F|1992-05-08|1992-05-19|1992-06-05|TAKE BACK RETURN|REG AIR|ecial realms. furiously entici| +5574|94|6|4|14|13917.26|0.09|0.01|R|F|1992-05-20|1992-04-09|1992-05-23|COLLECT COD|REG AIR| use slyly carefully special requests? slyl| +5574|85|6|5|19|18716.52|0.05|0.03|A|F|1992-05-28|1992-04-24|1992-06-11|TAKE BACK RETURN|REG AIR|old deposits int| +5575|58|10|1|7|6706.35|0.01|0.07|N|O|1995-10-01|1995-09-30|1995-10-06|NONE|FOB|s. slyly pending theodolites prin| +5575|31|7|2|23|21413.69|0.04|0.02|N|O|1995-10-26|1995-10-09|1995-11-13|TAKE BACK RETURN|AIR|enticingly final requests. ironically| +5575|63|8|3|16|15408.96|0.00|0.07|N|O|1995-08-17|1995-10-14|1995-08-30|NONE|RAIL|jole boldly beyond the final as| +5575|110|1|4|7|7070.77|0.01|0.04|N|O|1995-10-15|1995-09-14|1995-10-18|DELIVER IN PERSON|RAIL|special requests. final, final | +5600|187|8|1|34|36964.12|0.02|0.00|N|O|1997-03-22|1997-04-05|1997-04-09|TAKE BACK RETURN|MAIL|ly above the stealthy ideas. permane| +5600|8|5|2|19|17252.00|0.00|0.01|N|O|1997-04-10|1997-03-24|1997-04-16|TAKE BACK RETURN|TRUCK|dencies. carefully p| +5601|38|4|1|29|27202.87|0.09|0.04|A|F|1992-04-06|1992-02-24|1992-04-29|DELIVER IN PERSON|TRUCK| ironic ideas. final| +5601|164|1|2|45|47887.20|0.10|0.07|A|F|1992-03-25|1992-04-03|1992-04-04|TAKE BACK RETURN|MAIL|ts-- blithely final accounts cajole. carefu| +5601|73|4|3|38|36976.66|0.07|0.00|A|F|1992-01-08|1992-03-01|1992-01-09|TAKE BACK RETURN|REG AIR|ter the evenly final deposit| +5601|148|5|4|12|12577.68|0.03|0.01|A|F|1992-02-27|1992-03-16|1992-03-27|COLLECT COD|TRUCK|ep carefully a| +5602|176|4|1|9|9685.53|0.08|0.03|N|O|1997-10-14|1997-09-14|1997-11-11|COLLECT COD|FOB|lar foxes; quickly ironic ac| +5602|62|7|2|31|29823.86|0.04|0.08|N|O|1997-09-04|1997-10-24|1997-09-07|NONE|TRUCK|rate fluffily regular platelets. blithel| +5602|68|5|3|30|29041.80|0.04|0.00|N|O|1997-09-20|1997-10-25|1997-10-12|DELIVER IN PERSON|FOB|e slyly even packages. careful| +5603|98|2|1|50|49904.50|0.03|0.02|A|F|1992-10-06|1992-08-20|1992-10-08|COLLECT COD|SHIP|final theodolites accor| +5603|116|6|2|49|49789.39|0.06|0.05|A|F|1992-06-24|1992-07-28|1992-07-01|DELIVER IN PERSON|FOB|fully silent requests. carefully fin| +5603|32|8|3|49|45669.47|0.00|0.02|R|F|1992-10-07|1992-07-21|1992-10-10|DELIVER IN PERSON|TRUCK|nic, pending dependencies print| +5604|136|7|1|44|45589.72|0.05|0.01|N|O|1998-08-06|1998-07-08|1998-09-04|NONE|RAIL|efully ironi| +5604|136|2|2|49|50770.37|0.10|0.00|N|O|1998-05-02|1998-07-07|1998-05-20|NONE|FOB|ove the regula| +5604|78|8|3|10|9780.70|0.07|0.05|N|O|1998-08-03|1998-06-23|1998-08-04|COLLECT COD|SHIP|ly final realms wake blit| +5605|87|8|1|50|49354.00|0.08|0.05|N|O|1996-08-26|1996-10-15|1996-09-04|TAKE BACK RETURN|RAIL|instructions sleep carefully ironic req| +5605|151|2|2|7|7358.05|0.06|0.01|N|O|1996-12-13|1996-10-13|1996-12-15|TAKE BACK RETURN|FOB|lowly special courts nag among the furi| +5605|173|2|3|3|3219.51|0.01|0.02|N|O|1996-09-01|1996-10-02|1996-09-20|TAKE BACK RETURN|AIR|posits. accounts boost. t| +5605|55|3|4|45|42977.25|0.00|0.01|N|O|1996-09-05|1996-10-04|1996-09-13|COLLECT COD|FOB|ly unusual instructions. carefully ironic p| +5605|70|7|5|39|37832.73|0.00|0.08|N|O|1996-12-13|1996-11-03|1996-12-24|DELIVER IN PERSON|REG AIR|cial deposits. theodolites w| +5605|166|7|6|29|30918.64|0.08|0.08|N|O|1996-09-19|1996-10-22|1996-10-06|DELIVER IN PERSON|SHIP| quickly. quickly pending sen| +5606|174|5|1|47|50485.99|0.10|0.04|N|O|1996-12-23|1997-01-31|1997-01-20|DELIVER IN PERSON|REG AIR|carefully final foxes. pending, final| +5606|92|3|2|34|33731.06|0.09|0.06|N|O|1997-02-23|1997-02-08|1997-03-09|TAKE BACK RETURN|REG AIR|uses. slyly final | +5606|127|8|3|46|47247.52|0.04|0.00|N|O|1997-03-11|1997-01-13|1997-03-23|DELIVER IN PERSON|REG AIR|ter the ironic accounts. even, ironic depos| +5606|82|3|4|30|29462.40|0.08|0.04|N|O|1997-02-06|1997-01-26|1997-02-16|DELIVER IN PERSON|REG AIR| nag always. blithely express packages | +5606|7|2|5|25|22675.00|0.06|0.00|N|O|1996-12-25|1997-01-12|1997-01-11|TAKE BACK RETURN|AIR|breach about the furiously bold | +5606|154|5|6|3|3162.45|0.04|0.06|N|O|1997-01-11|1997-01-04|1997-02-08|COLLECT COD|AIR| sauternes. asympto| +5606|74|5|7|46|44807.22|0.07|0.01|N|O|1997-02-01|1997-01-31|1997-02-15|DELIVER IN PERSON|TRUCK|ow requests wake around the regular accoun| +5607|132|8|1|23|23738.99|0.02|0.06|R|F|1992-04-17|1992-02-12|1992-04-30|DELIVER IN PERSON|MAIL|the special, final patterns | +5632|10|3|1|48|43680.48|0.06|0.06|N|O|1996-05-08|1996-03-24|1996-06-04|TAKE BACK RETURN|FOB|unts. decoys u| +5632|106|7|2|21|21128.10|0.02|0.08|N|O|1996-03-22|1996-03-10|1996-04-10|NONE|AIR|refully regular pinto beans. ironic reques| +5632|67|2|3|24|23209.44|0.04|0.06|N|O|1996-03-23|1996-04-02|1996-03-30|TAKE BACK RETURN|MAIL|beans detect. quickly final i| +5633|160|2|1|28|29684.48|0.02|0.00|N|O|1998-08-14|1998-07-24|1998-08-26|TAKE BACK RETURN|SHIP|as boost quickly. unusual pinto | +5633|102|3|2|10|10021.00|0.09|0.04|N|O|1998-07-15|1998-08-03|1998-08-03|COLLECT COD|AIR|its cajole fluffily fluffily special pinto| +5633|46|7|3|27|25543.08|0.03|0.02|N|O|1998-09-28|1998-07-28|1998-10-12|DELIVER IN PERSON|AIR|ructions. even ideas haggle carefully r| +5633|164|5|4|50|53208.00|0.02|0.05|N|O|1998-07-23|1998-07-09|1998-08-21|DELIVER IN PERSON|TRUCK|ts. slyly regular | +5633|100|2|5|48|48004.80|0.01|0.05|N|O|1998-06-24|1998-07-22|1998-07-18|DELIVER IN PERSON|TRUCK|even courts haggle slyly at the requ| +5633|107|2|6|1|1007.10|0.02|0.03|N|O|1998-09-29|1998-08-28|1998-10-19|NONE|RAIL|thely notornis: | +5633|11|5|7|39|35529.39|0.02|0.08|N|O|1998-07-12|1998-07-03|1998-07-13|COLLECT COD|TRUCK|ding ideas cajole furiously after| +5634|185|6|1|26|28214.68|0.10|0.08|N|O|1996-10-29|1996-09-15|1996-11-24|COLLECT COD|REG AIR|ptotes mold qu| +5634|175|3|2|22|23653.74|0.02|0.05|N|O|1996-09-01|1996-08-31|1996-09-05|DELIVER IN PERSON|MAIL|silently unusual foxes above the blithely| +5634|109|6|3|16|16145.60|0.08|0.02|N|O|1996-11-15|1996-09-14|1996-12-04|NONE|AIR|ess ideas are carefully pending, even re| +5634|182|3|4|29|31383.22|0.00|0.01|N|O|1996-08-10|1996-10-29|1996-08-11|TAKE BACK RETURN|MAIL|ely final ideas. deposits sleep. reg| +5634|1|2|5|1|901.00|0.04|0.02|N|O|1996-10-02|1996-10-21|1996-10-27|COLLECT COD|MAIL|ctions haggle carefully. carefully clo| +5635|83|4|1|43|42272.44|0.03|0.00|R|F|1992-10-12|1992-09-29|1992-11-01|TAKE BACK RETURN|TRUCK|cross the d| +5635|72|3|2|5|4860.35|0.05|0.08|R|F|1992-10-02|1992-11-05|1992-10-26|TAKE BACK RETURN|REG AIR|yly along the ironic, fi| +5635|72|1|3|12|11664.84|0.09|0.02|A|F|1992-10-18|1992-09-24|1992-11-17|NONE|REG AIR|ke slyly against the carefully final req| +5635|8|5|4|40|36320.00|0.03|0.01|A|F|1992-09-25|1992-11-05|1992-10-11|NONE|FOB|pending foxes. regular packages| +5635|169|10|5|38|40628.08|0.05|0.06|A|F|1992-10-09|1992-09-25|1992-10-18|NONE|MAIL|ckly pendin| +5635|162|9|6|23|24429.68|0.05|0.04|A|F|1992-08-24|1992-11-10|1992-09-21|NONE|AIR|ily pending packages. bold,| +5635|137|3|7|32|33188.16|0.03|0.08|R|F|1992-11-24|1992-09-20|1992-12-17|TAKE BACK RETURN|TRUCK|slyly even| +5636|70|9|1|18|17461.26|0.05|0.03|R|F|1995-05-14|1995-05-17|1995-06-12|DELIVER IN PERSON|REG AIR|slyly express requests. furiously pen| +5636|70|5|2|26|25221.82|0.03|0.06|A|F|1995-03-05|1995-05-16|1995-03-23|TAKE BACK RETURN|AIR| furiously final pinto beans o| +5636|90|1|3|21|20791.89|0.03|0.03|A|F|1995-03-13|1995-05-11|1995-03-24|COLLECT COD|AIR| are furiously unusual | +5636|109|6|4|15|15136.50|0.03|0.04|R|F|1995-04-21|1995-04-30|1995-05-05|DELIVER IN PERSON|REG AIR|efully special| +5636|47|4|5|13|12311.52|0.10|0.03|A|F|1995-05-11|1995-04-27|1995-05-26|COLLECT COD|AIR|en, fluffy accounts amon| +5636|12|3|6|33|30096.33|0.06|0.04|A|F|1995-03-09|1995-04-05|1995-03-23|DELIVER IN PERSON|MAIL|ding to the | +5636|134|10|7|24|24819.12|0.10|0.05|R|F|1995-04-12|1995-03-27|1995-04-16|DELIVER IN PERSON|RAIL|counts sleep furiously b| +5637|47|4|1|14|13258.56|0.03|0.05|N|O|1996-07-20|1996-07-26|1996-08-14|COLLECT COD|MAIL|y bold deposits wak| +5637|172|3|2|35|37525.95|0.09|0.08|N|O|1996-08-01|1996-08-04|1996-08-20|NONE|AIR|s sleep blithely alongside of the ironic| +5637|96|10|3|22|21913.98|0.01|0.07|N|O|1996-08-28|1996-07-30|1996-09-17|COLLECT COD|REG AIR|nding requests are ca| +5637|66|1|4|16|15456.96|0.03|0.03|N|O|1996-09-08|1996-08-31|1996-09-29|TAKE BACK RETURN|TRUCK|d packages. express requests| +5637|196|7|5|10|10961.90|0.01|0.00|N|O|1996-08-25|1996-08-11|1996-09-23|TAKE BACK RETURN|MAIL|ickly ironic gifts. blithely even cour| +5637|129|4|6|27|27786.24|0.01|0.05|N|O|1996-06-27|1996-08-09|1996-07-27|DELIVER IN PERSON|REG AIR|oss the carefully express warhorses| +5638|138|9|1|45|46715.85|0.09|0.07|A|F|1994-05-17|1994-03-09|1994-06-15|NONE|TRUCK|ar foxes. fluffily pending accounts | +5638|168|3|2|12|12817.92|0.02|0.05|A|F|1994-02-05|1994-04-01|1994-02-25|COLLECT COD|TRUCK|n, even requests. furiously ironic not| +5638|162|9|3|21|22305.36|0.08|0.00|A|F|1994-03-13|1994-03-27|1994-03-17|DELIVER IN PERSON|TRUCK|press courts use f| +5639|47|10|1|11|10417.44|0.09|0.02|R|F|1994-09-18|1994-07-10|1994-10-12|TAKE BACK RETURN|SHIP|g the unusual pinto beans caj| +5664|122|1|1|25|25553.00|0.00|0.06|N|O|1998-10-29|1998-09-23|1998-11-25|COLLECT COD|FOB|eposits: furiously ironic grouch| +5664|173|2|2|9|9658.53|0.07|0.05|N|O|1998-07-31|1998-08-26|1998-08-12|COLLECT COD|RAIL| ironic deposits haggle furiously. re| +5664|53|4|3|31|29544.55|0.01|0.03|N|O|1998-11-10|1998-09-12|1998-12-07|TAKE BACK RETURN|FOB|ainst the never silent request| +5664|138|9|4|33|34258.29|0.08|0.03|N|O|1998-08-29|1998-09-17|1998-09-25|DELIVER IN PERSON|RAIL|d the final | +5664|112|2|5|44|44532.84|0.01|0.06|N|O|1998-09-24|1998-09-26|1998-10-23|NONE|TRUCK|ang thinly bold pa| +5664|68|5|6|34|32914.04|0.09|0.01|N|O|1998-09-10|1998-10-05|1998-09-15|COLLECT COD|RAIL|st. fluffily pending foxes na| +5664|182|3|7|9|9739.62|0.01|0.05|N|O|1998-11-04|1998-10-15|1998-11-20|TAKE BACK RETURN|REG AIR|yly. express ideas agai| +5665|101|2|1|32|32035.20|0.00|0.02|A|F|1993-08-11|1993-08-01|1993-09-07|NONE|AIR|f the slyly even requests! regular request| +5665|5|8|2|14|12670.00|0.02|0.00|R|F|1993-06-29|1993-09-16|1993-07-16|DELIVER IN PERSON|AIR|- special pinto beans sleep quickly blithel| +5665|158|9|3|41|43384.15|0.09|0.02|A|F|1993-08-23|1993-09-22|1993-09-11|COLLECT COD|REG AIR| idle ideas across | +5665|46|9|4|47|44463.88|0.01|0.01|A|F|1993-10-06|1993-09-19|1993-11-01|NONE|RAIL|s mold fluffily. final deposits along the| +5666|122|5|1|7|7154.84|0.09|0.08|R|F|1994-05-10|1994-04-06|1994-05-21|NONE|FOB| ideas. regular packag| +5666|36|7|2|14|13104.42|0.08|0.01|A|F|1994-02-27|1994-04-11|1994-03-06|DELIVER IN PERSON|TRUCK|lar deposits nag against the slyly final d| +5666|193|6|3|39|42634.41|0.00|0.01|A|F|1994-05-13|1994-04-02|1994-06-12|DELIVER IN PERSON|TRUCK|the even, final foxes. quickly iron| +5666|131|2|4|24|24747.12|0.07|0.01|R|F|1994-02-14|1994-03-09|1994-03-06|DELIVER IN PERSON|FOB|on the carefully pending asympto| +5666|109|10|5|36|36327.60|0.07|0.07|R|F|1994-03-15|1994-03-16|1994-03-18|COLLECT COD|TRUCK|accounts. furiousl| +5667|145|4|1|37|38670.18|0.09|0.06|N|O|1995-09-24|1995-09-17|1995-10-03|NONE|REG AIR|s cajole blit| +5668|4|9|1|15|13560.00|0.03|0.04|A|F|1995-04-06|1995-05-12|1995-04-17|COLLECT COD|FOB| the express, pending requests. bo| +5669|191|2|1|7|7638.33|0.06|0.06|N|O|1996-06-19|1996-07-07|1996-07-11|COLLECT COD|SHIP|yly regular requests lose blithely. careful| +5669|156|8|2|2|2112.30|0.06|0.07|N|O|1996-08-04|1996-06-15|1996-08-20|NONE|SHIP| blithely excuses. slyly| +5669|158|9|3|40|42326.00|0.00|0.02|N|O|1996-08-30|1996-06-15|1996-09-07|TAKE BACK RETURN|FOB|ar accounts alongside of the final, p| +5669|90|1|4|31|30692.79|0.04|0.05|N|O|1996-08-05|1996-06-10|1996-08-29|COLLECT COD|AIR|to beans against the regular depo| +5669|140|6|5|30|31204.20|0.07|0.01|N|O|1996-07-14|1996-07-28|1996-08-10|TAKE BACK RETURN|TRUCK|l accounts. care| +5670|90|1|1|27|26732.43|0.10|0.06|R|F|1993-05-09|1993-05-30|1993-06-06|TAKE BACK RETURN|REG AIR| ideas promise bli| +5670|186|7|2|43|46705.74|0.06|0.00|A|F|1993-07-09|1993-06-03|1993-07-14|DELIVER IN PERSON|FOB|ests in place of the carefully sly depos| +5670|7|8|3|24|21768.00|0.09|0.04|A|F|1993-07-17|1993-07-01|1993-08-03|NONE|AIR|press, express requests haggle| +5670|142|9|4|11|11463.54|0.06|0.06|R|F|1993-07-11|1993-06-26|1993-07-24|DELIVER IN PERSON|MAIL|etect furiously among the even pin| +5671|120|7|1|25|25503.00|0.00|0.08|N|O|1998-04-17|1998-03-28|1998-05-06|DELIVER IN PERSON|AIR|cording to the quickly final requests-- | +5671|129|8|2|46|47339.52|0.05|0.08|N|O|1998-03-28|1998-04-22|1998-04-19|TAKE BACK RETURN|MAIL|lar pinto beans detect care| +5671|172|10|3|13|13938.21|0.10|0.06|N|O|1998-03-02|1998-04-03|1998-03-08|TAKE BACK RETURN|TRUCK|bold theodolites about| +5671|111|1|4|42|42466.62|0.00|0.07|N|O|1998-02-17|1998-04-24|1998-03-17|TAKE BACK RETURN|SHIP|carefully slyly special deposit| +5671|129|4|5|13|13378.56|0.09|0.00|N|O|1998-04-24|1998-03-26|1998-04-27|NONE|REG AIR|ers according to the ironic, unusual excu| +5671|114|1|6|30|30423.30|0.09|0.07|N|O|1998-06-06|1998-04-15|1998-07-01|DELIVER IN PERSON|TRUCK|fily ironi| +5696|137|3|1|28|29039.64|0.03|0.06|N|O|1995-07-03|1995-06-14|1995-07-27|COLLECT COD|REG AIR| the fluffily brave pearls | +5696|59|1|2|46|44116.30|0.01|0.00|N|O|1995-08-10|1995-07-08|1995-08-25|COLLECT COD|AIR|ter the instruct| +5696|167|2|3|42|44820.72|0.04|0.01|N|F|1995-06-06|1995-06-11|1995-06-19|TAKE BACK RETURN|SHIP|te furious| +5696|98|10|4|20|19961.80|0.08|0.00|N|O|1995-06-25|1995-07-18|1995-07-16|NONE|TRUCK|silent, pending ideas sleep fluffil| +5696|124|9|5|19|19458.28|0.07|0.05|N|O|1995-08-31|1995-06-13|1995-09-10|COLLECT COD|SHIP|unusual requests sleep furiously ru| +5696|132|8|6|37|38188.81|0.04|0.05|N|O|1995-07-21|1995-06-23|1995-08-19|NONE|RAIL| carefully expres| +5696|102|9|7|6|6012.60|0.07|0.05|N|O|1995-08-03|1995-07-15|1995-09-01|DELIVER IN PERSON|REG AIR|n patterns lose slyly fina| +5697|55|7|1|24|22921.20|0.10|0.07|R|F|1992-10-27|1992-11-28|1992-11-20|NONE|RAIL|uffily iro| +5697|16|10|2|43|39388.43|0.06|0.02|R|F|1992-12-08|1992-12-03|1992-12-17|TAKE BACK RETURN|FOB|blithely reg| +5697|56|8|3|42|40154.10|0.03|0.01|A|F|1992-12-19|1992-12-08|1993-01-03|COLLECT COD|TRUCK|inal theodolites cajole after the bli| +5698|11|8|1|30|27330.30|0.01|0.05|A|F|1994-05-26|1994-08-16|1994-06-19|COLLECT COD|AIR|its. quickly regular foxes aro| +5698|163|4|2|25|26579.00|0.08|0.07|R|F|1994-08-06|1994-06-21|1994-08-25|NONE|SHIP| asymptotes sleep slyly above the| +5698|155|3|3|45|47481.75|0.03|0.01|A|F|1994-06-23|1994-08-13|1994-07-02|NONE|FOB|ng excuses. slyly express asymptotes| +5698|58|6|4|15|14370.75|0.07|0.08|R|F|1994-06-29|1994-07-03|1994-07-02|COLLECT COD|REG AIR|ly ironic frets haggle carefully | +5698|140|1|5|37|38485.18|0.06|0.06|A|F|1994-06-30|1994-06-23|1994-07-22|TAKE BACK RETURN|SHIP|ts. even, ironic | +5698|188|9|6|1|1088.18|0.06|0.04|R|F|1994-05-31|1994-07-10|1994-06-03|DELIVER IN PERSON|MAIL|nts. slyly quiet pinto beans nag carefu| +5699|2|7|1|24|21648.00|0.01|0.07|A|F|1992-10-21|1992-09-04|1992-11-04|COLLECT COD|AIR|kages. fin| +5699|55|10|2|26|24831.30|0.06|0.06|R|F|1992-08-11|1992-09-21|1992-08-14|COLLECT COD|MAIL|y final deposits wake fluffily u| +5699|18|2|3|48|44064.48|0.10|0.05|R|F|1992-11-23|1992-10-20|1992-11-29|DELIVER IN PERSON|TRUCK|s. carefully regul| +5699|55|3|4|46|43932.30|0.08|0.02|A|F|1992-11-28|1992-09-23|1992-12-27|TAKE BACK RETURN|FOB|o the slyly| +5699|28|7|5|21|19488.42|0.02|0.02|A|F|1992-10-13|1992-09-30|1992-10-19|NONE|MAIL|lyly final pla| +5699|191|5|6|30|32735.70|0.08|0.05|R|F|1992-11-13|1992-10-01|1992-12-11|DELIVER IN PERSON|AIR| the carefully final | +5699|129|8|7|45|46310.40|0.09|0.06|A|F|1992-09-23|1992-10-22|1992-10-04|DELIVER IN PERSON|SHIP|rmanent packages sleep across the f| +5700|168|5|1|24|25635.84|0.09|0.00|N|O|1997-12-26|1998-01-28|1998-01-18|DELIVER IN PERSON|REG AIR|ix carefully | +5700|123|8|2|30|30693.60|0.00|0.06|N|O|1998-04-19|1998-03-13|1998-04-27|COLLECT COD|MAIL|ly blithely final instructions. fl| +5700|126|5|3|23|23600.76|0.03|0.05|N|O|1998-01-30|1998-01-31|1998-01-31|NONE|REG AIR| wake quickly carefully fluffy hockey| +5701|54|2|1|17|16218.85|0.02|0.05|N|O|1997-03-27|1997-04-08|1997-04-21|DELIVER IN PERSON|RAIL|tes. quickly final a| +5702|77|7|1|44|42991.08|0.06|0.02|R|F|1994-01-04|1993-11-25|1994-01-22|NONE|RAIL|lites. carefully final requests doze b| +5702|86|7|2|37|36484.96|0.10|0.05|R|F|1993-12-14|1993-10-21|1994-01-08|NONE|FOB|ix slyly. regular instructions slee| +5702|131|7|3|44|45369.72|0.00|0.02|R|F|1993-11-28|1993-12-02|1993-12-22|NONE|TRUCK|ake according to th| +5702|63|8|4|31|29854.86|0.00|0.04|A|F|1994-01-04|1993-10-22|1994-01-26|DELIVER IN PERSON|TRUCK|pinto beans. blithely | +5703|88|9|1|2|1976.16|0.09|0.01|R|F|1993-05-29|1993-07-26|1993-06-05|TAKE BACK RETURN|REG AIR|nts against the blithely sile| +5728|44|1|1|47|44369.88|0.10|0.05|A|F|1994-12-13|1995-01-25|1994-12-25|TAKE BACK RETURN|MAIL|nd the bravely final deposits. final ideas| +5728|159|1|2|40|42366.00|0.05|0.08|A|F|1995-03-28|1995-01-17|1995-04-14|TAKE BACK RETURN|SHIP|final deposits. theodolite| +5729|143|4|1|5|5215.70|0.07|0.00|R|F|1994-11-27|1994-11-11|1994-12-23|TAKE BACK RETURN|MAIL|s. even sheaves nag courts. | +5729|107|10|2|39|39276.90|0.10|0.00|A|F|1995-01-22|1994-11-21|1995-02-13|TAKE BACK RETURN|MAIL|. special pl| +5729|12|3|3|50|45600.50|0.00|0.05|R|F|1994-12-09|1994-12-31|1994-12-24|TAKE BACK RETURN|AIR|ly special sentiments. car| +5730|151|2|1|2|2102.30|0.08|0.00|N|O|1998-02-24|1998-03-15|1998-03-11|COLLECT COD|SHIP|ely ironic foxes. carefu| +5730|200|1|2|9|9901.80|0.10|0.01|N|O|1998-03-05|1998-02-02|1998-03-28|DELIVER IN PERSON|MAIL|s lose blithely. specia| +5731|192|6|1|13|14198.47|0.02|0.04|N|O|1997-07-30|1997-06-23|1997-08-13|COLLECT COD|RAIL|ngside of the quickly regular depos| +5731|105|6|2|11|11056.10|0.00|0.08|N|O|1997-06-06|1997-07-08|1997-06-25|NONE|MAIL| furiously final accounts wake. d| +5731|111|2|3|6|6066.66|0.01|0.04|N|O|1997-07-02|1997-07-01|1997-07-08|COLLECT COD|SHIP|sits integrate slyly close platelets. quick| +5731|14|1|4|6|5484.06|0.03|0.06|N|O|1997-09-07|1997-06-20|1997-09-20|TAKE BACK RETURN|RAIL|rs. quickly regular theo| +5731|195|6|5|19|20808.61|0.08|0.02|N|O|1997-06-29|1997-06-27|1997-07-15|NONE|REG AIR|ly unusual ideas above the | +5732|139|5|1|26|27017.38|0.02|0.07|N|O|1997-08-18|1997-10-25|1997-09-12|TAKE BACK RETURN|TRUCK|totes cajole according to the theodolites.| +5733|33|4|1|39|36388.17|0.01|0.07|A|F|1993-03-22|1993-05-24|1993-04-04|DELIVER IN PERSON|FOB|side of the| +5734|183|4|1|29|31412.22|0.05|0.01|N|O|1997-12-01|1997-12-08|1997-12-23|NONE|RAIL|structions cajole final, express | +5734|150|3|2|6|6300.90|0.07|0.00|N|O|1997-10-27|1997-12-19|1997-11-02|COLLECT COD|RAIL|s. regular platelets cajole furiously. regu| +5734|67|8|3|10|9670.60|0.01|0.03|N|O|1997-12-28|1997-12-24|1998-01-24|DELIVER IN PERSON|TRUCK|equests; accounts above| +5735|60|1|1|41|39362.46|0.01|0.01|R|F|1994-12-23|1995-02-10|1995-01-22|COLLECT COD|MAIL|lthily ruthless i| +5760|1|8|1|6|5406.00|0.09|0.03|R|F|1994-07-30|1994-07-31|1994-08-16|COLLECT COD|REG AIR|ng the acco| +5760|6|1|2|24|21744.00|0.04|0.05|A|F|1994-07-15|1994-07-04|1994-08-08|NONE|MAIL|s. bravely ironic accounts among| +5760|148|5|3|8|8385.12|0.07|0.04|A|F|1994-09-06|1994-08-03|1994-10-06|NONE|AIR|l accounts among the carefully even de| +5760|123|4|4|19|19439.28|0.10|0.01|R|F|1994-08-02|1994-08-02|1994-08-15|COLLECT COD|SHIP|sits nag. even, regular ideas cajole b| +5760|166|1|5|6|6396.96|0.03|0.07|R|F|1994-06-09|1994-07-06|1994-06-16|DELIVER IN PERSON|MAIL| shall have to cajole along the | +5761|47|6|1|41|38828.64|0.08|0.00|N|O|1998-07-31|1998-08-09|1998-08-08|TAKE BACK RETURN|TRUCK|pecial deposits. qu| +5761|108|9|2|36|36291.60|0.00|0.07|N|O|1998-09-07|1998-09-21|1998-09-11|TAKE BACK RETURN|TRUCK| pinto beans thrash alongside of the pendi| +5761|198|2|3|49|53811.31|0.04|0.08|N|O|1998-07-14|1998-08-20|1998-07-25|NONE|SHIP|ly bold accounts wake above the| +5762|175|6|1|6|6451.02|0.05|0.02|N|O|1997-04-07|1997-03-25|1997-05-02|NONE|AIR|ironic dependencies doze carefu| +5762|102|9|2|27|27056.70|0.02|0.08|N|O|1997-02-21|1997-05-08|1997-03-23|NONE|REG AIR|across the bold ideas. carefully sp| +5762|89|10|3|40|39563.20|0.00|0.08|N|O|1997-04-30|1997-05-09|1997-05-08|COLLECT COD|SHIP|al instructions. furiousl| +5762|133|4|4|47|48557.11|0.05|0.06|N|O|1997-03-02|1997-03-23|1997-03-19|NONE|RAIL|equests sleep after the furiously ironic pa| +5762|25|6|5|28|25900.56|0.02|0.06|N|O|1997-02-22|1997-03-25|1997-02-24|TAKE BACK RETURN|SHIP|ic foxes among the blithely qui| +5762|12|6|6|12|10944.12|0.00|0.06|N|O|1997-04-18|1997-04-27|1997-05-11|DELIVER IN PERSON|REG AIR|ages are abo| +5763|131|2|1|32|32996.16|0.02|0.06|N|O|1998-07-16|1998-09-13|1998-08-02|DELIVER IN PERSON|FOB|ding instruct| +5763|136|2|2|23|23830.99|0.09|0.04|N|O|1998-07-25|1998-09-21|1998-08-15|DELIVER IN PERSON|SHIP|re after the blithel| +5763|13|3|3|25|22825.25|0.01|0.02|N|O|1998-10-04|1998-08-16|1998-10-09|DELIVER IN PERSON|REG AIR|inal theodolites. even re| +5763|121|6|4|47|47992.64|0.09|0.00|N|O|1998-08-22|1998-09-22|1998-09-04|NONE|REG AIR|gle slyly. slyly final re| +5763|123|4|5|8|8184.96|0.06|0.05|N|O|1998-09-23|1998-09-15|1998-09-27|DELIVER IN PERSON|TRUCK|foxes wake slyly. car| +5763|190|1|6|9|9811.71|0.08|0.02|N|O|1998-09-24|1998-09-01|1998-10-02|NONE|AIR| deposits. instru| +5764|101|2|1|28|28030.80|0.04|0.04|A|F|1993-12-07|1993-12-20|1993-12-26|TAKE BACK RETURN|RAIL|sleep furi| +5764|200|3|2|20|22004.00|0.10|0.05|A|F|1993-10-17|1993-12-24|1993-10-18|TAKE BACK RETURN|FOB|ng to the fluffily qu| +5764|188|9|3|4|4352.72|0.03|0.05|A|F|1993-10-25|1993-12-23|1993-11-06|DELIVER IN PERSON|AIR|ily regular courts haggle| +5765|162|7|1|31|32926.96|0.00|0.06|A|F|1995-01-11|1995-02-13|1995-01-23|TAKE BACK RETURN|AIR|r foxes. ev| +5765|124|9|2|29|29699.48|0.07|0.08|A|F|1994-12-29|1995-02-01|1995-01-26|NONE|RAIL|nic requests. deposits wake quickly among | +5765|139|10|3|31|32213.03|0.05|0.01|R|F|1995-03-01|1995-01-23|1995-03-31|TAKE BACK RETURN|REG AIR|the furiou| +5765|152|4|4|46|48398.90|0.07|0.07|R|F|1995-03-13|1995-02-12|1995-03-20|DELIVER IN PERSON|MAIL|ccounts sleep about th| +5765|174|3|5|48|51560.16|0.09|0.02|A|F|1995-03-30|1995-01-14|1995-04-09|DELIVER IN PERSON|SHIP|theodolites integrate furiously| +5765|83|4|6|41|40306.28|0.04|0.00|A|F|1994-12-31|1995-02-11|1995-01-17|TAKE BACK RETURN|SHIP| furiously. slyly sile| +5765|42|5|7|21|19782.84|0.05|0.04|R|F|1995-04-05|1995-02-12|1995-05-05|COLLECT COD|TRUCK|ole furiously. quick, special dependencies | +5766|188|9|1|1|1088.18|0.10|0.01|R|F|1994-01-16|1993-11-16|1994-01-23|NONE|MAIL|blithely regular the| +5766|149|8|2|39|40916.46|0.02|0.07|A|F|1993-10-24|1993-12-07|1993-11-08|DELIVER IN PERSON|SHIP| furiously unusual courts. slyly final pear| +5766|118|8|3|4|4072.44|0.08|0.08|R|F|1993-11-10|1993-10-30|1993-12-01|COLLECT COD|TRUCK|ly even requests. furiou| +5767|167|8|1|11|11738.76|0.08|0.01|A|F|1992-06-02|1992-05-30|1992-06-08|NONE|TRUCK|instructions. carefully final accou| +5767|69|8|2|15|14535.90|0.07|0.05|R|F|1992-06-05|1992-07-28|1992-06-08|DELIVER IN PERSON|MAIL|warthogs. carefully unusual g| +5767|191|3|3|42|45829.98|0.06|0.01|R|F|1992-07-31|1992-06-09|1992-08-09|COLLECT COD|TRUCK| blithe deposi| +5767|153|4|4|34|35807.10|0.06|0.01|R|F|1992-06-02|1992-06-23|1992-06-17|NONE|FOB|sits among the| +5767|46|7|5|36|34057.44|0.03|0.00|A|F|1992-07-17|1992-06-10|1992-07-19|COLLECT COD|AIR|ake carefully. packages | +5792|178|8|1|34|36657.78|0.08|0.07|R|F|1993-05-23|1993-06-25|1993-06-12|NONE|RAIL|requests are against t| +5792|157|5|2|47|49686.05|0.10|0.00|A|F|1993-06-08|1993-05-10|1993-06-26|COLLECT COD|AIR|regular, ironic excuses n| +5792|183|4|3|32|34661.76|0.05|0.08|R|F|1993-06-26|1993-05-23|1993-07-07|COLLECT COD|RAIL|s are slyly against the ev| +5792|14|8|4|14|12796.14|0.09|0.02|A|F|1993-07-28|1993-06-17|1993-08-27|DELIVER IN PERSON|RAIL|olites print carefully| +5792|102|9|5|31|31065.10|0.02|0.01|A|F|1993-06-17|1993-05-05|1993-07-01|COLLECT COD|TRUCK|s? furiously even instructions | +5793|53|5|1|20|19061.00|0.05|0.03|N|O|1997-10-05|1997-09-04|1997-10-30|COLLECT COD|AIR|e carefully ex| +5793|170|5|2|41|43876.97|0.06|0.06|N|O|1997-08-04|1997-10-10|1997-08-12|DELIVER IN PERSON|TRUCK|snooze quick| +5793|43|4|3|8|7544.32|0.07|0.03|N|O|1997-08-16|1997-09-08|1997-08-28|COLLECT COD|AIR|al foxes l| +5793|148|7|4|48|50310.72|0.02|0.02|N|O|1997-09-27|1997-08-23|1997-10-27|DELIVER IN PERSON|REG AIR|quickly enticing excuses use slyly abov| +5794|158|9|1|42|44442.30|0.06|0.05|R|F|1993-06-29|1993-05-30|1993-07-28|COLLECT COD|REG AIR|he careful| +5794|115|2|2|14|14211.54|0.09|0.02|R|F|1993-04-19|1993-07-02|1993-05-18|COLLECT COD|SHIP|uriously carefully ironic reque| +5794|7|8|3|15|13605.00|0.09|0.06|R|F|1993-06-25|1993-06-27|1993-07-09|NONE|MAIL|blithely regular ideas. final foxes haggle | +5794|137|3|4|47|48745.11|0.00|0.08|A|F|1993-07-16|1993-06-21|1993-08-05|TAKE BACK RETURN|REG AIR|quests. blithely final excu| +5795|193|6|1|34|37168.46|0.09|0.05|A|F|1992-08-21|1992-07-30|1992-08-27|COLLECT COD|REG AIR|al instructions must affix along the ironic| +5796|58|3|1|27|25867.35|0.10|0.00|N|O|1996-04-06|1996-02-29|1996-04-20|DELIVER IN PERSON|RAIL|s wake quickly aro| +5797|61|6|1|17|16338.02|0.09|0.03|N|O|1997-12-13|1998-01-12|1997-12-23|NONE|REG AIR|the ironic, even theodoli| +5798|127|8|1|2|2054.24|0.09|0.00|N|O|1998-05-25|1998-06-22|1998-06-09|COLLECT COD|FOB|e furiously across | +5798|124|9|2|14|14337.68|0.06|0.05|N|O|1998-04-01|1998-06-14|1998-04-27|NONE|RAIL|he special, bold packages. carefully iron| +5798|134|5|3|22|22750.86|0.02|0.01|N|O|1998-06-24|1998-06-06|1998-07-20|COLLECT COD|TRUCK|sits poach carefully| +5798|146|3|4|40|41845.60|0.08|0.06|N|O|1998-07-09|1998-06-24|1998-07-16|NONE|TRUCK| integrate carefu| +5798|149|8|5|7|7343.98|0.06|0.07|N|O|1998-06-06|1998-05-10|1998-06-07|NONE|SHIP|ts against the blithely final p| +5798|38|4|6|9|8442.27|0.06|0.02|N|O|1998-05-05|1998-05-25|1998-05-09|DELIVER IN PERSON|REG AIR|e blithely| +5798|115|9|7|32|32483.52|0.08|0.01|N|O|1998-04-27|1998-05-03|1998-05-08|TAKE BACK RETURN|REG AIR|ubt blithely above the | +5799|95|6|1|41|40798.69|0.04|0.02|N|O|1995-11-13|1995-10-31|1995-11-16|COLLECT COD|TRUCK|al accounts sleep ruthlessl| +5799|100|3|2|30|30003.00|0.03|0.08|N|O|1995-09-12|1995-09-13|1995-09-19|NONE|RAIL| furiously s| +5824|77|7|1|40|39082.80|0.06|0.06|N|O|1997-01-14|1997-01-17|1997-02-02|NONE|REG AIR|he final packag| +5824|182|3|2|42|45451.56|0.09|0.00|N|O|1997-02-01|1997-02-20|1997-02-07|COLLECT COD|SHIP|ts sleep. carefully regular accounts h| +5824|73|1|3|16|15569.12|0.03|0.02|N|O|1997-02-13|1997-01-07|1997-02-17|TAKE BACK RETURN|TRUCK|sly express Ti| +5824|92|5|4|32|31746.88|0.03|0.02|N|O|1997-02-16|1997-01-24|1997-02-20|DELIVER IN PERSON|RAIL|ven requests. | +5824|107|8|5|44|44312.40|0.08|0.03|N|O|1997-01-24|1997-01-31|1997-02-11|COLLECT COD|TRUCK|fily fluffily bold| +5825|159|7|1|23|24360.45|0.10|0.05|R|F|1995-05-10|1995-04-28|1995-05-13|DELIVER IN PERSON|TRUCK| special pinto beans. dependencies haggl| +5826|144|1|1|4|4176.56|0.03|0.06|N|O|1998-07-31|1998-09-10|1998-08-27|NONE|AIR| packages across the fluffily spec| +5826|64|5|2|18|17353.08|0.04|0.01|N|O|1998-07-17|1998-09-03|1998-07-22|NONE|SHIP|atelets use above t| +5827|187|8|1|30|32615.40|0.03|0.05|N|O|1998-11-11|1998-09-27|1998-11-30|DELIVER IN PERSON|RAIL|ounts may c| +5827|103|6|2|23|23071.30|0.09|0.05|N|O|1998-11-16|1998-09-14|1998-11-17|COLLECT COD|RAIL|ans. furiously special instruct| +5827|164|1|3|3|3192.48|0.03|0.06|N|O|1998-10-17|1998-09-29|1998-10-28|DELIVER IN PERSON|MAIL|uses eat along the furiously| +5827|200|1|4|26|28605.20|0.06|0.00|N|O|1998-07-29|1998-09-24|1998-07-30|DELIVER IN PERSON|SHIP|arefully special packages wake thin| +5827|112|9|5|38|38460.18|0.03|0.06|N|O|1998-10-18|1998-08-27|1998-10-23|TAKE BACK RETURN|TRUCK|ly ruthless accounts| +5827|17|4|6|14|12838.14|0.05|0.01|N|O|1998-08-31|1998-09-06|1998-09-13|TAKE BACK RETURN|RAIL|rges. fluffily pending | +5828|2|9|1|28|25256.00|0.10|0.03|A|F|1994-05-15|1994-05-20|1994-06-08|DELIVER IN PERSON|MAIL| special ideas haggle slyly ac| +5828|158|3|2|37|39151.55|0.01|0.00|R|F|1994-06-07|1994-05-30|1994-06-17|NONE|RAIL|e carefully spec| +5829|40|1|1|4|3760.16|0.01|0.02|N|O|1997-03-01|1997-02-17|1997-03-22|NONE|TRUCK|ithely; accounts cajole ideas. regular foxe| +5829|107|10|2|40|40284.00|0.04|0.01|N|O|1997-04-21|1997-02-12|1997-05-04|COLLECT COD|TRUCK| the carefully ironic accounts. a| +5829|129|8|3|6|6174.72|0.05|0.06|N|O|1997-01-22|1997-03-12|1997-02-02|TAKE BACK RETURN|AIR|sts. slyly special fo| +5829|90|1|4|42|41583.78|0.02|0.07|N|O|1997-03-26|1997-04-01|1997-03-30|COLLECT COD|REG AIR|pearls. slyly bold deposits solve final| +5829|191|5|5|49|53468.31|0.05|0.01|N|O|1997-01-31|1997-03-13|1997-02-18|NONE|MAIL| ironic excuses use fluf| +5829|18|5|6|17|15606.17|0.09|0.02|N|O|1997-04-10|1997-03-29|1997-04-22|COLLECT COD|AIR|after the furiously ironic ideas no| +5829|78|9|7|27|26407.89|0.08|0.04|N|O|1997-02-25|1997-03-31|1997-03-03|DELIVER IN PERSON|AIR|ns about the excuses are c| +5830|160|2|1|29|30744.64|0.10|0.02|R|F|1993-06-19|1993-05-10|1993-07-13|DELIVER IN PERSON|REG AIR|y bold excuses| +5831|191|2|1|2|2182.38|0.10|0.01|N|O|1997-02-09|1997-01-20|1997-03-07|TAKE BACK RETURN|TRUCK|quickly silent req| +5831|74|3|2|33|32144.31|0.04|0.03|N|O|1996-11-20|1997-01-18|1996-12-18|TAKE BACK RETURN|MAIL| instructions wake. slyly sil| +5831|82|3|3|6|5892.48|0.05|0.07|N|O|1997-01-29|1997-01-14|1997-02-09|NONE|MAIL|ly ironic accounts nag pendin| +5831|13|10|4|46|41998.46|0.06|0.02|N|O|1997-02-24|1997-01-18|1997-03-02|COLLECT COD|MAIL|ly final pa| +5831|43|4|5|37|34892.48|0.05|0.01|N|O|1997-01-17|1997-02-08|1997-02-01|NONE|FOB|uriously even requests| +5856|4|1|1|1|904.00|0.03|0.02|A|F|1994-12-29|1995-01-07|1995-01-10|TAKE BACK RETURN|MAIL|tly. special deposits wake blithely even| +5856|35|6|2|35|32726.05|0.09|0.02|R|F|1994-11-24|1994-12-23|1994-11-30|COLLECT COD|AIR|excuses. finally ir| +5856|153|4|3|39|41072.85|0.05|0.03|A|F|1995-01-18|1995-01-11|1995-01-19|DELIVER IN PERSON|TRUCK|uickly quickly fluffy in| +5857|58|9|1|25|23951.25|0.03|0.02|N|O|1997-12-02|1997-12-17|1997-12-08|DELIVER IN PERSON|REG AIR|ding platelets. pending excu| +5857|195|9|2|50|54759.50|0.06|0.07|N|O|1997-12-04|1997-12-16|1997-12-20|NONE|TRUCK|y regular d| +5857|68|3|3|1|968.06|0.03|0.01|N|O|1998-02-01|1997-12-09|1998-02-20|TAKE BACK RETURN|SHIP|instructions detect final reques| +5857|118|2|4|12|12217.32|0.03|0.08|N|O|1998-01-24|1997-12-27|1998-02-10|TAKE BACK RETURN|AIR|counts. express, final| +5857|192|4|5|14|15290.66|0.07|0.07|N|O|1997-12-10|1998-01-06|1998-01-04|TAKE BACK RETURN|TRUCK|ffily pendin| +5857|93|5|6|49|48661.41|0.00|0.04|N|O|1998-01-23|1997-12-12|1998-01-28|DELIVER IN PERSON|REG AIR|egular pinto beans| +5858|121|4|1|20|20422.40|0.02|0.06|A|F|1992-07-23|1992-08-26|1992-07-24|COLLECT COD|SHIP|uffily unusual pinto beans sleep| +5858|16|7|2|36|32976.36|0.00|0.05|A|F|1992-09-25|1992-08-16|1992-10-11|NONE|SHIP|osits wake quickly quickly sile| +5858|148|5|3|7|7336.98|0.08|0.02|A|F|1992-10-07|1992-08-16|1992-10-15|TAKE BACK RETURN|REG AIR|. doggedly regular packages use pendin| +5858|164|9|4|46|48951.36|0.07|0.06|R|F|1992-09-07|1992-10-06|1992-10-06|DELIVER IN PERSON|MAIL|posits withi| +5858|161|8|5|18|19100.88|0.00|0.07|A|F|1992-11-05|1992-10-08|1992-12-03|NONE|TRUCK|al excuses. bold| +5858|154|9|6|7|7379.05|0.04|0.00|A|F|1992-09-14|1992-10-01|1992-10-01|TAKE BACK RETURN|RAIL|dly pending ac| +5858|11|5|7|50|45550.50|0.06|0.00|R|F|1992-07-20|1992-10-07|1992-07-25|NONE|TRUCK|r the ironic ex| +5859|175|4|1|50|53758.50|0.07|0.01|N|O|1997-07-08|1997-06-20|1997-07-27|COLLECT COD|MAIL|ly regular deposits use. ironic| +5859|9|6|2|17|15453.00|0.03|0.03|N|O|1997-05-15|1997-06-30|1997-05-26|DELIVER IN PERSON|AIR|ly ironic requests. quickly unusual pin| +5859|46|3|3|33|31219.32|0.10|0.04|N|O|1997-07-08|1997-06-22|1997-07-18|TAKE BACK RETURN|TRUCK|eposits unwind furiously final pinto bea| +5859|93|4|4|40|39723.60|0.09|0.02|N|O|1997-08-05|1997-06-17|1997-08-20|NONE|REG AIR|l dependenci| +5859|153|8|5|35|36860.25|0.00|0.08|N|O|1997-05-28|1997-07-14|1997-06-15|COLLECT COD|TRUCK|egular acco| +5859|44|5|6|9|8496.36|0.01|0.02|N|O|1997-06-15|1997-06-06|1997-06-20|NONE|RAIL|ges boost quickly. blithely r| +5859|191|5|7|27|29462.13|0.05|0.08|N|O|1997-07-30|1997-07-08|1997-08-08|NONE|MAIL| across th| +5860|51|3|1|10|9510.50|0.04|0.04|A|F|1992-03-11|1992-03-30|1992-03-31|NONE|MAIL|ual patterns try to eat carefully above| +5861|191|5|1|32|34918.08|0.00|0.03|N|O|1997-05-27|1997-05-29|1997-05-28|TAKE BACK RETURN|MAIL|nt asymptotes. carefully express request| +5861|86|7|2|6|5916.48|0.10|0.03|N|O|1997-07-28|1997-05-18|1997-08-24|TAKE BACK RETURN|TRUCK|olites. slyly| +5862|113|7|1|4|4052.44|0.09|0.06|N|O|1997-06-04|1997-04-26|1997-06-19|NONE|TRUCK|yly silent deposit| +5862|2|7|2|29|26158.00|0.03|0.05|N|O|1997-04-02|1997-04-16|1997-04-04|NONE|FOB|e fluffily. furiously| +5863|161|10|1|45|47752.20|0.07|0.06|A|F|1993-12-19|1994-01-25|1994-01-05|NONE|REG AIR| deposits are ab| +5863|160|8|2|21|22263.36|0.09|0.03|R|F|1994-01-13|1994-01-09|1994-01-28|DELIVER IN PERSON|FOB|atelets nag blithely furi| +5888|62|7|1|46|44254.76|0.02|0.00|N|O|1996-11-18|1996-11-05|1996-12-08|TAKE BACK RETURN|FOB|yly final accounts hag| +5888|112|3|2|24|24290.64|0.03|0.01|N|O|1996-11-07|1996-11-30|1996-11-20|COLLECT COD|SHIP|ing to the spe| +5889|77|7|1|17|16610.19|0.09|0.02|N|O|1995-07-01|1995-08-12|1995-07-25|NONE|AIR|blithely pending packages. flu| +5890|113|4|1|38|38498.18|0.01|0.08|A|F|1993-02-14|1992-12-09|1993-02-27|COLLECT COD|FOB| accounts. carefully final asymptotes| +5891|85|6|1|22|21671.76|0.00|0.06|R|F|1993-01-01|1993-02-18|1993-01-14|DELIVER IN PERSON|TRUCK|iresias cajole deposits. special, ir| +5891|186|7|2|9|9775.62|0.03|0.07|R|F|1993-01-20|1993-02-27|1993-02-10|COLLECT COD|REG AIR|cajole carefully | +5891|30|9|3|10|9300.30|0.08|0.01|A|F|1993-04-14|1993-02-07|1993-04-15|DELIVER IN PERSON|RAIL|nding requests. b| +5892|148|9|1|7|7336.98|0.02|0.03|N|O|1995-06-26|1995-07-18|1995-07-25|COLLECT COD|AIR|e furiously. quickly even deposits da| +5892|150|9|2|37|38855.55|0.09|0.06|N|O|1995-08-12|1995-06-11|1995-09-05|NONE|REG AIR|maintain. bold, expre| +5892|3|4|3|28|25284.00|0.03|0.06|N|O|1995-08-16|1995-07-06|1995-08-22|DELIVER IN PERSON|MAIL|ithely unusual accounts will have to integ| +5892|75|6|4|23|22426.61|0.08|0.04|R|F|1995-05-18|1995-07-06|1995-05-29|COLLECT COD|MAIL| foxes nag slyly about the qui| +5893|134|10|1|43|44467.59|0.05|0.02|R|F|1992-11-02|1992-09-27|1992-11-21|TAKE BACK RETURN|RAIL|s. regular courts above the carefully silen| +5893|2|9|2|2|1804.00|0.10|0.04|R|F|1992-07-18|1992-09-10|1992-08-12|NONE|RAIL|ckages wake sly| +5894|8|5|1|23|20884.00|0.04|0.08|A|F|1994-09-05|1994-10-27|1994-09-13|NONE|TRUCK| furiously even deposits haggle alw| +5894|79|8|2|48|46995.36|0.04|0.08|A|F|1994-09-04|1994-11-03|1994-09-17|NONE|TRUCK| asymptotes among the blithely silent | +5895|15|9|1|38|34770.38|0.05|0.08|N|O|1997-04-05|1997-03-06|1997-05-03|DELIVER IN PERSON|RAIL|ts are furiously. regular, final excuses | +5895|122|3|2|47|48039.64|0.04|0.06|N|O|1997-04-27|1997-03-17|1997-05-07|DELIVER IN PERSON|AIR|r packages wake carefull| +5895|84|5|3|49|48219.92|0.03|0.07|N|O|1997-03-15|1997-02-17|1997-04-04|NONE|TRUCK|permanent foxes. packages| +5895|146|7|4|31|32430.34|0.03|0.01|N|O|1997-03-03|1997-03-30|1997-03-08|TAKE BACK RETURN|TRUCK| final deposits nod slyly careful| +5895|200|1|5|20|22004.00|0.07|0.00|N|O|1997-04-30|1997-02-07|1997-05-08|DELIVER IN PERSON|AIR|gular deposits wake blithely carefully fin| +5895|78|7|6|15|14671.05|0.08|0.08|N|O|1997-04-19|1997-03-09|1997-05-13|TAKE BACK RETURN|RAIL|silent package| +5920|187|8|1|50|54359.00|0.06|0.00|A|F|1995-03-13|1995-01-03|1995-03-31|TAKE BACK RETURN|RAIL|across the carefully pending platelets| +5920|58|9|2|24|22993.20|0.01|0.05|A|F|1994-12-28|1995-01-21|1994-12-31|DELIVER IN PERSON|FOB|fully regular dolphins. furiousl| +5920|117|1|3|2|2034.22|0.08|0.07|A|F|1995-02-18|1995-01-13|1995-03-04|NONE|SHIP| evenly spe| +5920|12|2|4|28|25536.28|0.06|0.02|R|F|1994-12-17|1995-02-13|1994-12-31|NONE|SHIP|le slyly slyly even deposits. f| +5920|100|4|5|42|42004.20|0.09|0.08|A|F|1994-12-18|1995-01-07|1995-01-14|COLLECT COD|AIR|lar, ironic dependencies sno| +5921|99|3|1|44|43959.96|0.07|0.01|R|F|1994-07-14|1994-06-30|1994-07-15|NONE|TRUCK|ain about the special| +5921|146|9|2|25|26153.50|0.06|0.01|A|F|1994-05-19|1994-06-15|1994-06-17|COLLECT COD|TRUCK|nd the slyly regular deposits. quick| +5921|68|5|3|17|16457.02|0.06|0.01|R|F|1994-05-20|1994-05-26|1994-05-23|NONE|FOB|final asymptotes. even packages boost | +5921|28|7|4|26|24128.52|0.03|0.04|A|F|1994-05-03|1994-07-06|1994-05-06|NONE|AIR|hy dependenc| +5921|143|10|5|41|42768.74|0.04|0.02|R|F|1994-04-13|1994-05-31|1994-04-26|DELIVER IN PERSON|AIR|nusual, regular theodol| +5921|115|6|6|5|5075.55|0.02|0.00|R|F|1994-06-01|1994-05-07|1994-06-10|COLLECT COD|TRUCK|eas cajole across the final, fi| +5922|196|10|1|9|9865.71|0.07|0.00|N|O|1996-12-04|1997-01-20|1996-12-08|DELIVER IN PERSON|RAIL|haggle slyly even packages. packages| +5922|157|2|2|37|39114.55|0.01|0.04|N|O|1996-12-19|1996-12-16|1997-01-15|COLLECT COD|RAIL|s wake slyly. requests cajole furiously asy| +5922|90|1|3|35|34653.15|0.08|0.00|N|O|1996-12-12|1997-01-21|1997-01-01|DELIVER IN PERSON|SHIP|accounts. regu| +5922|66|7|4|13|12558.78|0.08|0.07|N|O|1997-03-08|1996-12-26|1997-04-03|DELIVER IN PERSON|FOB|sly special accounts wake ironically.| +5922|57|5|5|39|37324.95|0.04|0.07|N|O|1997-03-04|1997-01-17|1997-03-25|TAKE BACK RETURN|SHIP|e of the instructions. quick| +5922|179|9|6|10|10791.70|0.04|0.01|N|O|1997-02-23|1996-12-26|1997-03-04|NONE|REG AIR|sly regular deposits haggle quickly ins| +5923|177|8|1|27|29083.59|0.08|0.03|N|O|1997-08-16|1997-06-27|1997-08-29|DELIVER IN PERSON|RAIL|arefully i| +5923|119|3|2|42|42802.62|0.01|0.08|N|O|1997-09-16|1997-07-23|1997-09-27|COLLECT COD|REG AIR|y regular theodolites w| +5923|108|5|3|2|2016.20|0.06|0.05|N|O|1997-06-19|1997-07-31|1997-06-28|TAKE BACK RETURN|TRUCK|express patterns. even deposits| +5923|174|4|4|46|49411.82|0.05|0.04|N|O|1997-07-29|1997-07-23|1997-08-23|COLLECT COD|SHIP|nto beans cajole blithe| +5923|59|4|5|35|33566.75|0.04|0.05|N|O|1997-07-21|1997-07-11|1997-08-01|DELIVER IN PERSON|AIR|sts affix unusual, final requests. request| +5924|176|5|1|38|40894.46|0.06|0.05|N|O|1995-12-17|1995-12-11|1996-01-06|TAKE BACK RETURN|AIR|ions cajole carefully along the | +5924|53|1|2|49|46699.45|0.04|0.00|N|O|1995-10-25|1995-12-11|1995-11-08|NONE|MAIL|inly final excuses. blithely regular requ| +5924|17|8|3|24|22008.24|0.09|0.08|N|O|1996-01-12|1995-12-13|1996-01-25|COLLECT COD|REG AIR| use carefully. special, e| +5925|87|8|1|42|41457.36|0.05|0.02|N|O|1996-03-05|1996-01-13|1996-03-10|COLLECT COD|SHIP|to the furiously| +5925|125|4|2|31|31778.72|0.03|0.03|N|O|1996-01-02|1995-12-14|1996-01-07|TAKE BACK RETURN|FOB|e slyly. furiously regular deposi| +5925|89|10|3|50|49454.00|0.03|0.04|N|O|1996-02-14|1996-01-10|1996-02-15|NONE|TRUCK|es. stealthily express pains print bli| +5925|54|9|4|30|28621.50|0.02|0.07|N|O|1996-02-21|1996-02-11|1996-03-10|NONE|TRUCK| the packa| +5925|160|1|5|41|43466.56|0.00|0.06|N|O|1996-02-03|1995-12-24|1996-02-20|NONE|SHIP| across the pending deposits nag caref| +5925|50|9|6|48|45602.40|0.02|0.00|N|O|1996-02-03|1996-01-19|1996-03-04|DELIVER IN PERSON|REG AIR| haggle after the fo| +5926|90|1|1|8|7920.72|0.02|0.00|R|F|1994-07-17|1994-07-20|1994-08-11|COLLECT COD|MAIL|gle furiously express foxes. bo| +5926|50|9|2|27|25651.35|0.09|0.05|A|F|1994-07-05|1994-08-11|1994-08-02|DELIVER IN PERSON|MAIL|ironic requests| +5926|127|8|3|46|47247.52|0.01|0.03|R|F|1994-09-05|1994-08-12|1994-09-11|COLLECT COD|RAIL|ts integrate. courts haggl| +5926|190|1|4|23|25074.37|0.01|0.02|A|F|1994-07-23|1994-08-10|1994-07-27|DELIVER IN PERSON|FOB|ickly special packages among | +5927|90|1|1|44|43563.96|0.04|0.05|N|O|1997-11-29|1997-11-21|1997-12-13|DELIVER IN PERSON|TRUCK|rding to the special, final decoy| +5927|115|2|2|8|8120.88|0.04|0.05|N|O|1997-09-24|1997-11-15|1997-10-22|TAKE BACK RETURN|SHIP|ilent dependencies nod c| +5927|167|6|3|32|34149.12|0.10|0.07|N|O|1997-12-26|1997-10-27|1997-12-31|COLLECT COD|AIR|telets. carefully bold accounts was| +5952|200|2|1|49|53909.80|0.10|0.02|N|O|1997-06-30|1997-07-10|1997-07-02|COLLECT COD|AIR|e furiously regular| +5952|191|5|2|11|12003.09|0.10|0.05|N|O|1997-05-13|1997-06-04|1997-05-27|DELIVER IN PERSON|FOB|y nag blithely aga| +5952|71|2|3|43|41756.01|0.01|0.01|N|O|1997-06-29|1997-06-06|1997-07-15|COLLECT COD|MAIL|posits sleep furiously quickly final p| +5952|158|3|4|23|24337.45|0.00|0.07|N|O|1997-05-13|1997-06-27|1997-05-20|NONE|TRUCK|e blithely packages. eve| +5953|129|10|1|36|37048.32|0.03|0.00|R|F|1992-05-28|1992-06-24|1992-05-29|DELIVER IN PERSON|FOB| cajole furio| +5953|13|7|2|34|31042.34|0.03|0.04|A|F|1992-05-04|1992-06-12|1992-06-02|NONE|RAIL|hockey players use furiously against th| +5953|162|9|3|5|5310.80|0.07|0.06|A|F|1992-04-10|1992-04-27|1992-04-14|NONE|SHIP|s. blithely | +5953|169|8|4|23|24590.68|0.09|0.02|R|F|1992-06-05|1992-06-03|1992-06-29|TAKE BACK RETURN|FOB|he silent ideas. silent foxes po| +5954|147|6|1|8|8377.12|0.03|0.00|A|F|1993-03-27|1993-01-22|1993-04-04|TAKE BACK RETURN|AIR|unusual th| +5954|81|2|2|40|39243.20|0.02|0.01|A|F|1992-12-30|1993-01-16|1993-01-09|COLLECT COD|RAIL|iously ironic deposits after| +5954|94|8|3|20|19881.80|0.09|0.07|A|F|1992-12-25|1993-02-05|1992-12-31|COLLECT COD|REG AIR| accounts wake carefu| +5954|145|4|4|20|20902.80|0.00|0.01|R|F|1993-02-27|1993-01-04|1993-03-08|NONE|TRUCK|ke furiously blithely special packa| +5954|100|4|5|35|35003.50|0.04|0.06|A|F|1993-03-17|1993-02-06|1993-04-10|NONE|SHIP|tions maintain slyly. furious| +5954|193|5|6|39|42634.41|0.04|0.08|A|F|1993-02-27|1993-02-25|1993-03-29|DELIVER IN PERSON|REG AIR| always regular dolphins. furiously p| +5955|140|1|1|14|14561.96|0.08|0.08|N|O|1995-06-22|1995-05-23|1995-06-24|DELIVER IN PERSON|TRUCK| unusual, bold theodolit| +5955|62|7|2|15|14430.90|0.08|0.07|R|F|1995-04-22|1995-05-28|1995-04-27|NONE|FOB|y final accounts above the regu| +5955|112|9|3|40|40484.40|0.03|0.00|R|F|1995-04-01|1995-06-11|1995-04-27|NONE|FOB|oss the fluffily regular| +5956|155|3|1|10|10551.50|0.04|0.05|N|O|1998-07-27|1998-07-04|1998-08-21|NONE|MAIL|ic packages am| +5956|55|7|2|23|21966.15|0.08|0.03|N|O|1998-06-06|1998-07-10|1998-06-15|DELIVER IN PERSON|RAIL|ly slyly special | +5956|175|5|3|47|50532.99|0.04|0.06|N|O|1998-09-06|1998-06-29|1998-09-18|TAKE BACK RETURN|MAIL|lyly express theodol| +5956|20|10|4|40|36800.80|0.09|0.05|N|O|1998-06-11|1998-07-19|1998-06-21|NONE|MAIL|final theodolites sleep carefully ironic c| +5957|15|9|1|37|33855.37|0.07|0.00|A|F|1994-04-18|1994-02-19|1994-05-11|NONE|AIR| ideas use ruthlessly.| +5957|59|4|2|46|44116.30|0.04|0.08|A|F|1994-01-23|1994-01-30|1994-02-07|NONE|SHIP|platelets. furiously unusual requests | +5957|2|7|3|17|15334.00|0.01|0.01|A|F|1994-01-24|1994-02-16|1994-02-08|TAKE BACK RETURN|SHIP|. final, pending packages| +5957|132|3|4|29|29931.77|0.01|0.03|R|F|1994-02-24|1994-03-04|1994-03-08|COLLECT COD|REG AIR|sits. final, even asymptotes cajole quickly| +5957|88|9|5|40|39523.20|0.04|0.04|R|F|1994-01-07|1994-02-05|1994-01-26|DELIVER IN PERSON|SHIP|ironic asymptotes sleep blithely again| +5957|6|1|6|41|37146.00|0.10|0.07|R|F|1994-03-25|1994-02-20|1994-03-31|DELIVER IN PERSON|MAIL|es across the regular requests maint| +5957|159|1|7|32|33892.80|0.10|0.07|A|F|1994-03-05|1994-02-20|1994-03-09|NONE|TRUCK| boost carefully across the | +5958|149|8|1|33|34621.62|0.02|0.04|N|O|1995-09-24|1995-12-12|1995-10-05|COLLECT COD|MAIL|lar, regular accounts wake furi| +5958|43|6|2|23|21689.92|0.03|0.04|N|O|1995-09-26|1995-10-19|1995-09-27|COLLECT COD|SHIP|regular requests. bold, bold deposits unwin| +5958|153|8|3|42|44232.30|0.10|0.00|N|O|1995-12-12|1995-10-19|1996-01-09|NONE|AIR|n accounts. final, ironic packages | +5958|39|10|4|18|16902.54|0.04|0.05|N|O|1995-12-02|1995-10-17|1995-12-22|COLLECT COD|FOB|regular requests haggle| +5958|132|8|5|32|33028.16|0.06|0.00|N|O|1995-09-20|1995-12-10|1995-10-14|COLLECT COD|REG AIR|e carefully special theodolites. carefully | +5959|135|1|1|49|50721.37|0.07|0.03|R|F|1992-07-16|1992-08-09|1992-08-14|DELIVER IN PERSON|SHIP|usual packages haggle slyly pi| +5959|147|8|2|17|17801.38|0.09|0.07|R|F|1992-06-10|1992-07-06|1992-06-23|COLLECT COD|MAIL|ackages. blithely ex| +5959|5|6|3|4|3620.00|0.04|0.03|R|F|1992-06-14|1992-07-05|1992-07-01|NONE|MAIL|gular requests ar| +5959|196|7|4|13|14250.47|0.03|0.00|A|F|1992-07-29|1992-07-13|1992-08-20|COLLECT COD|SHIP|ar forges. deposits det| +5959|40|6|5|37|34781.48|0.04|0.01|R|F|1992-06-05|1992-07-18|1992-06-29|NONE|TRUCK|endencies. brai| +5959|119|3|6|35|35668.85|0.03|0.00|A|F|1992-05-27|1992-06-19|1992-06-23|NONE|TRUCK|ely silent deposits. | +5959|43|10|7|47|44322.88|0.02|0.01|R|F|1992-08-28|1992-07-24|1992-09-09|TAKE BACK RETURN|RAIL|deposits. slyly special cou| +5984|70|5|1|13|12610.91|0.06|0.07|R|F|1994-10-16|1994-09-06|1994-11-11|NONE|MAIL|lar platelets. f| +5984|102|3|2|25|25052.50|0.05|0.08|R|F|1994-10-06|1994-07-21|1994-10-28|COLLECT COD|RAIL|gular accounts. even packages nag slyly| +5984|1|4|3|8|7208.00|0.10|0.00|R|F|1994-09-17|1994-08-28|1994-09-25|COLLECT COD|RAIL|its. express,| +5984|190|1|4|35|38156.65|0.00|0.01|A|F|1994-08-25|1994-08-05|1994-08-31|DELIVER IN PERSON|SHIP|le fluffily regula| +5985|86|7|1|4|3944.32|0.02|0.02|A|F|1995-05-04|1995-04-01|1995-05-17|DELIVER IN PERSON|MAIL|ole along the quickly slow d| +5986|79|7|1|26|25455.82|0.00|0.00|R|F|1992-08-10|1992-05-23|1992-08-24|TAKE BACK RETURN|SHIP|e fluffily ironic ideas. silent | +5986|196|8|2|25|27404.75|0.03|0.06|A|F|1992-06-16|1992-07-17|1992-06-29|TAKE BACK RETURN|MAIL| instructions. slyly regular de| +5986|30|5|3|1|930.03|0.07|0.06|A|F|1992-05-21|1992-06-21|1992-05-24|DELIVER IN PERSON|REG AIR|fix quickly quickly final deposits. fluffil| +5986|90|1|4|31|30692.79|0.00|0.03|A|F|1992-08-21|1992-06-29|1992-09-14|NONE|AIR|structions! furiously pending instructi| +5986|136|7|5|6|6216.78|0.05|0.02|A|F|1992-07-16|1992-06-10|1992-07-29|DELIVER IN PERSON|RAIL|al foxes within the slyly speci| +5987|23|2|1|1|923.02|0.01|0.04|N|O|1996-09-13|1996-10-29|1996-09-21|DELIVER IN PERSON|REG AIR|refully final excuses haggle furiously ag| +5987|176|5|2|20|21523.40|0.10|0.06|N|O|1996-11-28|1996-09-17|1996-12-05|TAKE BACK RETURN|RAIL|ing excuses nag quickly always bold| +5987|92|3|3|43|42659.87|0.08|0.04|N|O|1996-10-30|1996-10-13|1996-11-12|NONE|AIR|theodolites wake above the furiously b| +5987|97|1|4|37|36892.33|0.08|0.08|N|O|1996-10-15|1996-10-27|1996-11-09|NONE|MAIL|le furiously carefully special | +5988|172|1|1|41|43958.97|0.08|0.03|R|F|1994-01-20|1994-02-06|1994-02-10|COLLECT COD|AIR|the pending, express reque| diff --git a/zetasql/examples/tpch/catalog/nation.tbl b/zetasql/examples/tpch/catalog/nation.tbl new file mode 100644 index 000000000..ed3fd5b8c --- /dev/null +++ b/zetasql/examples/tpch/catalog/nation.tbl @@ -0,0 +1,25 @@ +0|ALGERIA|0| haggle. carefully final deposits detect slyly agai| +1|ARGENTINA|1|al foxes promise slyly according to the regular accounts. bold requests alon| +2|BRAZIL|1|y alongside of the pending deposits. carefully special packages are about the ironic forges. slyly special | +3|CANADA|1|eas hang ironic, silent packages. slyly regular packages are furiously over the tithes. fluffily bold| +4|EGYPT|4|y above the carefully unusual theodolites. final dugouts are quickly across the furiously regular d| +5|ETHIOPIA|0|ven packages wake quickly. regu| +6|FRANCE|3|refully final requests. regular, ironi| +7|GERMANY|3|l platelets. regular accounts x-ray: unusual, regular acco| +8|INDIA|2|ss excuses cajole slyly across the packages. deposits print aroun| +9|INDONESIA|2| slyly express asymptotes. regular deposits haggle slyly. carefully ironic hockey players sleep blithely. carefull| +10|IRAN|4|efully alongside of the slyly final dependencies. | +11|IRAQ|4|nic deposits boost atop the quickly final requests? quickly regula| +12|JAPAN|2|ously. final, express gifts cajole a| +13|JORDAN|4|ic deposits are blithely about the carefully regular pa| +14|KENYA|0| pending excuses haggle furiously deposits. pending, express pinto beans wake fluffily past t| +15|MOROCCO|0|rns. blithely bold courts among the closely regular packages use furiously bold platelets?| +16|MOZAMBIQUE|0|s. ironic, unusual asymptotes wake blithely r| +17|PERU|1|platelets. blithely pending dependencies use fluffily across the even pinto beans. carefully silent accoun| +18|CHINA|2|c dependencies. furiously express notornis sleep slyly regular accounts. ideas sleep. depos| +19|ROMANIA|3|ular asymptotes are about the furious multipliers. express dependencies nag above the ironically ironic account| +20|SAUDI ARABIA|4|ts. silent requests haggle. closely express packages sleep across the blithely| +21|VIETNAM|2|hely enticingly express accounts. even, final | +22|RUSSIA|3| requests against the platelets use never according to the quickly regular pint| +23|UNITED KINGDOM|3|eans boost carefully special requests. accounts are. carefull| +24|UNITED STATES|1|y final packages. slow foxes cajole quickly. quickly silent platelets breach ironic accounts. unusual pinto be| diff --git a/zetasql/examples/tpch/catalog/orders.tbl b/zetasql/examples/tpch/catalog/orders.tbl new file mode 100644 index 000000000..1ebd66354 --- /dev/null +++ b/zetasql/examples/tpch/catalog/orders.tbl @@ -0,0 +1,1500 @@ +1|37|O|131251.81|1996-01-02|5-LOW|Clerk#000000951|0|nstructions sleep furiously among | +2|79|O|40183.29|1996-12-01|1-URGENT|Clerk#000000880|0| foxes. pending accounts at the pending, silent asymptot| +3|124|F|160882.76|1993-10-14|5-LOW|Clerk#000000955|0|sly final accounts boost. carefully regular ideas cajole carefully. depos| +4|137|O|31084.79|1995-10-11|5-LOW|Clerk#000000124|0|sits. slyly regular warthogs cajole. regular, regular theodolites acro| +5|46|F|86615.25|1994-07-30|5-LOW|Clerk#000000925|0|quickly. bold deposits sleep slyly. packages use slyly| +6|56|F|36468.55|1992-02-21|4-NOT SPECIFIED|Clerk#000000058|0|ggle. special, final requests are against the furiously specia| +7|40|O|171488.73|1996-01-10|2-HIGH|Clerk#000000470|0|ly special requests | +32|131|O|116923.00|1995-07-16|2-HIGH|Clerk#000000616|0|ise blithely bold, regular requests. quickly unusual dep| +33|67|F|99798.76|1993-10-27|3-MEDIUM|Clerk#000000409|0|uriously. furiously final request| +34|62|O|41670.02|1998-07-21|3-MEDIUM|Clerk#000000223|0|ly final packages. fluffily final deposits wake blithely ideas. spe| +35|128|O|148789.52|1995-10-23|4-NOT SPECIFIED|Clerk#000000259|0|zzle. carefully enticing deposits nag furio| +36|116|O|38988.98|1995-11-03|1-URGENT|Clerk#000000358|0| quick packages are blithely. slyly silent accounts wake qu| +37|88|F|113701.89|1992-06-03|3-MEDIUM|Clerk#000000456|0|kly regular pinto beans. carefully unusual waters cajole never| +38|125|O|46366.56|1996-08-21|4-NOT SPECIFIED|Clerk#000000604|0|haggle blithely. furiously express ideas haggle blithely furiously regular re| +39|82|O|219707.84|1996-09-20|3-MEDIUM|Clerk#000000659|0|ole express, ironic requests: ir| +64|34|F|20065.73|1994-07-16|3-MEDIUM|Clerk#000000661|0|wake fluffily. sometimes ironic pinto beans about the dolphin| +65|17|P|65883.92|1995-03-18|1-URGENT|Clerk#000000632|0|ular requests are blithely pending orbits-- even requests against the deposit| +66|130|F|79258.24|1994-01-20|5-LOW|Clerk#000000743|0|y pending requests integrate| +67|58|O|116227.05|1996-12-19|4-NOT SPECIFIED|Clerk#000000547|0|symptotes haggle slyly around the furiously iron| +68|29|O|215135.72|1998-04-18|3-MEDIUM|Clerk#000000440|0| pinto beans sleep carefully. blithely ironic deposits haggle furiously acro| +69|85|F|162176.23|1994-06-04|4-NOT SPECIFIED|Clerk#000000330|0| depths atop the slyly thin deposits detect among the furiously silent accou| +70|65|F|84651.80|1993-12-18|5-LOW|Clerk#000000322|0| carefully ironic request| +71|4|O|178821.73|1998-01-24|4-NOT SPECIFIED|Clerk#000000271|0| express deposits along the blithely regul| +96|109|F|55090.67|1994-04-17|2-HIGH|Clerk#000000395|0|oost furiously. pinto| +97|22|F|68908.31|1993-01-29|3-MEDIUM|Clerk#000000547|0|hang blithely along the regular accounts. furiously even ideas after the| +98|106|F|51004.44|1994-09-25|1-URGENT|Clerk#000000448|0|c asymptotes. quickly regular packages should have to nag re| +99|89|F|92326.79|1994-03-13|4-NOT SPECIFIED|Clerk#000000973|0|e carefully ironic packages. pending| +100|148|O|141311.01|1998-02-28|4-NOT SPECIFIED|Clerk#000000577|0|heodolites detect slyly alongside of the ent| +101|28|O|95591.40|1996-03-17|3-MEDIUM|Clerk#000000419|0|ding accounts above the slyly final asymptote| +102|1|O|113954.89|1997-05-09|2-HIGH|Clerk#000000596|0| slyly according to the asymptotes. carefully final packages integrate furious| +103|31|O|95563.95|1996-06-20|4-NOT SPECIFIED|Clerk#000000090|0|ges. carefully unusual instructions haggle quickly regular f| +128|74|F|36333.34|1992-06-15|1-URGENT|Clerk#000000385|0|ns integrate fluffily. ironic asymptotes after the regular excuses nag around | +129|73|F|188124.55|1992-11-19|5-LOW|Clerk#000000859|0|ing tithes. carefully pending deposits boost about the silently express | +130|37|F|115717.37|1992-05-08|2-HIGH|Clerk#000000036|0|le slyly unusual, regular packages? express deposits det| +131|94|F|96596.81|1994-06-08|3-MEDIUM|Clerk#000000625|0|after the fluffily special foxes integrate s| +132|28|F|118802.62|1993-06-11|3-MEDIUM|Clerk#000000488|0|sits are daringly accounts. carefully regular foxes sleep slyly about the| +133|44|O|80437.72|1997-11-29|1-URGENT|Clerk#000000738|0|usly final asymptotes | +134|7|F|154260.84|1992-05-01|4-NOT SPECIFIED|Clerk#000000711|0|lar theodolites boos| +135|61|O|174569.88|1995-10-21|4-NOT SPECIFIED|Clerk#000000804|0|l platelets use according t| +160|83|O|86076.86|1996-12-19|4-NOT SPECIFIED|Clerk#000000342|0|thely special sauternes wake slyly of t| +161|17|F|19056.99|1994-08-31|2-HIGH|Clerk#000000322|0|carefully! special instructions sin| +162|16|O|2158.13|1995-05-08|3-MEDIUM|Clerk#000000378|0|nts hinder fluffily ironic instructions. express, express excuses | +163|88|O|125170.86|1997-09-05|3-MEDIUM|Clerk#000000379|0|y final packages. final foxes since the quickly even| +164|1|F|202660.52|1992-10-21|5-LOW|Clerk#000000209|0|cajole ironic courts. slyly final ideas are slyly. blithely final Tiresias sub| +165|28|F|141824.23|1993-01-30|4-NOT SPECIFIED|Clerk#000000292|0|across the blithely regular accounts. bold| +166|109|O|93335.60|1995-09-12|2-HIGH|Clerk#000000440|0|lets. ironic, bold asymptotes kindle| +167|121|F|52982.23|1993-01-04|4-NOT SPECIFIED|Clerk#000000731|0|s nag furiously bold excuses. fluffily iron| +192|83|O|133002.55|1997-11-25|5-LOW|Clerk#000000483|0|y unusual platelets among the final instructions integrate rut| +193|80|F|48053.18|1993-08-08|1-URGENT|Clerk#000000025|0|the furiously final pin| +194|62|F|114097.63|1992-04-05|3-MEDIUM|Clerk#000000352|0|egular requests haggle slyly regular, regular pinto beans. asymptote| +195|136|F|120053.52|1993-12-28|3-MEDIUM|Clerk#000000216|0|old forges are furiously sheaves. slyly fi| +196|65|F|33248.04|1993-03-17|2-HIGH|Clerk#000000988|0|beans boost at the foxes. silent foxes| +197|34|P|100290.07|1995-04-07|2-HIGH|Clerk#000000969|0|solve quickly about the even braids. carefully express deposits affix care| +198|112|O|125792.83|1998-01-02|4-NOT SPECIFIED|Clerk#000000331|0|its. carefully ironic requests sleep. furiously express fox| +199|53|O|80592.44|1996-03-07|2-HIGH|Clerk#000000489|0|g theodolites. special packag| +224|4|F|155680.60|1994-06-18|4-NOT SPECIFIED|Clerk#000000642|0|r the quickly thin courts. carefully| +225|34|P|165890.47|1995-05-25|1-URGENT|Clerk#000000177|0|s. blithely ironic accounts wake quickly fluffily special acc| +226|128|F|180119.22|1993-03-10|2-HIGH|Clerk#000000756|0|s are carefully at the blithely ironic acc| +227|10|O|46076.46|1995-11-10|5-LOW|Clerk#000000919|0| express instructions. slyly regul| +228|46|F|2638.98|1993-02-25|1-URGENT|Clerk#000000562|0|es was slyly among the regular foxes. blithely regular dependenci| +229|112|F|142290.77|1993-12-29|1-URGENT|Clerk#000000628|0|he fluffily even instructions. furiously i| +230|103|F|107231.60|1993-10-27|1-URGENT|Clerk#000000520|0|odolites. carefully quick requ| +231|91|F|141554.06|1994-09-29|2-HIGH|Clerk#000000446|0| packages haggle slyly after the carefully ironic instruct| +256|125|F|106315.25|1993-10-19|4-NOT SPECIFIED|Clerk#000000834|0|he fluffily final ideas might are final accounts. carefully f| +257|124|O|7102.74|1998-03-28|3-MEDIUM|Clerk#000000680|0|ts against the sly warhorses cajole slyly accounts| +258|43|F|186669.10|1993-12-29|1-URGENT|Clerk#000000167|0|dencies. blithely quick packages cajole. ruthlessly final accounts| +259|44|F|75661.70|1993-09-29|4-NOT SPECIFIED|Clerk#000000601|0|ages doubt blithely against the final foxes. carefully express deposits dazzle| +260|106|O|179292.14|1996-12-10|3-MEDIUM|Clerk#000000960|0|lently regular pinto beans sleep after the slyly e| +261|47|F|201003.12|1993-06-29|3-MEDIUM|Clerk#000000310|0|ully fluffily brave instructions. furiousl| +262|31|O|108443.84|1995-11-25|4-NOT SPECIFIED|Clerk#000000551|0|l packages. blithely final pinto beans use carefu| +263|118|F|79782.56|1994-05-17|2-HIGH|Clerk#000000088|0| pending instructions. blithely un| +288|8|O|163794.53|1997-02-21|1-URGENT|Clerk#000000109|0|uriously final requests. even, final ideas det| +289|104|O|131092.67|1997-02-10|3-MEDIUM|Clerk#000000103|0|sily. slyly special excuse| +290|118|F|62814.89|1994-01-01|4-NOT SPECIFIED|Clerk#000000735|0|efully dogged deposits. furiou| +291|142|F|66817.05|1994-03-13|1-URGENT|Clerk#000000923|0|dolites. carefully regular pinto beans cajol| +292|23|F|30783.05|1992-01-13|2-HIGH|Clerk#000000193|0|g pinto beans will have to sleep f| +293|31|F|37248.78|1992-10-02|2-HIGH|Clerk#000000629|0|re bold, ironic deposits. platelets c| +294|52|F|30059.47|1993-07-16|3-MEDIUM|Clerk#000000499|0|kly according to the frays. final dolphins affix quickly | +295|19|F|89345.99|1994-09-29|2-HIGH|Clerk#000000155|0| unusual pinto beans play. regular ideas haggle| +320|1|O|39835.54|1997-11-21|2-HIGH|Clerk#000000573|0|ar foxes nag blithely| +321|124|F|62251.15|1993-03-21|3-MEDIUM|Clerk#000000289|0|equests run. blithely final dependencies after the deposits wake caref| +322|134|F|127068.89|1992-03-19|1-URGENT|Clerk#000000158|0|fully across the slyly bold packages. packages against the quickly regular i| +323|40|F|79683.42|1994-03-26|1-URGENT|Clerk#000000959|0|arefully pending foxes sleep blithely. slyly express accoun| +324|106|F|26868.85|1992-03-20|1-URGENT|Clerk#000000352|0| about the ironic, regular deposits run blithely against the excuses| +325|41|F|71543.41|1993-10-17|5-LOW|Clerk#000000844|0|ly sometimes pending pa| +326|76|O|229165.17|1995-06-04|2-HIGH|Clerk#000000466|0| requests. furiously ironic asymptotes mold carefully alongside of the blit| +327|145|P|24468.16|1995-04-17|5-LOW|Clerk#000000992|0|ng the slyly final courts. slyly even escapades eat | +352|107|F|16003.86|1994-03-08|2-HIGH|Clerk#000000932|0|ke slyly bold pinto beans. blithely regular accounts against the spe| +353|2|F|179984.42|1993-12-31|5-LOW|Clerk#000000449|0| quiet ideas sleep. even instructions cajole slyly. silently spe| +354|139|O|157062.70|1996-03-14|2-HIGH|Clerk#000000511|0|ly regular ideas wake across the slyly silent ideas. final deposits eat b| +355|71|F|69447.25|1994-06-14|5-LOW|Clerk#000000532|0|s. sometimes regular requests cajole. regular, pending accounts a| +356|148|F|162786.67|1994-06-30|4-NOT SPECIFIED|Clerk#000000944|0|as wake along the bold accounts. even, | +357|61|O|98723.11|1996-10-09|2-HIGH|Clerk#000000301|0|e blithely about the express, final accounts. quickl| +358|4|F|226806.66|1993-09-20|2-HIGH|Clerk#000000392|0|l, silent instructions are slyly. silently even de| +359|79|F|142891.22|1994-12-19|3-MEDIUM|Clerk#000000934|0|n dolphins. special courts above the carefully ironic requests use| +384|115|F|122785.82|1992-03-03|5-LOW|Clerk#000000206|0|, even accounts use furiously packages. slyly ironic pla| +385|34|O|50724.06|1996-03-22|5-LOW|Clerk#000000600|0|hless accounts unwind bold pain| +386|61|F|90380.40|1995-01-25|2-HIGH|Clerk#000000648|0| haggle quickly. stealthily bold asymptotes haggle among the furiously even re| +387|4|O|130647.18|1997-01-26|4-NOT SPECIFIED|Clerk#000000768|0| are carefully among the quickly even deposits. furiously silent req| +388|46|F|120533.46|1992-12-16|4-NOT SPECIFIED|Clerk#000000356|0|ar foxes above the furiously ironic deposits nag slyly final reque| +389|127|F|1984.14|1994-02-17|2-HIGH|Clerk#000000062|0|ing to the regular asymptotes. final, pending foxes about the blithely sil| +390|103|O|168562.27|1998-04-07|5-LOW|Clerk#000000404|0|xpress asymptotes use among the regular, final pinto b| +391|112|F|13282.23|1994-11-17|2-HIGH|Clerk#000000256|0|orges thrash fluffil| +416|41|F|71362.50|1993-09-27|5-LOW|Clerk#000000294|0| the accounts. fluffily bold depo| +417|55|F|91982.29|1994-02-06|3-MEDIUM|Clerk#000000468|0|ironic, even packages. thinly unusual accounts sleep along the slyly unusual | +418|95|P|33124.96|1995-04-13|4-NOT SPECIFIED|Clerk#000000643|0|. furiously ironic instruc| +419|118|O|111597.96|1996-10-01|3-MEDIUM|Clerk#000000376|0|osits. blithely pending theodolites boost carefully| +420|91|O|198039.23|1995-10-31|4-NOT SPECIFIED|Clerk#000000756|0|leep carefully final excuses. fluffily pending requests unwind carefully above| +421|40|F|1084.38|1992-02-22|5-LOW|Clerk#000000405|0|egular, even packages according to the final, un| +422|74|O|106045.89|1997-05-31|4-NOT SPECIFIED|Clerk#000000049|0|aggle carefully across the accounts. regular accounts eat fluffi| +423|104|O|26981.31|1996-06-01|1-URGENT|Clerk#000000674|0|quests. deposits cajole quickly. furiously bold accounts haggle q| +448|149|O|114978.03|1995-08-21|3-MEDIUM|Clerk#000000597|0| regular, express foxes use blithely. quic| +449|97|O|41605.63|1995-07-20|2-HIGH|Clerk#000000841|0|. furiously regular theodolites affix blithely | +450|49|P|153386.61|1995-03-05|4-NOT SPECIFIED|Clerk#000000293|0|d theodolites. boldly bold foxes since the pack| +451|100|O|104664.40|1998-05-25|5-LOW|Clerk#000000048|0|nic pinto beans. theodolites poach carefully; | +452|61|O|2007.48|1997-10-14|1-URGENT|Clerk#000000498|0|t, unusual instructions above the blithely bold pint| +453|46|O|216826.73|1997-05-26|5-LOW|Clerk#000000504|0|ss foxes. furiously regular ideas sleep according to t| +454|49|O|23198.24|1995-12-27|5-LOW|Clerk#000000890|0|dolites sleep carefully blithely regular deposits. quickly regul| +455|13|O|138010.76|1996-12-04|1-URGENT|Clerk#000000796|0| about the final platelets. dependen| +480|73|F|20530.97|1993-05-08|5-LOW|Clerk#000000004|0|ealthy pinto beans. fluffily regular requests along the special sheaves wake | +481|31|F|117827.18|1992-10-08|2-HIGH|Clerk#000000230|0|ly final ideas. packages haggle fluffily| +482|127|O|136634.34|1996-03-26|1-URGENT|Clerk#000000295|0|ts. deposits wake: final acco| +483|35|O|39793.05|1995-07-11|2-HIGH|Clerk#000000025|0|cross the carefully final e| +484|55|O|219920.62|1997-01-03|3-MEDIUM|Clerk#000000545|0|grouches use. furiously bold accounts maintain. bold, regular deposits| +485|101|O|110432.76|1997-03-26|2-HIGH|Clerk#000000105|0| regular ideas nag thinly furiously s| +486|52|O|185968.15|1996-03-11|4-NOT SPECIFIED|Clerk#000000803|0|riously dolphins. fluffily ironic requ| +487|109|F|48502.79|1992-08-18|1-URGENT|Clerk#000000086|0|ithely unusual courts eat accordi| +512|64|P|124661.48|1995-05-20|5-LOW|Clerk#000000814|0|ding requests. carefully express theodolites was quickly. furious| +513|61|O|63703.92|1995-05-01|2-HIGH|Clerk#000000522|0|regular packages. pinto beans cajole carefully against the even| +514|76|O|104585.77|1996-04-04|2-HIGH|Clerk#000000094|0| cajole furiously. slyly final excuses cajole. slyly special instructions | +515|142|F|153720.22|1993-08-29|4-NOT SPECIFIED|Clerk#000000700|0|eposits are furiously furiously silent pinto beans. pending pack| +516|44|O|10677.86|1998-04-21|2-HIGH|Clerk#000000305|0|lar, unusual platelets are carefully. even courts sleep bold, final pinto bea| +517|10|O|82197.79|1997-04-07|5-LOW|Clerk#000000359|0|slyly pending deposits cajole quickly packages. furiou| +518|145|O|223537.09|1998-02-08|2-HIGH|Clerk#000000768|0| the carefully bold accounts. quickly regular excuses are| +519|64|O|95731.50|1997-10-31|1-URGENT|Clerk#000000985|0|ains doze furiously against the f| +544|94|F|47627.89|1993-02-17|2-HIGH|Clerk#000000145|0|the special, final accounts. dogged dolphins| +545|64|O|23476.12|1995-11-07|2-HIGH|Clerk#000000537|0|as. blithely final hockey players about th| +546|145|O|14790.37|1996-11-01|2-HIGH|Clerk#000000041|0|osits sleep. slyly special dolphins about the q| +547|100|O|96855.29|1996-06-22|3-MEDIUM|Clerk#000000976|0|ing accounts eat. carefully regular packa| +548|124|F|99088.75|1994-09-21|1-URGENT|Clerk#000000435|0|arefully express instru| +549|110|F|141679.41|1992-07-13|1-URGENT|Clerk#000000196|0|ideas alongside of | +550|25|O|33123.28|1995-08-02|1-URGENT|Clerk#000000204|0|t requests. blithely | +551|91|O|46355.83|1995-05-30|1-URGENT|Clerk#000000179|0|xpress accounts boost quic| +576|31|O|18307.45|1997-05-13|3-MEDIUM|Clerk#000000955|0|l requests affix regular requests. final account| +577|56|F|34768.68|1994-12-19|5-LOW|Clerk#000000154|0| deposits engage stealthil| +578|94|O|70392.02|1997-01-10|5-LOW|Clerk#000000281|0|e blithely even packages. slyly pending platelets bes| +579|68|O|120828.12|1998-03-11|2-HIGH|Clerk#000000862|0| regular instructions. blithely even p| +580|61|O|88219.12|1997-07-05|2-HIGH|Clerk#000000314|0|tegrate fluffily regular accou| +581|70|O|126066.00|1997-02-23|4-NOT SPECIFIED|Clerk#000000239|0| requests. even requests use slyly. blithely ironic | +582|50|O|129004.81|1997-10-21|1-URGENT|Clerk#000000378|0|n pinto beans print a| +583|49|O|127817.38|1997-03-19|3-MEDIUM|Clerk#000000792|0|efully express requests. a| +608|26|O|62567.99|1996-02-28|3-MEDIUM|Clerk#000000995|0|nic waters wake slyly slyly expre| +609|127|F|21088.59|1994-06-01|3-MEDIUM|Clerk#000000348|0|- ironic gifts believe furiously ca| +610|52|O|175142.28|1995-08-02|1-URGENT|Clerk#000000610|0|totes. ironic, unusual packag| +611|106|F|73907.63|1993-01-27|1-URGENT|Clerk#000000401|0|ounts detect furiously ac| +612|82|F|145695.42|1992-10-21|3-MEDIUM|Clerk#000000759|0|boost quickly quickly final excuses. final foxes use bravely afte| +613|139|O|33396.35|1995-06-18|2-HIGH|Clerk#000000172|0|ts hinder among the deposits. fluffily ironic depos| +614|134|F|218116.21|1992-12-01|2-HIGH|Clerk#000000388|0| deposits! even, daring theodol| +615|67|F|32890.89|1992-05-09|5-LOW|Clerk#000000388|0|t to promise asymptotes. packages haggle alongside of the fluffil| +640|97|F|145495.62|1993-01-23|2-HIGH|Clerk#000000433|0|r, unusual accounts boost carefully final ideas. slyly silent theod| +641|133|F|120626.49|1993-08-30|5-LOW|Clerk#000000175|0|ents cajole furiously about the quickly silent pac| +642|40|F|22994.51|1993-12-16|3-MEDIUM|Clerk#000000357|0| among the requests wake slyly alongside of th| +643|58|P|180396.95|1995-03-25|2-HIGH|Clerk#000000354|0|g dependencies. regular accounts | +644|8|F|201268.06|1992-05-01|1-URGENT|Clerk#000000550|0| blithely unusual platelets haggle ironic, special excuses. excuses unwi| +645|115|F|234763.73|1994-12-03|2-HIGH|Clerk#000000090|0|quickly daring theodolites across the regu| +646|52|F|142070.65|1994-11-22|2-HIGH|Clerk#000000203|0|carefully even foxes. fina| +647|143|O|56449.23|1997-08-07|1-URGENT|Clerk#000000270|0|egular pearls. carefully express asymptotes are. even account| +672|109|F|89877.09|1994-04-14|5-LOW|Clerk#000000106|0|egular requests are furiously according to | +673|80|F|21137.08|1994-03-10|1-URGENT|Clerk#000000448|0| special pinto beans use quickly furiously even depende| +674|34|F|27204.60|1992-08-29|5-LOW|Clerk#000000448|0|ully special deposits. furiously final warhorses affix carefully. fluffily f| +675|13|O|125188.72|1997-07-31|2-HIGH|Clerk#000000168|0|ffily between the careful| +676|38|O|163966.67|1996-12-13|2-HIGH|Clerk#000000248|0|the final deposits. special, pending| +677|124|F|147915.68|1993-11-24|3-MEDIUM|Clerk#000000824|0|uriously special pinto beans cajole carefully. fi| +678|131|F|135761.05|1993-02-27|5-LOW|Clerk#000000530|0|. blithely final somas about the| +679|49|O|8945.03|1995-12-15|2-HIGH|Clerk#000000853|0|tealthy, final pinto beans haggle slyly. pending platelets about the special, | +704|85|O|56210.26|1996-11-21|3-MEDIUM|Clerk#000000682|0|blithely pending platelets wake alongside of the final, iron| +705|43|O|83773.49|1997-02-13|4-NOT SPECIFIED|Clerk#000000294|0|ithely regular dependencies. express, even packages sleep slyly pending t| +706|148|O|23973.60|1995-09-09|1-URGENT|Clerk#000000448|0|g the packages. deposits caj| +707|118|F|58218.35|1994-11-20|3-MEDIUM|Clerk#000000199|0| ideas about the silent, bold deposits nag dolphins| +708|32|O|100445.59|1998-07-03|3-MEDIUM|Clerk#000000101|0|lphins cajole about t| +709|37|O|72055.87|1998-04-21|1-URGENT|Clerk#000000461|0|ons alongside of the carefully bold pinto bea| +710|133|F|208974.42|1993-01-02|5-LOW|Clerk#000000026|0| regular, regular requests boost. fluffily re| +711|64|F|92484.70|1993-09-23|4-NOT SPECIFIED|Clerk#000000856|0|its. fluffily regular gifts are furi| +736|47|O|130204.17|1998-06-21|5-LOW|Clerk#000000881|0|refully of the final pi| +737|121|F|12984.85|1992-04-26|5-LOW|Clerk#000000233|0|ake blithely express, ironic theodolites. blithely special accounts wa| +738|22|F|114145.18|1993-03-02|4-NOT SPECIFIED|Clerk#000000669|0|ly even foxes. furiously regular accounts cajole ca| +739|1|O|159171.69|1998-05-31|5-LOW|Clerk#000000900|0| against the slyly ironic packages nag slyly ironic| +740|44|O|83490.99|1995-07-16|3-MEDIUM|Clerk#000000583|0|courts haggle furiously across the final, regul| +741|106|O|47985.98|1998-07-07|2-HIGH|Clerk#000000295|0|ic instructions. slyly express instructions solv| +742|103|F|207632.55|1994-12-23|5-LOW|Clerk#000000543|0|equests? slyly ironic dolphins boost carefully above the blithely| +743|79|O|23614.89|1996-10-04|4-NOT SPECIFIED|Clerk#000000933|0|eans. furiously ironic deposits sleep carefully carefully qui| +768|98|O|220636.82|1996-08-20|3-MEDIUM|Clerk#000000411|0|jole slyly ironic packages. slyly even idea| +769|80|F|43092.76|1993-06-02|3-MEDIUM|Clerk#000000172|0|ggle furiously. ironic packages haggle slyly. bold platelets affix s| +770|32|O|64271.75|1998-05-23|5-LOW|Clerk#000000572|0|heodolites. furiously special pinto beans cajole pac| +771|46|O|105302.05|1995-06-17|1-URGENT|Clerk#000000105|0|s. furiously final instructions across the deposit| +772|97|F|128234.96|1993-04-17|2-HIGH|Clerk#000000430|0|s boost blithely fluffily idle ideas? fluffily even pin| +773|133|F|146862.27|1993-09-26|3-MEDIUM|Clerk#000000307|0|tions are quickly accounts. accounts use bold, even pinto beans. gifts ag| +774|80|O|145857.60|1995-12-04|1-URGENT|Clerk#000000883|0|tealthily even depths| +775|134|F|59455.61|1995-03-18|1-URGENT|Clerk#000000191|0|kly express requests. fluffily silent accounts poach furiously| +800|56|O|87892.38|1998-07-14|2-HIGH|Clerk#000000213|0|y alongside of the pending packages? final platelets nag fluffily carefu| +801|118|F|127717.72|1992-02-18|1-URGENT|Clerk#000000186|0|iously from the furiously enticing reques| +802|137|F|156381.95|1995-01-05|1-URGENT|Clerk#000000516|0|posits. ironic, pending requests cajole. even theodol| +803|16|O|27629.66|1997-04-29|5-LOW|Clerk#000000260|0|ic instructions. even deposits haggle furiously at the deposits-- regular de| +804|50|F|94400.43|1993-03-12|3-MEDIUM|Clerk#000000931|0|s. blithely final foxes are about the packag| +805|127|O|90042.41|1995-07-05|4-NOT SPECIFIED|Clerk#000000856|0|y according to the fluffily | +806|131|O|26839.16|1996-06-20|2-HIGH|Clerk#000000240|0| the ironic packages wake carefully fina| +807|145|F|222392.53|1993-11-24|3-MEDIUM|Clerk#000000012|0|refully special tithes. blithely regular accoun| +832|29|F|68494.08|1992-04-19|5-LOW|Clerk#000000495|0|xes. bravely regular packages sleep up the furiously bold accou| +833|56|F|49033.69|1994-02-13|3-MEDIUM|Clerk#000000437|0|ts haggle quickly across the slyl| +834|43|F|46459.92|1994-05-23|3-MEDIUM|Clerk#000000805|0| sleep. quickly even foxes are boldly. slyly express requests use slyly| +835|65|O|62430.67|1995-10-08|4-NOT SPECIFIED|Clerk#000000416|0|s about the carefully special foxes haggle quickly about the| +836|70|O|72843.48|1996-11-25|4-NOT SPECIFIED|Clerk#000000729|0|ely bold excuses sleep regular ideas. furiously unusual ideas wake furiou| +837|116|F|60918.41|1994-06-15|4-NOT SPECIFIED|Clerk#000000563|0|kages sleep slyly above the ironic, final orbits| +838|17|O|82918.36|1998-01-29|5-LOW|Clerk#000000213|0| slyly around the slyly even| +839|28|O|70182.63|1995-08-08|1-URGENT|Clerk#000000951|0|the carefully even platelets. furiously unusual fo| +864|139|O|74710.74|1997-08-17|1-URGENT|Clerk#000000036|0|ly after the slyly regular deposits. express, regular asymptotes nag ca| +865|4|F|70430.54|1993-05-04|3-MEDIUM|Clerk#000000337|0|. special packages wake after the carefully final accounts. express pinto be| +866|40|F|4766.19|1992-11-28|3-MEDIUM|Clerk#000000718|0|ins after the even, even accounts nod blithel| +867|26|F|7471.75|1993-11-16|3-MEDIUM|Clerk#000000877|0|pades nag quickly final, | +868|104|F|127345.45|1992-06-09|4-NOT SPECIFIED|Clerk#000000782|0|onic theodolites print carefully. blithely dogge| +869|136|O|58932.19|1997-01-12|2-HIGH|Clerk#000000245|0|ar sheaves are slowly. slyly even attainments boost theodolites. furiously| +870|34|F|40492.37|1993-06-20|4-NOT SPECIFIED|Clerk#000000123|0|blithely ironic ideas nod. sly, r| +871|16|O|172861.58|1995-11-15|5-LOW|Clerk#000000882|0|oss the ironic theodolites.| +896|2|F|169847.63|1993-03-09|1-URGENT|Clerk#000000187|0|inal packages eat blithely according to the warhorses. furiously quiet de| +897|49|P|57697.44|1995-03-20|1-URGENT|Clerk#000000316|0| wake quickly against | +898|55|F|101020.75|1993-06-03|2-HIGH|Clerk#000000611|0|. unusual pinto beans haggle quickly across | +899|109|O|125562.09|1998-04-08|5-LOW|Clerk#000000575|0|rts engage carefully final theodolites.| +900|46|F|120073.51|1994-10-01|4-NOT SPECIFIED|Clerk#000000060|0| fluffily express deposits nag furiousl| +901|13|O|81826.12|1998-07-21|4-NOT SPECIFIED|Clerk#000000929|0|lyly even foxes are furious, silent requests. requests about the quickly | +902|10|F|37348.62|1994-07-27|4-NOT SPECIFIED|Clerk#000000811|0|yly final requests over the furiously regula| +903|11|O|109351.87|1995-07-07|4-NOT SPECIFIED|Clerk#000000793|0|e slyly about the final pl| +928|67|F|228136.49|1995-03-02|5-LOW|Clerk#000000450|0|ithely express pinto beans. | +929|83|F|109301.02|1992-10-02|2-HIGH|Clerk#000000160|0|its. furiously even foxes affix carefully finally silent accounts. express req| +930|131|F|199102.23|1994-12-17|1-URGENT|Clerk#000000004|0| accounts nag slyly. ironic, ironic accounts wake blithel| +931|103|F|117909.23|1992-12-07|1-URGENT|Clerk#000000881|0|ss packages haggle furiously express, regular deposits. even, e| +932|41|O|40234.50|1997-05-16|2-HIGH|Clerk#000000218|0|ly express instructions boost furiously reg| +933|97|F|71349.30|1992-08-05|4-NOT SPECIFIED|Clerk#000000752|0|ial courts wake permanently against the furiously regular ideas. unusual | +934|52|O|17213.59|1996-07-03|1-URGENT|Clerk#000000229|0|ts integrate carefully. sly, regular deposits af| +935|50|O|97733.87|1997-09-24|5-LOW|Clerk#000000180|0|iously final deposits cajole. blithely even packages | +960|35|F|63537.13|1994-09-21|3-MEDIUM|Clerk#000000120|0|regular accounts. requests| +961|56|P|158893.16|1995-06-04|4-NOT SPECIFIED|Clerk#000000720|0|ons nag furiously among the quickl| +962|37|F|98258.73|1994-05-06|5-LOW|Clerk#000000463|0|ments nag deposits. fluffily ironic a| +963|26|F|53287.25|1994-05-26|3-MEDIUM|Clerk#000000497|0|uses haggle carefully. slyly even dependencies after the packages ha| +964|76|O|131146.47|1995-05-20|3-MEDIUM|Clerk#000000657|0|print blithely ironic, careful theodolit| +965|70|P|41758.44|1995-05-15|5-LOW|Clerk#000000218|0|iously special packages. slyly pending requests are carefully | +966|14|O|120516.93|1998-04-30|2-HIGH|Clerk#000000239|0|special deposits. furious| +967|110|F|179287.95|1992-06-21|3-MEDIUM|Clerk#000000167|0|excuses engage quickly bold dep| +992|55|O|133665.12|1997-11-11|3-MEDIUM|Clerk#000000875|0|ts. regular pinto beans thrash carefully sl| +993|80|O|198238.65|1995-09-10|3-MEDIUM|Clerk#000000894|0|quickly express accounts among the furiously bol| +994|2|F|41433.48|1994-04-20|5-LOW|Clerk#000000497|0|ole. slyly bold excuses nag caref| +995|116|P|135157.92|1995-05-31|3-MEDIUM|Clerk#000000439|0|deas. blithely final deposits play. express accounts wake blithely caref| +996|71|O|47447.63|1997-12-29|1-URGENT|Clerk#000000497|0|arefully final packages into the slyly final requests affix blit| +997|109|O|27561.82|1997-05-19|2-HIGH|Clerk#000000651|0|ly express depths. furiously final requests haggle furiously. carefu| +998|32|F|65269.38|1994-11-26|4-NOT SPECIFIED|Clerk#000000956|0|ronic dolphins. ironic, bold ideas haggle furiously furious| +999|61|F|145249.13|1993-09-05|5-LOW|Clerk#000000464|0|pitaphs sleep. regular accounts use. f| +1024|4|O|176084.63|1997-12-23|5-LOW|Clerk#000000903|0| blithely. even, express theodolites cajole slyly across| +1025|103|F|82034.03|1995-05-05|2-HIGH|Clerk#000000376|0|ross the slyly final pa| +1026|73|O|36464.76|1997-06-04|5-LOW|Clerk#000000223|0|s wake blithely. special acco| +1027|128|F|112770.89|1992-06-03|3-MEDIUM|Clerk#000000241|0|equests cajole. slyly final pinto bean| +1028|70|F|153864.67|1994-01-01|2-HIGH|Clerk#000000131|0|ts are. final, silent deposits are among the fl| +1029|130|F|47440.91|1994-06-21|2-HIGH|Clerk#000000700|0|quests sleep. slyly even foxes wake quickly final theodolites. clo| +1030|134|F|16346.94|1994-06-15|5-LOW|Clerk#000000422|0|ully ironic accounts sleep carefully. requests are carefully alongside of the | +1031|4|F|128024.71|1994-09-01|3-MEDIUM|Clerk#000000448|0|s; ironic theodolites along the carefully ex| +1056|28|F|38446.39|1995-02-11|1-URGENT|Clerk#000000125|0|t, even deposits hang about the slyly special i| +1057|76|F|108107.42|1992-02-20|1-URGENT|Clerk#000000124|0|cuses dazzle carefully careful, ironic pinto beans. carefully even theod| +1058|53|F|89359.11|1993-04-26|3-MEDIUM|Clerk#000000373|0|kly pending courts haggle. blithely regular sheaves integrate carefully fi| +1059|127|F|198360.22|1994-02-27|1-URGENT|Clerk#000000104|0|en accounts. carefully bold packages cajole daringly special depende| +1060|140|F|121994.04|1993-02-21|3-MEDIUM|Clerk#000000989|0|l platelets sleep quickly slyly special requests. furiously | +1061|103|O|166947.75|1998-05-15|5-LOW|Clerk#000000576|0|uests sleep at the packages. fur| +1062|106|O|39805.04|1997-01-15|1-URGENT|Clerk#000000152|0|eposits use blithely | +1063|37|F|41392.31|1994-04-02|2-HIGH|Clerk#000000024|0|deposits nag quickly regular deposits. quickl| +1088|148|F|47120.41|1992-05-21|5-LOW|Clerk#000000347|0|counts are blithely. platelets print. carefully | +1089|49|O|103192.74|1996-05-04|4-NOT SPECIFIED|Clerk#000000226|0|ns haggle ruthlessly. even requests are quickly abov| +1090|19|O|32929.30|1997-11-15|2-HIGH|Clerk#000000300|0| furiously regular platelets haggle along the slyly unusual foxes! | +1091|83|O|35795.22|1996-08-27|1-URGENT|Clerk#000000549|0| even pinto beans haggle quickly alongside of the eve| +1092|124|P|85552.21|1995-03-04|3-MEDIUM|Clerk#000000006|0|re quickly along the blithe| +1093|101|O|79189.58|1997-07-31|4-NOT SPECIFIED|Clerk#000000159|0| after the carefully ironic requests. carefully ironic packages wake fluffil| +1094|145|O|9006.25|1997-12-24|3-MEDIUM|Clerk#000000570|0|beans affix furiously about the pending, even deposits. finally pendi| +1095|145|O|178491.24|1995-08-22|3-MEDIUM|Clerk#000000709|0|sly bold requests cajole carefully according to| +1120|140|O|107958.62|1997-11-07|3-MEDIUM|Clerk#000000319|0|lly special requests. slyly pending platelets are quickly pending requ| +1121|29|O|241837.88|1997-01-13|3-MEDIUM|Clerk#000000541|0|r escapades. deposits above the fluffily bold requests hag| +1122|121|O|179747.47|1997-01-10|1-URGENT|Clerk#000000083|0|uffily carefully final theodolites. furiously express packages affix| +1123|73|O|93259.93|1996-08-03|3-MEDIUM|Clerk#000000929|0|uriously pending requests. slyly regular instruction| +1124|80|O|141858.97|1998-07-30|5-LOW|Clerk#000000326|0|regular pinto beans along the fluffily silent packages| +1125|25|F|80438.38|1994-10-27|2-HIGH|Clerk#000000510|0|ithely final requests. i| +1126|145|O|59982.31|1998-01-28|4-NOT SPECIFIED|Clerk#000000928|0|d slyly regular ideas: special ideas believe slyly. slyly ironic sheaves w| +1127|58|O|103320.91|1995-09-19|4-NOT SPECIFIED|Clerk#000000397|0|usly silent, regular pinto beans. blithely express requests boos| +1152|49|F|51775.54|1994-08-14|4-NOT SPECIFIED|Clerk#000000496|0|equests. deposits ab| +1153|121|O|220727.97|1996-04-18|5-LOW|Clerk#000000059|0| across the pending deposi| +1154|37|F|192417.85|1992-02-15|1-URGENT|Clerk#000000268|0|old asymptotes are special requests. blithely even deposits sleep furiously| +1155|149|O|126902.81|1997-10-06|2-HIGH|Clerk#000000164|0|c deposits haggle among the ironic, even requests. carefully ironic sheaves n| +1156|133|O|217682.81|1996-10-19|1-URGENT|Clerk#000000200|0| blithely ironic dolphins. furiously pendi| +1157|97|O|85394.06|1998-01-14|4-NOT SPECIFIED|Clerk#000000207|0|out the regular excuses boost carefully against the furio| +1158|142|O|31075.51|1996-06-30|2-HIGH|Clerk#000000549|0|integrate slyly furiously ironic deposit| +1159|70|F|55553.68|1992-09-18|3-MEDIUM|Clerk#000000992|0|ts may sleep. requests according to the| +1184|89|O|39700.29|1997-10-26|5-LOW|Clerk#000000777|0|iously even packages haggle fluffily care| +1185|74|F|47033.21|1992-08-24|5-LOW|Clerk#000000344|0| even escapades are. package| +1186|59|O|82026.18|1996-08-15|4-NOT SPECIFIED|Clerk#000000798|0|ingly regular pinto beans: instructi| +1187|134|F|85948.02|1992-11-20|3-MEDIUM|Clerk#000000047|0|s after the furiously final deposits boost slyly under the| +1188|20|O|54655.07|1996-04-11|2-HIGH|Clerk#000000256|0|ully ironic deposits. slyl| +1189|46|F|71017.99|1994-04-09|1-URGENT|Clerk#000000243|0|f the even accounts. courts print blithely ironic accounts. sile| +1190|13|O|31043.39|1997-03-16|5-LOW|Clerk#000000575|0|ccounts above the foxes integrate carefully after the | +1191|112|O|28623.04|1995-11-07|3-MEDIUM|Clerk#000000011|0|uests nag furiously. carefully even requests| +1216|122|F|68056.57|1992-12-07|5-LOW|Clerk#000000918|0|nal foxes around the e| +1217|7|F|40982.08|1992-04-26|4-NOT SPECIFIED|Clerk#000000538|0| foxes nag quickly. ironic excuses nod. blithely pending| +1218|10|F|99834.47|1994-06-20|4-NOT SPECIFIED|Clerk#000000994|0|s cajole. special, silent deposits about the theo| +1219|28|O|10163.56|1995-10-05|3-MEDIUM|Clerk#000000800|0|od carefully. slyly final dependencies across the even fray| +1220|49|O|122157.14|1996-08-29|1-URGENT|Clerk#000000712|0|inal theodolites wake. fluffily ironic asymptotes cajol| +1221|14|F|117397.16|1992-04-19|4-NOT SPECIFIED|Clerk#000000852|0| detect against the silent, even deposits. carefully ironic| +1222|10|F|47623.94|1993-02-05|3-MEDIUM|Clerk#000000811|0|theodolites use quickly even accounts. carefully final asympto| +1223|10|O|26714.67|1996-05-25|4-NOT SPECIFIED|Clerk#000000238|0|posits was blithely fr| +1248|49|F|210713.88|1992-01-02|1-URGENT|Clerk#000000890|0|t the carefully regular dugouts. s| +1249|149|F|45889.09|1994-01-05|1-URGENT|Clerk#000000095|0|al ideas sleep above the pending pin| +1250|37|F|12907.62|1992-09-29|4-NOT SPECIFIED|Clerk#000000652|0|ts after the fluffily pending instructions use slyly about the s| +1251|38|O|109536.55|1997-10-30|1-URGENT|Clerk#000000276|0|, brave sauternes. deposits boost fluffily.| +1252|149|O|93403.05|1997-08-04|5-LOW|Clerk#000000348|0|ng the slyly regular excuses. special courts nag furiously blithely e| +1253|115|F|92730.74|1993-01-26|1-URGENT|Clerk#000000775|0| requests sleep furiously even foxes. ruthless packag| +1254|70|O|94649.25|1995-12-22|1-URGENT|Clerk#000000607|0| pinto beans. carefully regular request| +1255|122|F|62518.31|1994-05-30|4-NOT SPECIFIED|Clerk#000000798|0|ct slyly regular accounts. quick| +1280|97|F|91664.85|1993-01-11|5-LOW|Clerk#000000160|0|posits thrash quickly after the theodolites. furiously iro| +1281|62|F|165454.51|1994-12-11|1-URGENT|Clerk#000000430|0|counts. carefully pending accounts eat | +1282|116|F|61297.42|1992-02-29|4-NOT SPECIFIED|Clerk#000000168|0|he quickly special packages. furiously final re| +1283|118|O|202623.92|1996-08-30|4-NOT SPECIFIED|Clerk#000000260|0| pinto beans boost slyly ac| +1284|134|O|106122.38|1996-01-07|2-HIGH|Clerk#000000492|0|s. blithely silent deposits s| +1285|11|F|139124.72|1992-06-01|1-URGENT|Clerk#000000423|0|cial deposits cajole after the ironic requests. p| +1286|109|F|207291.83|1993-05-14|4-NOT SPECIFIED|Clerk#000000939|0| deposits use carefully from the excuses. slyly bold p| +1287|19|F|131432.42|1994-07-05|2-HIGH|Clerk#000000288|0|ly ironic dolphins integrate furiously among the final packages. st| +1312|112|F|58111.00|1994-05-19|3-MEDIUM|Clerk#000000538|0|n, express accounts across the ironic| +1313|148|F|46598.65|1994-09-13|1-URGENT|Clerk#000000774|0|ld accounts. regular deposits cajole. ironically pending theodolites use car| +1314|143|F|56207.66|1994-05-13|3-MEDIUM|Clerk#000000485|0|ickly blithe packages nod ideas. furiously bold braids boost around the car| +1315|22|O|121935.23|1998-03-22|5-LOW|Clerk#000000840|0|final theodolites alongside of the carefu| +1316|16|F|163746.47|1993-12-03|1-URGENT|Clerk#000000857|0|ully bold theodolites? pending, bold pin| +1317|100|P|139714.71|1995-05-19|2-HIGH|Clerk#000000373|0|sts. furiously special deposits lose fur| +1318|128|O|81663.65|1998-06-27|3-MEDIUM|Clerk#000000581|0|s hang bold requests. pending, re| +1319|32|O|31103.83|1996-09-27|2-HIGH|Clerk#000000257|0|y across the ruthlessly ironic accounts. unusu| +1344|17|F|43809.37|1992-04-16|5-LOW|Clerk#000000178|0|omise close, silent requests. pending theodolites boost pending | +1345|95|F|111207.93|1992-10-28|5-LOW|Clerk#000000447|0| regular tithes. quickly fluffy de| +1346|76|F|171975.62|1992-06-18|2-HIGH|Clerk#000000374|0|ges sleep quickly-- even pint| +1347|41|O|173444.60|1997-06-20|5-LOW|Clerk#000000977|0|he furiously even foxes use carefully express req| +1348|19|O|94135.77|1998-04-18|5-LOW|Clerk#000000206|0|tly. quickly even deposi| +1349|64|O|46376.09|1997-10-26|1-URGENT|Clerk#000000543|0|yly! blithely special theodolites cajole. unusual, reg| +1350|52|F|49305.98|1993-08-24|1-URGENT|Clerk#000000635|0|iously about the blithely special a| +1351|106|O|24637.96|1998-04-20|1-URGENT|Clerk#000000012|0| cajole. regular, special re| +1376|47|O|23984.88|1997-05-04|4-NOT SPECIFIED|Clerk#000000730|0|der furiously final, final frets. carefull| +1377|20|O|108334.30|1998-04-24|4-NOT SPECIFIED|Clerk#000000625|0|lly across the blithely express accounts. ironic excuses promise carefully de| +1378|20|O|118495.12|1996-03-09|4-NOT SPECIFIED|Clerk#000000705|0| furiously even tithes cajole slyly among the quick| +1379|65|O|84627.76|1998-05-25|5-LOW|Clerk#000000861|0|y deposits are caref| +1380|137|O|94969.41|1996-07-07|3-MEDIUM|Clerk#000000969|0|inal deposits wake slyly daringly even requests. bold, even foxe| +1381|127|O|58212.22|1998-05-25|3-MEDIUM|Clerk#000000107|0|even requests breach after the bold, ironic instructions. slyly even| +1382|133|F|173522.71|1993-08-17|5-LOW|Clerk#000000241|0|fully final packages sl| +1383|121|F|34797.72|1993-04-27|2-HIGH|Clerk#000000785|0|ts. express requests sleep blithel| +1408|55|O|183965.61|1997-12-26|4-NOT SPECIFIED|Clerk#000000942|0|t the quickly final asymptotes. unusual| +1409|143|F|72440.52|1992-12-31|4-NOT SPECIFIED|Clerk#000000065|0|ructions. furiously unusual excuses are regular, unusual theodolites. fin| +1410|113|O|114879.19|1997-04-12|5-LOW|Clerk#000000123|0|iously along the bravely regular dolphins. pinto beans cajole furiously sp| +1411|95|F|164462.61|1994-12-21|2-HIGH|Clerk#000000566|0|s. furiously special excuses across the pending pinto beans haggle sp| +1412|53|F|78676.54|1993-03-13|4-NOT SPECIFIED|Clerk#000000083|0|uffily daring theodolit| +1413|91|O|75733.58|1997-06-14|3-MEDIUM|Clerk#000000342|0|, ironic instructions. carefully even packages dazzle| +1414|77|O|38057.81|1995-08-16|1-URGENT|Clerk#000000883|0|ccounts. ironic foxes haggle car| +1415|79|F|24654.79|1994-05-29|4-NOT SPECIFIED|Clerk#000000601|0|rays. blithely final ideas affix quickl| +1440|98|O|50201.16|1995-08-10|5-LOW|Clerk#000000956|0| pending requests. closely s| +1441|122|O|156477.94|1997-03-06|4-NOT SPECIFIED|Clerk#000000156|0|ter the excuses. ironic dependencies m| +1442|112|F|7108.12|1994-07-05|4-NOT SPECIFIED|Clerk#000000935|0|nal pinto beans. slyly ironic ideas cajol| +1443|44|O|44672.03|1996-12-16|5-LOW|Clerk#000000185|0|x blithely against the carefully final somas. even asymptotes are. quickly spe| +1444|134|F|207907.60|1994-12-06|3-MEDIUM|Clerk#000000783|0|ove the bold accounts cajole fluffily about| +1445|115|F|154653.32|1995-01-10|3-MEDIUM|Clerk#000000211|0|even packages wake fluffily | +1446|41|O|27663.16|1998-02-16|5-LOW|Clerk#000000274|0|lly regular notornis above the requests sleep final accounts! | +1447|91|F|108171.38|1992-10-15|2-HIGH|Clerk#000000880|0|inly against the blithely pending excuses. regular, pe| +1472|149|O|65331.05|1996-10-06|5-LOW|Clerk#000000303|0|y special dolphins around the final dependencies wake quick| +1473|94|O|80624.38|1997-03-17|3-MEDIUM|Clerk#000000960|0|furiously close accoun| +1474|70|F|51697.18|1995-01-09|1-URGENT|Clerk#000000438|0|detect quickly above the carefully even | +1475|5|O|185496.66|1997-11-12|2-HIGH|Clerk#000000972|0|cally final packages boost. blithely ironic packa| +1476|145|O|18795.62|1996-06-27|2-HIGH|Clerk#000000673|0|ding accounts hinder alongside of the quickly pending requests. fluf| +1477|76|O|231831.35|1997-08-24|5-LOW|Clerk#000000612|0|ly bold foxes. final ideas would cajo| +1478|50|O|20791.50|1997-08-03|2-HIGH|Clerk#000000827|0|lessly. carefully express| +1479|16|O|31471.04|1995-12-16|4-NOT SPECIFIED|Clerk#000000697|0|he furiously even foxes. thinly bold deposits| +1504|2|F|89399.40|1992-08-28|3-MEDIUM|Clerk#000000381|0|, brave deposits. bold de| +1505|37|F|55892.35|1992-08-21|2-HIGH|Clerk#000000544|0|s. slyly ironic packages cajole. carefully regular packages haggle | +1506|148|F|195844.84|1992-09-21|3-MEDIUM|Clerk#000000620|0| dependencies. accounts affix blithely slowly unusual deposits. slyly regular | +1507|121|F|96166.92|1993-10-14|3-MEDIUM|Clerk#000000305|0|stealthy, ironic de| +1508|103|O|151282.65|1998-04-10|5-LOW|Clerk#000000117|0| after the furiously regular pinto beans hang slyly quickly ironi| +1509|64|F|180455.98|1993-07-08|5-LOW|Clerk#000000770|0|the regular ideas. regul| +1510|53|O|154590.05|1996-09-17|5-LOW|Clerk#000000128|0|ld carefully. furiously final asymptotes haggle furiously| +1511|79|O|59651.38|1996-12-22|4-NOT SPECIFIED|Clerk#000000386|0|ts above the depend| +1536|94|O|5184.26|1997-01-26|3-MEDIUM|Clerk#000000117|0|ges are! furiously final deposits cajole iron| +1537|109|F|108317.51|1992-02-15|4-NOT SPECIFIED|Clerk#000000862|0|g to the even deposits. ironic, final packages | +1538|29|O|179554.41|1995-06-18|4-NOT SPECIFIED|Clerk#000000258|0| instructions. regular theod| +1539|112|F|39612.63|1995-03-10|5-LOW|Clerk#000000840|0|nstructions boost pa| +1540|16|F|128014.15|1992-08-05|2-HIGH|Clerk#000000927|0|r ideas hinder blithe| +1541|94|P|47286.32|1995-05-18|1-URGENT|Clerk#000000906|0|y. slyly ironic warhorses around the furiously regul| +1542|143|F|132972.24|1993-09-15|3-MEDIUM|Clerk#000000435|0|t the furiously close deposits do was f| +1543|52|O|139047.22|1997-02-20|1-URGENT|Clerk#000000398|0|unts. furiously pend| +1568|17|O|76119.72|1997-01-30|4-NOT SPECIFIED|Clerk#000000554|0|d notornis. carefully | +1569|104|O|87803.55|1998-04-02|5-LOW|Clerk#000000786|0|orbits. fluffily even decoys serve blithely. furiously furious realms nag acro| +1570|124|O|35589.57|1998-03-16|1-URGENT|Clerk#000000745|0|pinto beans haggle furiousl| +1571|103|F|151404.78|1992-12-05|2-HIGH|Clerk#000000565|0|ously furiously bold warthogs. slyly ironic instructions are quickly a| +1572|11|O|47232.79|1996-02-24|2-HIGH|Clerk#000000994|0|fluffily ironic accounts haggle blithely final platelets! slyly regular foxes| +1573|148|F|86918.57|1992-12-28|2-HIGH|Clerk#000000940|0|ess, ironic deposits use along the carefu| +1574|134|O|179923.54|1996-12-12|3-MEDIUM|Clerk#000000809|0| ideas hinder after the carefully unusual | +1575|145|O|197031.52|1995-09-13|3-MEDIUM|Clerk#000000497|0|. furiously regular dep| +1600|94|F|130515.61|1993-03-03|3-MEDIUM|Clerk#000000627|0|tions cajole quietly above the regular, silent requests. slyly fin| +1601|53|F|73962.95|1994-08-27|5-LOW|Clerk#000000469|0|ent deposits are ca| +1602|1|F|4225.26|1993-08-05|5-LOW|Clerk#000000660|0|deposits. busily silent instructions haggle furiously. fin| +1603|2|F|29305.47|1993-07-31|4-NOT SPECIFIED|Clerk#000000869|0|s. slyly silent deposits boo| +1604|113|F|107139.29|1993-07-17|5-LOW|Clerk#000000512|0|lithely silent waters. blithely unusual packages alongside | +1605|58|O|130687.64|1998-04-24|4-NOT SPECIFIED|Clerk#000000616|0|sleep furiously? ruthless, even pinto beans | +1606|53|O|115877.40|1997-04-17|4-NOT SPECIFIED|Clerk#000000550|0|r requests. quickly even platelets breach before the ironically| +1607|149|O|166335.03|1995-12-16|2-HIGH|Clerk#000000498|0| bold, pending foxes haggle. slyly silent | +1632|67|O|183286.33|1997-01-08|3-MEDIUM|Clerk#000000351|0|onic requests are accounts. bold a| +1633|16|O|52359.51|1995-10-14|2-HIGH|Clerk#000000666|0|y silent accounts sl| +1634|70|O|145898.47|1996-09-10|1-URGENT|Clerk#000000360|0|arefully blithely ironic requests. slyly unusual instructions alongside| +1635|4|O|70232.26|1997-02-13|3-MEDIUM|Clerk#000000958|0|s. slyly ironic requests affix slyly | +1636|79|O|172021.87|1997-06-17|3-MEDIUM|Clerk#000000457|0|ding requests. slyly ironic courts wake quickl| +1637|73|F|180912.15|1995-02-08|4-NOT SPECIFIED|Clerk#000000189|0| final accounts. blithely silent ideas cajole bravely. carefully express | +1638|139|O|172436.30|1997-08-13|2-HIGH|Clerk#000000643|0|he fluffily regular asymp| +1639|5|O|104166.56|1995-08-20|4-NOT SPECIFIED|Clerk#000000939|0|haggle furiously. final requests detect furious| +1664|64|O|178060.22|1996-03-03|1-URGENT|Clerk#000000090|0|y quickly even asymptotes. furiously regular packages haggle quickly fin| +1665|76|F|4819.91|1994-05-08|2-HIGH|Clerk#000000920|0|ly regular packages are fluffily even ideas. fluffily final| +1666|95|O|128367.97|1995-10-18|1-URGENT|Clerk#000000849|0|ffily pending dependencies wake fluffily. pending, final accounts | +1667|5|O|125030.37|1997-10-10|2-HIGH|Clerk#000000103|0|e accounts. slyly express accounts must are a| +1668|142|O|137576.19|1997-07-12|4-NOT SPECIFIED|Clerk#000000148|0|eodolites. carefully dogged dolphins haggle q| +1669|2|O|24362.39|1997-06-09|3-MEDIUM|Clerk#000000663|0|er ironic requests detect furiously blithely sp| +1670|25|O|89999.72|1997-05-24|2-HIGH|Clerk#000000320|0|unusual dependencies. furiously special platelets main| +1671|35|O|104391.11|1996-07-27|4-NOT SPECIFIED|Clerk#000000275|0|ly. slyly pending requests was above the | +1696|4|O|102665.03|1998-01-08|4-NOT SPECIFIED|Clerk#000000041|0|bravely bold accounts above the quickly bold| +1697|76|O|122621.31|1996-10-07|1-URGENT|Clerk#000000815|0|o x-ray blithely. pl| +1698|40|O|141118.87|1997-04-23|2-HIGH|Clerk#000000432|0|slyly. carefully express deposit| +1699|85|F|66408.29|1993-12-30|1-URGENT|Clerk#000000125|0|jole blithely. furiously un| +1700|65|O|89143.36|1996-06-15|3-MEDIUM|Clerk#000000328|0|ely final dolphins wake sometimes above the quietly regular deposits. fur| +1701|130|F|72835.95|1992-05-19|2-HIGH|Clerk#000000395|0|furiously. regular, close theodoli| +1702|67|P|194119.31|1995-05-07|2-HIGH|Clerk#000000300|0|around the carefully final deposits cajole carefully according to the b| +1703|134|F|121220.59|1993-01-28|3-MEDIUM|Clerk#000000463|0| pinto beans poach. bold courts boost. regular, express deposits at| +1728|64|O|131604.34|1996-05-22|2-HIGH|Clerk#000000711|0|beans. slyly regular instructions sleep! slyly final packages| +1729|133|F|12137.76|1992-05-19|2-HIGH|Clerk#000000158|0|pending foxes wake. accounts| +1730|124|O|150886.49|1998-07-24|5-LOW|Clerk#000000794|0| fluffily pending deposits serve. furiously even requests wake furiou| +1731|128|O|190490.78|1996-01-06|1-URGENT|Clerk#000000268|0|lithely regular, final instructions. ironic, express packages are above| +1732|146|F|179854.51|1993-11-29|5-LOW|Clerk#000000903|0|inal requests integrate dolph| +1733|148|O|165489.52|1996-05-12|2-HIGH|Clerk#000000789|0|e carefully according to the accounts. furiously pending instructions sleep| +1734|7|F|44002.53|1994-06-11|2-HIGH|Clerk#000000722|0| final ideas haggle. blithely quick foxes sleep busily bold ideas. i| +1735|22|F|98541.95|1992-12-27|1-URGENT|Clerk#000000458|0|ully idle requests wake qu| +1760|115|O|82151.12|1996-05-17|5-LOW|Clerk#000000917|0| deposits. busily regular deposits wake blithely along the furiously even re| +1761|106|F|211925.95|1993-12-24|2-HIGH|Clerk#000000817|0|efully slyly bold frets. packages boost b| +1762|77|F|202227.17|1994-08-20|4-NOT SPECIFIED|Clerk#000000653|0|ly ironic packages. furi| +1763|121|O|140685.01|1996-10-29|2-HIGH|Clerk#000000321|0|es. bold dependencies haggle furiously along | +1764|29|F|47384.71|1992-03-25|1-URGENT|Clerk#000000182|0|. slyly final packages integrate carefully acro| +1765|73|O|36551.43|1995-12-03|4-NOT SPECIFIED|Clerk#000000490|0| regular excuses wake slyly| +1766|139|O|41032.81|1996-10-12|2-HIGH|Clerk#000000983|0|unusual deposits affix quickly beyond the carefully s| +1767|25|P|136582.60|1995-03-14|2-HIGH|Clerk#000000327|0|eposits use carefully carefully regular platelets. quickly regular packages al| +1792|49|F|107919.86|1993-11-09|5-LOW|Clerk#000000102|0|ructions haggle along the pending packages. carefully speci| +1793|19|F|82504.56|1992-07-12|4-NOT SPECIFIED|Clerk#000000291|0|regular packages cajole. blithely special packages according to the final d| +1794|140|O|179462.21|1997-09-28|1-URGENT|Clerk#000000686|0|ally silent pinto beans. regular package| +1795|94|F|146849.33|1994-03-19|2-HIGH|Clerk#000000815|0| quickly final packages! blithely dogged accounts c| +1796|47|F|33755.47|1992-11-21|2-HIGH|Clerk#000000245|0|eans use furiously around th| +1797|125|O|51494.47|1996-05-07|3-MEDIUM|Clerk#000000508|0|quiet platelets haggle since the quickly ironic instructi| +1798|52|O|46393.97|1997-07-28|1-URGENT|Clerk#000000741|0|al foxes are blithe| +1799|61|F|46815.93|1994-03-07|4-NOT SPECIFIED|Clerk#000000339|0|ns sleep furiously final waters. blithely regular instructions h| +1824|49|F|81351.53|1994-05-05|1-URGENT|Clerk#000000972|0|e blithely fluffily| +1825|148|F|150582.77|1993-12-05|3-MEDIUM|Clerk#000000345|0|ironic, final accou| +1826|82|F|124719.97|1992-04-16|4-NOT SPECIFIED|Clerk#000000718|0|the even asymptotes dazzle fluffily slyly regular asymptotes. final, unu| +1827|106|O|210113.88|1996-06-22|4-NOT SPECIFIED|Clerk#000000369|0|luffily even requests haggle sly| +1828|32|F|137369.50|1994-04-18|3-MEDIUM|Clerk#000000840|0|y quickly bold packag| +1829|112|F|127532.20|1994-05-08|2-HIGH|Clerk#000000537|0| accounts wake above the furiously unusual requests. pending package| +1830|133|F|85122.24|1995-02-23|1-URGENT|Clerk#000000045|0|according to the even,| +1831|71|F|58032.77|1993-12-02|1-URGENT|Clerk#000000854|0| accounts. carefully even accounts boost furiously. regular ideas engage. | +1856|106|F|189361.42|1992-03-20|4-NOT SPECIFIED|Clerk#000000952|0|. special pinto beans run acr| +1857|133|F|102793.59|1993-01-13|2-HIGH|Clerk#000000083|0|hely final ideas slee| +1858|143|O|30457.91|1997-12-13|1-URGENT|Clerk#000000389|0|thely. slyly final deposits sleep| +1859|61|O|105094.09|1997-04-11|4-NOT SPECIFIED|Clerk#000000949|0| the foxes. bravely special excuses nag carefully special r| +1860|10|O|9103.40|1996-04-04|3-MEDIUM|Clerk#000000556|0|osits. quickly bold deposits according to | +1861|70|F|95063.41|1994-01-03|3-MEDIUM|Clerk#000000847|0|r the fluffily close sauternes. furio| +1862|34|O|97981.06|1998-02-24|5-LOW|Clerk#000000348|0|ts snooze ironically abou| +1863|74|F|96359.65|1993-09-23|4-NOT SPECIFIED|Clerk#000000658|0|old sentiments. careful, | +1888|121|F|224724.11|1993-10-31|4-NOT SPECIFIED|Clerk#000000659|0|olites. pinto beans cajole. regular deposits affix. slyly regular| +1889|25|O|96431.77|1997-03-16|1-URGENT|Clerk#000000854|0|p around the regular notornis. unusual deposits| +1890|10|O|202364.58|1996-12-18|4-NOT SPECIFIED|Clerk#000000627|0|romise final, regular deposits. regular fox| +1891|61|F|76848.96|1994-12-15|5-LOW|Clerk#000000495|0|unusual foxes sleep regular deposits. requests wake special pac| +1892|25|F|133273.64|1994-03-26|5-LOW|Clerk#000000733|0|sts. slyly regular dependencies use slyly. ironic, spec| +1893|125|O|116792.13|1997-10-30|2-HIGH|Clerk#000000111|0|olites. silent, special deposits eat slyly quickly express packages; hockey p| +1894|76|F|44387.23|1992-03-30|1-URGENT|Clerk#000000626|0|e furiously. furiously even accounts are slyly final accounts. closely speci| +1895|7|F|44429.81|1994-05-30|3-MEDIUM|Clerk#000000878|0|ress accounts. bold accounts cajole. slyly final pinto beans poach regul| +1920|110|O|119605.91|1998-06-24|5-LOW|Clerk#000000018|0|hely; furiously regular excuses| +1921|88|F|57584.12|1994-01-18|3-MEDIUM|Clerk#000000293|0|counts. slyly quiet requests along the ruthlessly regular accounts are | +1922|56|O|11575.77|1996-07-13|3-MEDIUM|Clerk#000000984|0|side of the blithely final re| +1923|136|O|171128.10|1997-07-07|1-URGENT|Clerk#000000471|0| express dolphins. | +1924|76|O|169756.19|1996-09-07|4-NOT SPECIFIED|Clerk#000000823|0| of the ironic accounts. instructions near the final instr| +1925|17|F|146382.71|1992-03-05|1-URGENT|Clerk#000000986|0|e slyly regular deposits. furiously | +1926|94|O|100035.03|1996-01-31|2-HIGH|Clerk#000000568|0|cajole. even warhorses sleep carefully. | +1927|140|O|23327.88|1995-09-30|3-MEDIUM|Clerk#000000616|0|riously special packages. permanent pearls wake furiously. even packages alo| +1952|67|F|12896.25|1994-03-16|2-HIGH|Clerk#000000254|0| silent accounts boost | +1953|149|F|57213.18|1993-11-30|3-MEDIUM|Clerk#000000891|0| fluffily along the quickly even packages. | +1954|56|O|158853.63|1997-05-31|4-NOT SPECIFIED|Clerk#000000104|0| unusual excuses cajole according to the blithely regular theodolites.| +1955|13|F|103085.13|1992-04-20|1-URGENT|Clerk#000000792|0|ly special ideas. sometimes final | +1956|127|F|88704.26|1992-09-20|4-NOT SPECIFIED|Clerk#000000600|0|ironic ideas are silent ideas. furiously final deposits sleep slyly carefu| +1957|31|O|77482.87|1998-07-21|2-HIGH|Clerk#000000639|0|nding excuses about the | +1958|53|O|176294.34|1995-09-22|5-LOW|Clerk#000000343|0| haggle blithely. flu| +1959|43|O|62277.18|1997-01-13|4-NOT SPECIFIED|Clerk#000000631|0| cajole about the blithely express requests. even excuses mold bl| +1984|52|O|79230.47|1998-04-01|1-URGENT|Clerk#000000416|0| slyly special instructions. unusual foxes use packages. carefully regular req| +1985|7|F|171522.54|1994-09-02|4-NOT SPECIFIED|Clerk#000000741|0|slyly slyly even pains. slyly reg| +1986|149|F|34269.96|1994-05-05|2-HIGH|Clerk#000000609|0|across the theodolites. quick| +1987|100|F|6406.29|1994-04-30|2-HIGH|Clerk#000000652|0|gular platelets alongside | +1988|109|O|117132.72|1995-10-06|4-NOT SPECIFIED|Clerk#000000011|0|ly ironic dolphins serve quickly busy accounts. bu| +1989|118|F|39263.28|1994-03-16|4-NOT SPECIFIED|Clerk#000000747|0|ely bold pinto beans ha| +1990|119|F|48781.39|1994-12-16|2-HIGH|Clerk#000000114|0|e bold patterns. always regul| +1991|19|F|139854.41|1992-09-07|4-NOT SPECIFIED|Clerk#000000854|0|ing accounts can haggle at the carefully final Tiresias-- pending, regular| +2016|8|O|24347.36|1996-08-16|3-MEDIUM|Clerk#000000641|0|the carefully ironic foxes. requests nag bold, r| +2017|101|O|70529.27|1998-05-13|3-MEDIUM|Clerk#000000427|0|nusual requests. blit| +2018|19|P|25007.95|1995-04-05|4-NOT SPECIFIED|Clerk#000000920|0|gular accounts wake fur| +2019|136|F|43789.14|1992-10-23|1-URGENT|Clerk#000000565|0| furiously bold packages. fluffily fi| +2020|73|F|136162.13|1993-06-21|3-MEDIUM|Clerk#000000192|0|es. furiously regular packages above the furiously special theodolites are a| +2021|70|O|27016.74|1995-07-15|1-URGENT|Clerk#000000155|0|ong the furiously regular requests. unusual deposits wake fluffily inside| +2022|62|F|206742.11|1992-03-15|1-URGENT|Clerk#000000268|0| dependencies sleep fluffily even, ironic deposits. express, silen| +2023|118|F|144123.37|1992-05-06|5-LOW|Clerk#000000137|0|ular courts engage according to the| +2048|17|F|33401.77|1993-11-15|1-URGENT|Clerk#000000934|0|s cajole after the blithely final accounts. f| +2049|31|O|153048.74|1995-12-07|2-HIGH|Clerk#000000859|0|ly regular requests thrash blithely about the fluffily even theodolites. r| +2050|28|F|208517.98|1994-06-02|4-NOT SPECIFIED|Clerk#000000821|0|d accounts against the furiously regular packages use bli| +2051|40|O|87988.34|1996-03-18|4-NOT SPECIFIED|Clerk#000000333|0|ctions sleep blithely. blithely regu| +2052|91|F|141822.19|1992-04-13|2-HIGH|Clerk#000000767|0| requests sleep around the even, even courts. ironic theodolites affix furious| +2053|142|F|125125.57|1995-02-07|1-URGENT|Clerk#000000717|0|ar requests: blithely sly accounts boost carefully across t| +2054|41|F|144335.16|1992-06-08|4-NOT SPECIFIED|Clerk#000000103|0|l requests affix carefully about the furiously special| +2055|97|F|57092.26|1993-09-04|1-URGENT|Clerk#000000067|0|. warhorses affix slyly blithely express instructions? fur| +2080|95|F|45767.69|1993-06-18|5-LOW|Clerk#000000190|0|ironic, pending theodolites are carefully about the quickly regular theodolite| +2081|121|O|145654.97|1997-07-05|2-HIGH|Clerk#000000136|0|ong the regular theo| +2082|49|F|46753.63|1995-01-10|2-HIGH|Clerk#000000354|0|cial accounts. ironic, express dolphins nod slyly sometimes final reques| +2083|101|F|31795.52|1993-07-14|3-MEDIUM|Clerk#000000361|0|al patterns. bold, final foxes nag bravely about the furiously express| +2084|80|F|190652.53|1993-03-17|2-HIGH|Clerk#000000048|0|zle furiously final, careful packages. slyly ironic ideas amo| +2085|49|F|45311.07|1993-11-21|3-MEDIUM|Clerk#000000818|0|ress, express ideas haggle| +2086|142|F|188985.18|1994-10-19|1-URGENT|Clerk#000000046|0| permanently regular| +2087|50|O|53581.41|1998-01-31|2-HIGH|Clerk#000000626|0|e always regular packages nod against the furiously spec| +2112|64|O|17986.15|1997-02-05|2-HIGH|Clerk#000000351|0|against the slyly even id| +2113|32|O|65678.21|1997-11-08|2-HIGH|Clerk#000000527|0|slyly regular instruct| +2114|79|F|106446.02|1995-01-16|5-LOW|Clerk#000000751|0|r, unusual accounts haggle across the busy platelets. carefully | +2115|106|O|134814.65|1998-05-23|4-NOT SPECIFIED|Clerk#000000101|0|odolites boost. carefully regular excuses cajole. quickly ironic pinto be| +2116|23|F|60887.90|1994-08-26|1-URGENT|Clerk#000000197|0|efully after the asymptotes. furiously sp| +2117|22|O|145713.03|1997-04-26|2-HIGH|Clerk#000000887|0|ely even dependencies. regular foxes use blithely.| +2118|134|O|38974.67|1996-10-09|1-URGENT|Clerk#000000196|0|ial requests wake carefully special packages. f| +2119|64|O|34632.57|1996-08-20|2-HIGH|Clerk#000000434|0|uickly pending escapades. fluffily ir| +2144|136|F|119917.28|1994-03-29|3-MEDIUM|Clerk#000000546|0|t. carefully quick requests across the deposits wake regu| +2145|134|F|18885.35|1992-10-03|1-URGENT|Clerk#000000886|0|sts would snooze blithely alongside of th| +2146|118|F|179686.07|1992-09-14|4-NOT SPECIFIED|Clerk#000000476|0|ven packages. dependencies wake slyl| +2147|100|F|91513.79|1992-09-06|4-NOT SPECIFIED|Clerk#000000424|0| haggle carefully furiously final foxes. pending escapades thrash. bold theod| +2148|130|F|19612.03|1995-04-19|4-NOT SPECIFIED|Clerk#000000517|0|ross the furiously unusual theodolites. always expre| +2149|101|F|105145.40|1993-03-13|5-LOW|Clerk#000000555|0|nusual accounts nag furiously special reques| +2150|82|F|166961.06|1994-06-03|3-MEDIUM|Clerk#000000154|0|ect slyly against the even, final packages. quickly regular pinto beans wake c| +2151|58|O|124608.69|1996-11-11|3-MEDIUM|Clerk#000000996|0|c requests. ironic platelets cajole across the quickly fluffy deposits.| +2176|104|F|87248.17|1992-11-10|1-URGENT|Clerk#000000195|0|s haggle regularly accor| +2177|136|O|183493.42|1997-01-20|3-MEDIUM|Clerk#000000161|0|ove the blithely unusual packages cajole carefully fluffily special request| +2178|8|O|79594.68|1996-12-12|3-MEDIUM|Clerk#000000656|0|thely according to the instructions. furious| +2179|41|O|77487.09|1996-09-07|2-HIGH|Clerk#000000935|0|ounts alongside of the furiously unusual braids cajol| +2180|76|O|208481.57|1996-09-14|4-NOT SPECIFIED|Clerk#000000650|0|xpress, unusual pains. furiously ironic excu| +2181|76|O|100954.64|1995-09-13|3-MEDIUM|Clerk#000000814|0|y against the ironic, even| +2182|23|F|116003.11|1994-04-05|2-HIGH|Clerk#000000071|0|ccounts. quickly bold deposits across the excuses sl| +2183|113|O|49841.12|1996-06-22|1-URGENT|Clerk#000000287|0| among the express, ironic packages. slyly ironic platelets integrat| +2208|68|P|245388.06|1995-05-01|4-NOT SPECIFIED|Clerk#000000900|0|symptotes wake slyly blithely unusual packages.| +2209|91|F|129086.93|1992-07-10|2-HIGH|Clerk#000000056|0|er above the slyly silent requests. furiously reg| +2210|32|F|31689.46|1992-01-16|2-HIGH|Clerk#000000941|0| believe carefully quickly express pinto beans. deposi| +2211|92|F|140031.23|1994-06-30|2-HIGH|Clerk#000000464|0|ffily bold courts e| +2212|118|F|17231.05|1994-03-23|3-MEDIUM|Clerk#000000954|0|structions above the unusual requests use fur| +2213|122|F|146136.10|1993-01-15|4-NOT SPECIFIED|Clerk#000000598|0|osits are carefully reg| +2214|115|O|150345.63|1998-05-05|3-MEDIUM|Clerk#000000253|0|packages. fluffily even accounts haggle blithely. carefully ironic depen| +2215|40|O|108239.46|1996-06-16|4-NOT SPECIFIED|Clerk#000000817|0|le final, final foxes. quickly regular gifts are carefully deposit| +2240|56|F|174090.30|1992-03-06|4-NOT SPECIFIED|Clerk#000000622|0|accounts against the slyly express foxes are after the slyly regular | +2241|103|F|165219.08|1993-05-11|1-URGENT|Clerk#000000081|0|y about the silent excuses. furiously ironic instructions along the sil| +2242|82|O|15082.82|1997-07-20|4-NOT SPECIFIED|Clerk#000000360|0| pending multipliers. carefully express asymptotes use quickl| +2243|49|O|10451.97|1995-06-10|2-HIGH|Clerk#000000813|0|ously regular deposits integrate s| +2244|127|F|21207.08|1993-01-09|1-URGENT|Clerk#000001000|0|ckages. ironic, ironic accounts haggle blithely express excuses. | +2245|58|F|150585.73|1993-04-28|3-MEDIUM|Clerk#000000528|0|ake carefully. braids haggle slyly quickly b| +2246|113|O|85755.84|1996-05-27|4-NOT SPECIFIED|Clerk#000000739|0| final gifts sleep | +2247|95|F|13491.31|1992-08-02|4-NOT SPECIFIED|Clerk#000000947|0|furiously regular packages. final brai| +2272|139|F|127934.71|1993-04-13|2-HIGH|Clerk#000000449|0|s. bold, ironic pinto beans wake. silently specia| +2273|136|O|142291.79|1996-12-14|5-LOW|Clerk#000000155|0|uickly express foxes haggle quickly against| +2274|104|F|58273.89|1993-09-04|4-NOT SPECIFIED|Clerk#000000258|0|nstructions try to hag| +2275|149|F|37398.90|1992-10-22|4-NOT SPECIFIED|Clerk#000000206|0| furiously furious platelets. slyly final packa| +2276|43|O|141159.63|1996-04-29|4-NOT SPECIFIED|Clerk#000000821|0|ecial requests. fox| +2277|89|F|79270.23|1995-01-02|4-NOT SPECIFIED|Clerk#000000385|0|accounts cajole. even i| +2278|142|O|101878.46|1998-04-25|3-MEDIUM|Clerk#000000186|0|r pinto beans integrate after the carefully even deposits. blit| +2279|80|F|142322.33|1993-02-23|3-MEDIUM|Clerk#000000898|0|de of the quickly unusual instructio| +2304|46|F|93769.28|1994-01-07|4-NOT SPECIFIED|Clerk#000000415|0|onic platelets. ironic packages haggle. packages nag doggedly according to| +2305|43|F|122964.66|1993-01-26|2-HIGH|Clerk#000000440|0|ove the furiously even acco| +2306|28|O|244704.23|1995-07-26|2-HIGH|Clerk#000000975|0| wake furiously requests. permanent requests affix. final packages caj| +2307|106|F|59417.76|1993-06-29|5-LOW|Clerk#000000952|0|furiously even asymptotes? carefully regular accounts| +2308|25|F|58546.02|1992-10-25|4-NOT SPECIFIED|Clerk#000000609|0|ts. slyly final depo| +2309|100|O|146933.07|1995-09-04|5-LOW|Clerk#000000803|0|he carefully pending packages. fluffily stealthy foxes engage carefully| +2310|31|O|82928.12|1996-09-20|5-LOW|Clerk#000000917|0|wake carefully. unusual instructions nag ironic, regular excuse| +2311|73|P|153233.93|1995-05-02|2-HIGH|Clerk#000000761|0|ly pending asymptotes-- furiously bold excus| +2336|142|O|22294.51|1996-01-07|4-NOT SPECIFIED|Clerk#000000902|0|c, final excuses sleep furiously among the even theodolites. f| +2337|142|O|45704.96|1997-06-18|4-NOT SPECIFIED|Clerk#000000754|0| quickly. final accounts haggle. carefully final acco| +2338|140|O|28155.92|1997-09-15|2-HIGH|Clerk#000000951|0|riously final dugouts. final, ironic packages wake express, ironic id| +2339|109|F|63470.78|1993-12-15|5-LOW|Clerk#000000847|0| against the regular | +2340|65|O|30778.78|1996-01-12|1-URGENT|Clerk#000000964|0|ter the deposits sleep according to the slyly regular packages. carefully | +2341|82|F|55950.21|1993-05-30|5-LOW|Clerk#000000443|0|sts-- blithely bold dolphins through the deposits nag blithely carefully re| +2342|37|O|104038.78|1996-06-09|1-URGENT|Clerk#000000615|0|oost carefully across the regular accounts. blithely final d| +2343|73|O|85381.00|1995-08-21|3-MEDIUM|Clerk#000000170|0|fluffily over the slyly special deposits. quickl| +2368|13|F|101240.96|1993-08-20|1-URGENT|Clerk#000000830|0|t the bold instructions. carefully unusual | +2369|110|O|73517.91|1996-12-24|2-HIGH|Clerk#000000752|0|iously even requests are dogged, express | +2370|142|F|73924.21|1994-01-17|1-URGENT|Clerk#000000231|0|lyly final packages. quickly final deposits haggl| +2371|19|O|193857.67|1998-01-07|1-URGENT|Clerk#000000028|0|ckages haggle at th| +2372|31|O|104927.66|1997-11-21|5-LOW|Clerk#000000342|0|s: deposits haggle along the final ideas. careful| +2373|28|F|55211.04|1994-03-12|4-NOT SPECIFIED|Clerk#000000306|0| even, special courts grow quickly. pending,| +2374|4|F|115219.88|1993-10-29|4-NOT SPECIFIED|Clerk#000000081|0| blithely regular packages. blithely unusua| +2375|5|O|106612.48|1996-11-20|3-MEDIUM|Clerk#000000197|0|unusual, pending theodolites cajole carefully | +2400|37|O|92798.66|1998-07-25|5-LOW|Clerk#000000782|0|nusual courts nag against the carefully unusual pinto b| +2401|148|O|88448.24|1997-07-29|4-NOT SPECIFIED|Clerk#000000531|0|ully unusual instructions boost carefully silently regular requests. | +2402|67|O|70403.62|1996-09-06|4-NOT SPECIFIED|Clerk#000000162|0|slyly final sheaves sleep slyly. q| +2403|55|O|111020.79|1998-04-11|3-MEDIUM|Clerk#000000820|0|furiously regular deposits use. furiously unusual accounts wake along the | +2404|77|O|109077.69|1997-03-13|4-NOT SPECIFIED|Clerk#000000409|0|deposits breach furiously. ironic foxes haggle carefully bold packag| +2405|73|O|115929.14|1996-12-23|3-MEDIUM|Clerk#000000535|0|ular, regular asympto| +2406|7|O|182516.77|1996-10-28|5-LOW|Clerk#000000561|0|blithely regular accounts u| +2407|55|O|112843.52|1998-06-19|2-HIGH|Clerk#000000068|0|uests affix slyly among the slyly regular depos| +2432|103|O|62661.93|1996-07-13|1-URGENT|Clerk#000000115|0|re. slyly even deposits wake bra| +2433|31|F|147071.86|1994-08-22|4-NOT SPECIFIED|Clerk#000000324|0|ess patterns are slyly. packages haggle carefu| +2434|25|O|123956.25|1997-04-27|3-MEDIUM|Clerk#000000190|0|s. quickly ironic dolphins impress final deposits. blithel| +2435|73|F|122490.66|1993-02-21|5-LOW|Clerk#000000112|0|es are carefully along the carefully final instructions. pe| +2436|125|O|73990.08|1995-09-11|4-NOT SPECIFIED|Clerk#000000549|0|arefully. blithely bold deposits affix special accounts. final foxes nag. spe| +2437|85|F|143411.69|1993-04-21|4-NOT SPECIFIED|Clerk#000000578|0|. theodolites wake slyly-- ironic, pending platelets above the carefully exp| +2438|13|F|214494.39|1993-07-15|2-HIGH|Clerk#000000744|0|the final, regular warhorses. regularly | +2439|55|O|41811.12|1997-03-15|2-HIGH|Clerk#000000819|0|lithely after the car| +2464|145|O|30495.65|1997-11-23|5-LOW|Clerk#000000633|0|le about the instructions. courts wake carefully even| +2465|34|O|180737.75|1995-06-24|1-URGENT|Clerk#000000078|0|al pinto beans. final, bold packages wake quickly| +2466|19|F|161625.50|1994-03-06|1-URGENT|Clerk#000000424|0|c pinto beans. express deposits wake quickly. even, final courts nag. package| +2467|35|O|7231.91|1995-07-16|4-NOT SPECIFIED|Clerk#000000914|0|pades sleep furiously. sometimes regular packages again| +2468|112|O|160627.01|1997-06-09|4-NOT SPECIFIED|Clerk#000000260|0|ickly regular packages. slyly ruthless requests snooze quickly blithe| +2469|124|O|192074.23|1996-11-26|5-LOW|Clerk#000000730|0| sleep closely regular instructions. furiously ironic instructi| +2470|58|O|104966.33|1997-04-19|3-MEDIUM|Clerk#000000452|0|to the furiously final packages? pa| +2471|89|O|34936.31|1998-03-12|4-NOT SPECIFIED|Clerk#000000860|0|carefully blithely regular pac| +2496|136|F|140390.60|1994-01-09|2-HIGH|Clerk#000000142|0|slyly. pending instructions sleep. quic| +2497|47|F|171326.48|1992-08-27|1-URGENT|Clerk#000000977|0|ily ironic pinto beans. furiously final platelets alongside of t| +2498|97|F|45514.27|1993-11-08|5-LOW|Clerk#000000373|0|g the slyly special pinto beans. | +2499|121|O|147243.86|1995-09-24|1-URGENT|Clerk#000000277|0|r the quickly bold foxes. bold instructi| +2500|133|F|131122.82|1992-08-15|2-HIGH|Clerk#000000447|0|integrate slyly pending deposits. furiously ironic accounts across the s| +2501|67|O|79380.51|1997-05-25|5-LOW|Clerk#000000144|0|ickly special theodolite| +2502|70|F|33470.40|1993-05-28|4-NOT SPECIFIED|Clerk#000000914|0|lyly: carefully pending ideas affix again| +2503|7|F|183671.08|1993-06-20|3-MEDIUM|Clerk#000000294|0|ly even packages was. ironic, regular deposits unwind furiously across the p| +2528|55|F|92069.62|1994-11-20|1-URGENT|Clerk#000000789|0|ular dependencies? regular frays kindle according to the blith| +2529|136|O|4104.30|1996-08-20|2-HIGH|Clerk#000000511|0|posits across the silent instructions wake blithely across | +2530|128|F|58853.11|1994-03-21|3-MEDIUM|Clerk#000000291|0|ular instructions about the quic| +2531|44|O|143212.85|1996-05-06|4-NOT SPECIFIED|Clerk#000000095|0|even accounts. furiously ironic excuses sleep fluffily. carefully silen| +2532|94|O|116093.49|1995-10-11|2-HIGH|Clerk#000000498|0|the blithely pending accounts. regular, regular excuses boost aro| +2533|50|O|168495.03|1997-03-24|1-URGENT|Clerk#000000594|0|ecial instructions. spec| +2534|76|O|202784.54|1996-07-17|3-MEDIUM|Clerk#000000332|0|packages cajole ironic requests. furiously regular| +2535|121|F|67018.30|1993-05-25|5-LOW|Clerk#000000296|0|phins cajole beneath the fluffily express asymptotes. c| +2560|131|F|153426.79|1992-09-05|1-URGENT|Clerk#000000538|0|atelets; quickly sly requests| +2561|58|O|137473.58|1997-11-14|1-URGENT|Clerk#000000861|0|ual requests. unusual deposits cajole furiously pending, regular platelets. | +2562|10|F|136360.37|1992-08-01|1-URGENT|Clerk#000000467|0|elets. pending dolphins promise slyly. bo| +2563|62|F|168952.10|1993-11-19|4-NOT SPECIFIED|Clerk#000000150|0|sly even packages after the furio| +2564|77|F|3967.47|1994-09-09|2-HIGH|Clerk#000000718|0|usly regular pinto beans. orbits wake carefully. slyly e| +2565|56|O|204438.57|1998-02-28|3-MEDIUM|Clerk#000000032|0|x-ray blithely along| +2566|86|F|89992.48|1992-10-10|3-MEDIUM|Clerk#000000414|0|ructions boost bold ideas. idly ironic accounts use according to th| +2567|70|O|263411.29|1998-02-27|2-HIGH|Clerk#000000031|0|detect. furiously ironic requests| +2592|101|F|8225.96|1993-03-05|4-NOT SPECIFIED|Clerk#000000524|0|ts nag fluffily. quickly stealthy theodolite| +2593|92|F|134726.09|1993-09-04|2-HIGH|Clerk#000000468|0|r the carefully final| +2594|79|F|94866.39|1992-12-17|1-URGENT|Clerk#000000550|0|ests. theodolites above the blithely even accounts detect furio| +2595|74|O|173130.20|1995-12-14|4-NOT SPECIFIED|Clerk#000000222|0|arefully ironic requests nag carefully ideas. | +2596|43|O|74940.13|1996-08-17|1-URGENT|Clerk#000000242|0|requests. ironic, bold theodolites wak| +2597|104|F|21964.66|1993-02-04|2-HIGH|Clerk#000000757|0|iously ruthless exc| +2598|112|O|84871.50|1996-03-05|3-MEDIUM|Clerk#000000391|0| ironic notornis according to the blithely final requests should | +2599|149|O|62807.13|1996-11-07|2-HIGH|Clerk#000000722|0|ts. slyly regular theodolites wake sil| +2624|52|O|27148.63|1996-11-28|5-LOW|Clerk#000000930|0|ic, regular packages| +2625|40|F|39382.74|1992-10-14|4-NOT SPECIFIED|Clerk#000000386|0| final deposits. blithely ironic ideas | +2626|139|O|84314.51|1995-09-08|4-NOT SPECIFIED|Clerk#000000289|0|gside of the carefully special packages are furiously after the slyly express | +2627|149|F|26798.65|1992-03-24|3-MEDIUM|Clerk#000000181|0|s. silent, ruthless requests| +2628|56|F|165655.99|1993-10-22|5-LOW|Clerk#000000836|0|ajole across the blithely careful accounts. blithely silent deposits sl| +2629|139|O|96458.03|1998-04-06|5-LOW|Clerk#000000680|0|uches dazzle carefully even, express excuses. ac| +2630|85|F|127132.51|1992-10-24|5-LOW|Clerk#000000712|0|inal theodolites. ironic instructions s| +2631|37|F|63103.32|1993-09-24|5-LOW|Clerk#000000833|0| quickly unusual deposits doubt around | +2656|77|F|105492.37|1993-05-04|1-URGENT|Clerk#000000307|0|elets. slyly final accou| +2657|25|O|148176.06|1995-10-17|2-HIGH|Clerk#000000160|0| foxes-- slyly final dependencies around the slyly final theodo| +2658|14|O|163834.46|1995-09-23|3-MEDIUM|Clerk#000000400|0|bout the slyly regular accounts. ironic, | +2659|83|F|79785.52|1993-12-18|4-NOT SPECIFIED|Clerk#000000758|0|cross the pending requests maintain | +2660|127|O|16922.51|1995-08-05|5-LOW|Clerk#000000480|0|ly finally regular deposits. ironic theodolites cajole| +2661|74|O|106036.84|1997-01-04|3-MEDIUM|Clerk#000000217|0|al, regular pinto beans. silently final deposits should have t| +2662|37|O|87689.88|1996-08-21|3-MEDIUM|Clerk#000000589|0|bold pinto beans above the slyly final accounts affix furiously deposits. pac| +2663|95|O|35131.80|1995-09-06|1-URGENT|Clerk#000000950|0|ar requests. furiously final dolphins along the fluffily spe| +2688|98|F|181077.36|1992-01-24|2-HIGH|Clerk#000000720|0|have to nag according to the pending theodolites. sly| +2689|103|F|41552.78|1992-04-09|4-NOT SPECIFIED|Clerk#000000698|0|press pains wake. furiously express theodolites alongsid| +2690|94|O|224674.27|1996-03-31|3-MEDIUM|Clerk#000000760|0|ravely even theodolites | +2691|7|F|30137.17|1992-04-30|5-LOW|Clerk#000000439|0|es at the regular deposits sleep slyly by the fluffy requests. eve| +2692|62|O|24265.24|1997-12-02|3-MEDIUM|Clerk#000000878|0|es. regular asymptotes cajole above t| +2693|19|O|66158.13|1996-09-04|1-URGENT|Clerk#000000370|0|ndle never. blithely regular packages nag carefully enticing platelets. ca| +2694|121|O|102807.59|1996-03-14|5-LOW|Clerk#000000722|0| requests. bold deposits above the theodol| +2695|58|O|138584.20|1996-08-20|1-URGENT|Clerk#000000697|0|ven deposits around the quickly regular packa| +2720|31|F|161307.05|1993-06-08|1-URGENT|Clerk#000000948|0|quickly. special asymptotes are fluffily ironi| +2721|79|O|59180.25|1996-01-27|2-HIGH|Clerk#000000401|0| ideas eat even, unusual ideas. theodolites are carefully| +2722|35|F|50328.84|1994-04-09|5-LOW|Clerk#000000638|0|rding to the carefully quick deposits. bli| +2723|61|O|104759.25|1995-10-06|5-LOW|Clerk#000000836|0|nts must have to cajo| +2724|137|F|116069.66|1994-09-14|2-HIGH|Clerk#000000217|0| sleep blithely. blithely idle | +2725|89|F|75144.68|1994-05-21|4-NOT SPECIFIED|Clerk#000000835|0|ular deposits. spec| +2726|7|F|47753.00|1992-11-27|5-LOW|Clerk#000000470|0| blithely even dinos sleep care| +2727|74|O|3089.42|1998-04-19|4-NOT SPECIFIED|Clerk#000000879|0|sual theodolites cajole enticingly above the furiously fin| +2752|59|F|187932.30|1993-11-19|2-HIGH|Clerk#000000648|0| carefully regular foxes are quickly quickl| +2753|16|F|159720.39|1993-11-30|2-HIGH|Clerk#000000380|0|ending instructions. unusual deposits| +2754|145|F|25985.52|1994-04-03|2-HIGH|Clerk#000000960|0|cies detect slyly. | +2755|118|F|101202.18|1992-02-07|4-NOT SPECIFIED|Clerk#000000177|0|ously according to the sly foxes. blithely regular pinto bean| +2756|118|F|142323.38|1994-04-18|1-URGENT|Clerk#000000537|0|arefully special warho| +2757|76|O|89792.48|1995-07-20|2-HIGH|Clerk#000000216|0| regular requests subl| +2758|43|O|36671.88|1998-07-12|5-LOW|Clerk#000000863|0|s cajole according to the carefully special | +2759|116|F|89731.10|1993-11-25|4-NOT SPECIFIED|Clerk#000000071|0|ts. regular, pending pinto beans sleep ab| +2784|95|O|106635.21|1998-01-07|1-URGENT|Clerk#000000540|0|g deposits alongside of the silent requests s| +2785|148|O|132854.79|1995-07-21|2-HIGH|Clerk#000000098|0|iously pending packages sleep according to the blithely unusual foxe| +2786|79|F|178254.66|1992-03-22|2-HIGH|Clerk#000000976|0|al platelets cajole blithely ironic requests. ironic re| +2787|103|O|3726.14|1995-09-30|1-URGENT|Clerk#000000906|0|he ironic, regular | +2788|124|F|17172.66|1994-09-22|1-URGENT|Clerk#000000641|0|nts wake across the fluffily bold accoun| +2789|37|O|219123.27|1998-03-14|2-HIGH|Clerk#000000972|0|gular patterns boost. carefully even re| +2790|25|F|177458.97|1994-08-19|2-HIGH|Clerk#000000679|0| the carefully express deposits sleep slyly | +2791|121|F|156697.55|1994-10-10|2-HIGH|Clerk#000000662|0|as. slyly ironic accounts play furiously bl| +2816|58|F|42225.53|1994-09-20|2-HIGH|Clerk#000000289|0|kages at the final deposits cajole furious foxes. quickly | +2817|40|F|71453.85|1994-04-19|3-MEDIUM|Clerk#000000982|0|ic foxes haggle upon the daringly even pinto beans. slyly| +2818|49|F|120086.84|1994-12-12|3-MEDIUM|Clerk#000000413|0|eep furiously special ideas. express | +2819|103|F|66927.16|1994-05-05|1-URGENT|Clerk#000000769|0|ngside of the blithely ironic dolphins. furio| +2820|19|F|143813.39|1994-05-20|3-MEDIUM|Clerk#000000807|0|equests are furiously. carefu| +2821|118|F|36592.48|1993-08-09|3-MEDIUM|Clerk#000000323|0|ng requests. even instructions are quickly express, silent instructi| +2822|79|F|40142.15|1993-07-26|2-HIGH|Clerk#000000510|0|furiously against the accounts. unusual accounts aft| +2823|79|O|171894.45|1995-09-09|2-HIGH|Clerk#000000567|0|encies. carefully fluffy accounts m| +2848|70|F|116258.53|1992-03-10|1-URGENT|Clerk#000000256|0|ly fluffy foxes sleep furiously across the slyly regu| +2849|46|O|180054.29|1996-04-30|2-HIGH|Clerk#000000659|0|al packages are after the quickly bold requests. carefully special | +2850|100|O|122969.79|1996-10-02|2-HIGH|Clerk#000000392|0|, regular deposits. furiously pending packages hinder carefully carefully u| +2851|145|O|7859.36|1997-09-07|5-LOW|Clerk#000000566|0|Tiresias wake quickly quickly even| +2852|91|F|99050.81|1993-01-16|1-URGENT|Clerk#000000740|0|ruthless deposits against the final instructions use quickly al| +2853|94|F|103641.15|1994-05-05|2-HIGH|Clerk#000000878|0|the carefully even packages.| +2854|139|F|153568.02|1994-06-27|1-URGENT|Clerk#000000010|0| furiously ironic tithes use furiously | +2855|49|F|48419.58|1993-04-04|4-NOT SPECIFIED|Clerk#000000973|0| silent, regular packages sleep | +2880|8|F|145761.99|1992-03-15|2-HIGH|Clerk#000000756|0|ves maintain doggedly spec| +2881|100|F|45695.84|1992-05-10|5-LOW|Clerk#000000864|0|uriously. slyly express requests according to the silent dol| +2882|121|O|172872.37|1995-08-22|2-HIGH|Clerk#000000891|0|pending deposits. carefully eve| +2883|121|F|170360.27|1995-01-23|5-LOW|Clerk#000000180|0|uses. carefully ironic accounts lose fluffil| +2884|92|O|71683.84|1997-10-12|3-MEDIUM|Clerk#000000780|0|efully express instructions sleep against| +2885|7|F|146896.72|1992-09-19|4-NOT SPECIFIED|Clerk#000000280|0|ly sometimes special excuses. final requests are | +2886|109|F|94527.23|1994-11-13|4-NOT SPECIFIED|Clerk#000000619|0|uctions. ironic packages sle| +2887|109|O|28571.39|1997-05-26|5-LOW|Clerk#000000566|0|slyly even pinto beans. slyly bold epitaphs cajole blithely above t| +2912|94|F|27727.52|1992-03-12|5-LOW|Clerk#000000186|0|jole blithely above the quickly regular packages. carefully regular pinto bean| +2913|43|O|130702.19|1997-07-12|3-MEDIUM|Clerk#000000118|0|mptotes doubt furiously slyly regu| +2914|109|F|60867.14|1993-03-03|3-MEDIUM|Clerk#000000543|0|he slyly regular theodolites are furiously sile| +2915|94|F|96015.13|1994-03-31|5-LOW|Clerk#000000410|0|ld packages. bold deposits boost blithely. ironic, unusual theodoli| +2916|8|O|20182.22|1995-12-27|2-HIGH|Clerk#000000681|0|ithely blithe deposits sleep beyond the| +2917|91|O|100714.13|1997-12-09|4-NOT SPECIFIED|Clerk#000000061|0| special dugouts among the special deposi| +2918|118|O|21760.09|1996-09-08|3-MEDIUM|Clerk#000000439|0|ular deposits across th| +2919|53|F|137223.14|1993-12-10|2-HIGH|Clerk#000000209|0|es. pearls wake quietly slyly ironic instructions--| +2944|14|O|146581.14|1997-09-24|4-NOT SPECIFIED|Clerk#000000740|0|deas. permanently special foxes haggle carefully ab| +2945|29|O|223507.72|1996-01-03|2-HIGH|Clerk#000000499|0|ons are carefully toward the permanent, bold pinto beans. regu| +2946|125|O|102226.59|1996-02-05|5-LOW|Clerk#000000329|0|g instructions about the regular accounts sleep carefully along the pen| +2947|70|P|43360.95|1995-04-26|1-URGENT|Clerk#000000464|0|ronic accounts. accounts run furiously d| +2948|44|F|100758.71|1994-08-23|5-LOW|Clerk#000000701|0| deposits according to the blithely pending | +2949|137|F|94231.71|1994-04-12|2-HIGH|Clerk#000000184|0|y ironic accounts use. quickly blithe accou| +2950|136|O|183620.33|1997-07-06|1-URGENT|Clerk#000000833|0| dolphins around the furiously | +2951|74|O|125509.17|1996-02-06|2-HIGH|Clerk#000000680|0|gular deposits above the finally regular ideas integrate idly stealthil| +2976|29|F|145768.47|1993-12-10|4-NOT SPECIFIED|Clerk#000000159|0|. furiously ironic asymptotes haggle ruthlessly silently regular r| +2977|73|O|25170.88|1996-08-27|3-MEDIUM|Clerk#000000252|0|quickly special platelets are furio| +2978|44|P|139542.14|1995-05-03|1-URGENT|Clerk#000000135|0|d. even platelets are. ironic dependencies cajole slow, e| +2979|133|O|116789.98|1996-03-23|3-MEDIUM|Clerk#000000820|0|even, ironic foxes sleep along| +2980|4|O|187514.11|1996-09-14|3-MEDIUM|Clerk#000000661|0|y quick pinto beans wake. slyly re| +2981|49|O|37776.79|1998-07-29|5-LOW|Clerk#000000299|0|hely among the express foxes. blithely stealthy requests cajole boldly. regu| +2982|85|F|55582.94|1995-03-19|2-HIGH|Clerk#000000402|0|lyly. express theodolites affix slyly after the slyly speci| +2983|62|F|58168.07|1992-01-07|1-URGENT|Clerk#000000278|0|r the even requests. accounts maintain. regular accounts| +3008|40|O|156018.74|1995-11-08|3-MEDIUM|Clerk#000000701|0|ze quickly. blithely regular packages above the slyly bold foxes shall| +3009|55|O|108424.94|1997-02-28|1-URGENT|Clerk#000000205|0|r ideas. carefully pe| +3010|8|O|141647.08|1996-01-26|2-HIGH|Clerk#000000931|0| blithely final requests. special deposits are slyl| +3011|91|F|46418.85|1992-01-14|5-LOW|Clerk#000000515|0|onic deposits kindle slyly. dependencies around the quickly iro| +3012|32|F|91678.66|1993-05-05|1-URGENT|Clerk#000000414|0|ts after the regular pinto beans impress blithely s| +3013|143|O|156407.40|1997-02-05|5-LOW|Clerk#000000591|0|the furiously pendin| +3014|29|F|194159.59|1992-10-30|4-NOT SPECIFIED|Clerk#000000476|0|ep blithely according to the blith| +3015|103|F|110826.83|1992-09-27|5-LOW|Clerk#000000013|0|ously regular deposits affix carefully. furiousl| +3040|112|F|119201.64|1993-04-12|3-MEDIUM|Clerk#000000544|0|carefully special packages. blithe| +3041|113|O|23039.46|1997-06-03|5-LOW|Clerk#000000092|0|s. unusual, pending deposits use carefully. thinly final| +3042|20|F|104523.03|1994-11-21|3-MEDIUM|Clerk#000000573|0| the slyly ironic depo| +3043|44|F|78221.69|1992-04-25|5-LOW|Clerk#000000137|0|cajole blithely furiously fina| +3044|53|O|52433.54|1996-04-03|2-HIGH|Clerk#000000008|0|cajole final courts. ironic deposits about the quickly final re| +3045|50|O|85822.67|1995-09-27|1-URGENT|Clerk#000000405|0| express courts sleep quickly special asymptotes. | +3046|32|O|117817.52|1995-11-30|2-HIGH|Clerk#000000522|0|r deposits. platelets use furi| +3047|25|O|37881.31|1997-03-21|1-URGENT|Clerk#000000962|0|as. slyly express deposits are dogged pearls. silent ide| +3072|23|F|87475.82|1994-01-30|4-NOT SPECIFIED|Clerk#000000370|0|ely final deposits cajole carefully. ironic, re| +3073|136|F|151419.50|1994-01-08|3-MEDIUM|Clerk#000000404|0|kly slyly bold accounts. express courts near the regular ideas sleep bli| +3074|67|F|85861.93|1992-11-01|5-LOW|Clerk#000000546|0|yly even asymptotes shall have to haggle fluffily. deposits are| +3075|127|F|37696.70|1994-05-07|3-MEDIUM|Clerk#000000433|0|ackages: carefully unusual reques| +3076|92|F|93828.15|1993-07-23|2-HIGH|Clerk#000000099|0|busy foxes. deposits affix quickly ironic, pending pint| +3077|121|O|99290.01|1997-08-06|2-HIGH|Clerk#000000228|0|kly. fluffily ironic requests use qui| +3078|49|F|46310.83|1993-02-12|2-HIGH|Clerk#000000110|0|ounts are alongside of the blith| +3079|100|O|148299.05|1997-09-12|5-LOW|Clerk#000000505|0|lly ironic accounts| +3104|70|F|102693.61|1993-09-16|3-MEDIUM|Clerk#000000871|0|ges boost-- regular accounts are furiousl| +3105|137|O|125396.80|1996-11-13|4-NOT SPECIFIED|Clerk#000000772|0|s. blithely final ins| +3106|145|O|132494.97|1997-01-12|3-MEDIUM|Clerk#000000729|0|its use slyly final theodolites; regular dolphins hang above t| +3107|26|O|107406.26|1997-08-21|1-URGENT|Clerk#000000669|0|ously even deposits acr| +3108|85|F|63278.00|1993-08-05|1-URGENT|Clerk#000000574|0|s packages haggle furiously am| +3109|124|F|216104.85|1993-07-24|5-LOW|Clerk#000000936|0|bold requests sleep quickly according to the slyly final| +3110|88|F|115161.29|1994-12-17|2-HIGH|Clerk#000000564|0|round the fluffy instructions. carefully silent packages cajol| +3111|133|O|154383.37|1995-08-25|5-LOW|Clerk#000000922|0|slyly regular theodolites. furious deposits cajole deposits. ironic theodoli| +3136|23|F|145426.11|1994-08-10|4-NOT SPECIFIED|Clerk#000000891|0|tructions sleep slyly. pending di| +3137|136|O|8958.65|1995-07-26|3-MEDIUM|Clerk#000000063|0|ymptotes wake carefully above t| +3138|139|F|139579.18|1994-02-09|4-NOT SPECIFIED|Clerk#000000650|0|e fluffily final theodolites. even dependencies wake along the quickly ir| +3139|17|F|40975.96|1992-01-02|3-MEDIUM|Clerk#000000855|0|ounts against the ruthlessly unusual dolphins| +3140|145|F|54356.10|1992-04-09|1-URGENT|Clerk#000000670|0|carefully ironic deposits use furiously. blith| +3141|26|O|115959.96|1995-11-10|1-URGENT|Clerk#000000475|0|es. furiously bold instructions after the carefully final p| +3142|8|F|16030.15|1992-06-28|3-MEDIUM|Clerk#000000043|0|usual accounts about the carefully special requests sleep slyly quickly regul| +3143|107|F|135647.68|1993-02-17|1-URGENT|Clerk#000000519|0| are final, ironic accounts. ironic | +3168|136|F|69412.71|1992-01-30|5-LOW|Clerk#000000352|0|s sleep slyly? ironic, furious instructions detect. quickly final i| +3169|19|F|126804.90|1993-12-21|3-MEDIUM|Clerk#000000252|0| even pinto beans are blithely special, special multip| +3170|5|O|190142.17|1997-11-09|1-URGENT|Clerk#000000288|0|requests. furiously bold| +3171|47|F|84405.78|1993-04-06|5-LOW|Clerk#000000940|0|ar deposits. idly r| +3172|89|F|121360.83|1992-06-03|4-NOT SPECIFIED|Clerk#000000771|0|es. slyly ironic packages x-ra| +3173|148|O|64892.73|1996-08-10|5-LOW|Clerk#000000516|0|ial requests lose along t| +3174|127|O|92856.91|1995-11-15|5-LOW|Clerk#000000663|0|rts. silent, regular pinto beans are blithely regular packages. furiousl| +3175|44|F|205282.63|1994-07-15|5-LOW|Clerk#000000629|0| across the slyly even realms use carefully ironic deposits: sl| +3200|13|O|131103.31|1996-02-07|1-URGENT|Clerk#000000020|0| regular dependencies impress evenly even excuses. blithely | +3201|97|F|90755.31|1993-07-02|4-NOT SPECIFIED|Clerk#000000738|0|. busy, express instruction| +3202|88|F|50601.01|1992-12-24|5-LOW|Clerk#000000067|0|fluffily express requests affix carefully around th| +3203|127|O|49357.72|1997-11-05|2-HIGH|Clerk#000000493|0|e furiously silent warhorses. slyly silent deposits wake bli| +3204|10|F|41573.42|1992-12-26|1-URGENT|Clerk#000000693|0|ess somas cajole slyly. pending accounts cajole| +3205|148|F|153637.79|1992-04-11|5-LOW|Clerk#000000803|0|e furiously. quickly regular dinos about the final pinto be| +3206|122|O|64344.86|1996-08-09|1-URGENT|Clerk#000000755|0|ntegrate furiously final, express | +3207|22|O|133038.59|1998-02-16|1-URGENT|Clerk#000000695|0|uriously accounts. fluffily i| +3232|82|F|55619.01|1992-10-09|1-URGENT|Clerk#000000314|0|yly final accounts. packages agains| +3233|140|F|54121.92|1994-10-24|5-LOW|Clerk#000000470|0|ly ironic epitaphs use stealthy, express deposits. quickly regular instruct| +3234|14|O|147343.68|1996-04-05|4-NOT SPECIFIED|Clerk#000000367|0|ents according to the dependencies will sleep after the blithely even p| +3235|46|O|104695.09|1995-11-15|5-LOW|Clerk#000000349|0| quickly pinto beans. ironi| +3236|142|O|39470.39|1996-11-06|4-NOT SPECIFIED|Clerk#000000553|0|ithely slyly pending req| +3237|19|F|10508.12|1992-06-03|1-URGENT|Clerk#000000606|0|inal requests. slyly even foxes detect about the furiously exp| +3238|61|F|41375.69|1993-02-21|5-LOW|Clerk#000000818|0|lly express deposits are. furiously unusual ideas wake carefully somas. instr| +3239|35|O|156802.80|1998-01-12|4-NOT SPECIFIED|Clerk#000000619|0| cajole carefully along the furiously pending deposits. | +3264|94|O|162634.53|1996-11-02|5-LOW|Clerk#000000244|0|carefully. express, bold| +3265|53|F|43315.15|1992-06-27|1-URGENT|Clerk#000000265|0|re quickly quickly pe| +3266|4|P|68309.28|1995-03-17|5-LOW|Clerk#000000545|0|refully ironic instructions. slyly final pi| +3267|112|O|33998.90|1997-01-07|5-LOW|Clerk#000000484|0| the packages. regular decoys about the bold dependencies grow fi| +3268|142|F|36024.96|1994-06-25|5-LOW|Clerk#000000746|0|y brave requests unwind furiously accordin| +3269|17|O|218697.85|1996-03-01|3-MEDIUM|Clerk#000000378|0|ts. accounts wake carefully. carefully dogged accounts wake slyly slyly i| +3270|38|O|166669.86|1997-05-28|1-URGENT|Clerk#000000375|0|uffily pending courts ca| +3271|34|F|86534.05|1992-01-01|1-URGENT|Clerk#000000421|0|s. furiously regular requests| +3296|148|F|187553.35|1994-10-19|3-MEDIUM|Clerk#000000991|0|as! carefully final requests wake. furiously even| +3297|139|F|9679.45|1992-11-03|2-HIGH|Clerk#000000220|0| after the theodolites cajole carefully according to the finally| +3298|116|O|62716.67|1996-04-17|5-LOW|Clerk#000000241|0|even accounts boost | +3299|91|F|42867.92|1993-12-26|3-MEDIUM|Clerk#000000853|0|bold deposits. special instructions sleep care| +3300|118|O|27049.22|1995-07-15|5-LOW|Clerk#000000198|0|ses. carefully unusual instructions must have to detect about the blithel| +3301|133|F|48497.09|1994-09-04|4-NOT SPECIFIED|Clerk#000000325|0|ular gifts impress enticingly carefully express deposits; instructions boo| +3302|34|O|38330.42|1995-11-14|2-HIGH|Clerk#000000367|0|eep blithely ironic requests. quickly even courts haggle slyly| +3303|145|O|97758.28|1997-12-14|4-NOT SPECIFIED|Clerk#000000661|0|nto beans sleep furiously above the carefully ironic | +3328|7|F|139580.85|1992-11-19|5-LOW|Clerk#000000384|0|ake among the express accounts? carefully ironic packages cajole never.| +3329|4|O|46107.70|1995-07-03|2-HIGH|Clerk#000000236|0|old deposits. special accounts haggle furiousl| +3330|7|F|43255.19|1994-12-19|1-URGENT|Clerk#000000124|0|kages use. carefully regular deposits cajole carefully about | +3331|91|F|65189.17|1993-05-21|2-HIGH|Clerk#000000901|0|uffily carefully sly accounts. blithely unu| +3332|143|F|73739.06|1994-11-05|1-URGENT|Clerk#000000840|0|ans detect carefully furiously final deposits: regular accoun| +3333|92|F|197973.22|1992-09-16|4-NOT SPECIFIED|Clerk#000000157|0|ctions boost slyly quickly even accounts. deposits along| +3334|76|O|28930.68|1996-02-18|5-LOW|Clerk#000000532|0|ounts maintain carefully. furiously close request| +3335|49|O|112603.34|1995-10-15|3-MEDIUM|Clerk#000000694|0| deposits poach. ironic ideas about the carefully ironi| +3360|103|O|168750.48|1998-01-23|5-LOW|Clerk#000000254|0| the deposits. fluffily bold requests cajole regula| +3361|49|F|75026.51|1992-08-23|4-NOT SPECIFIED|Clerk#000000577|0|unts detect furiously instructions. slow deposi| +3362|140|O|183176.60|1995-07-29|5-LOW|Clerk#000000011|0|the quickly pending deposits. silent, ev| +3363|52|O|91017.61|1995-09-23|2-HIGH|Clerk#000000615|0|posits. ironic, final deposits are furiously slyly pending | +3364|46|O|108412.57|1997-06-21|1-URGENT|Clerk#000000280|0|y even foxes? blithely stea| +3365|82|F|174634.12|1994-11-09|2-HIGH|Clerk#000000126|0|he slyly regular foxes nag about the accounts. fluffily | +3366|52|O|13603.08|1997-05-18|1-URGENT|Clerk#000000160|0| pinto beans upon the quickly expres| +3367|73|F|101339.68|1992-12-31|4-NOT SPECIFIED|Clerk#000000029|0|efully blithely ironic pinto beans. carefully close | +3392|74|O|96057.42|1995-10-28|1-URGENT|Clerk#000000325|0|es thrash blithely depths. bold multipliers wake f| +3393|98|O|183104.71|1995-07-04|2-HIGH|Clerk#000000076|0|even requests. excuses are carefully deposits. fluf| +3394|149|O|162165.94|1996-05-05|4-NOT SPECIFIED|Clerk#000000105|0| blithely among the attainments. carefully final accounts nag blit| +3395|149|F|141486.77|1994-10-30|4-NOT SPECIFIED|Clerk#000000682|0|ideas haggle beside the ev| +3396|149|F|196443.16|1994-05-21|3-MEDIUM|Clerk#000000868|0|uffily regular platelet| +3397|130|F|80084.61|1994-06-23|3-MEDIUM|Clerk#000000048|0|yly. final deposits wake f| +3398|67|O|1147.42|1996-09-23|1-URGENT|Clerk#000000818|0|uthless, special courts atop the unusual accounts grow fur| +3399|122|P|56938.16|1995-02-28|4-NOT SPECIFIED|Clerk#000000575|0|the carefully sly accounts. regular, pending theodolites wa| +3424|103|O|42410.57|1996-08-21|1-URGENT|Clerk#000000190|0|ven requests are quickly pending accounts. blithely furious requests | +3425|115|O|157040.57|1996-03-31|4-NOT SPECIFIED|Clerk#000000188|0|ions. deposits nag blithely alongside of the carefully f| +3426|53|O|91929.93|1996-10-16|3-MEDIUM|Clerk#000000283|0|alongside of the slyly| +3427|4|O|133451.14|1997-05-29|4-NOT SPECIFIED|Clerk#000000404|0|y final pinto beans snooze fluffily bold asymptot| +3428|10|O|88047.04|1996-04-07|5-LOW|Clerk#000000953|0|lar excuses. slyly pending ideas detect p| +3429|146|O|141902.54|1997-01-06|4-NOT SPECIFIED|Clerk#000000737|0|l deposits cajole furiously enticing deposits. blithe packages haggle careful| +3430|113|F|161066.22|1994-12-12|4-NOT SPECIFIED|Clerk#000000664|0| regular attainments are at the final foxes. final packages along the blithe| +3431|47|F|45536.27|1993-08-22|1-URGENT|Clerk#000000439|0| sleep. slyly busy Tiresias a| +3456|46|F|32796.35|1993-06-01|5-LOW|Clerk#000000924|0|es promise slyly. ironicall| +3457|25|P|174223.20|1995-04-27|4-NOT SPECIFIED|Clerk#000000849|0|ely thin asymptotes. deposits kindle. pending| +3458|95|F|153069.14|1994-12-22|2-HIGH|Clerk#000000392|0|rges snooze. slyly unusua| +3459|119|F|127134.05|1994-07-28|4-NOT SPECIFIED|Clerk#000000777|0|n instructions? carefully regular excuses are blithely. silent, ironi| +3460|82|O|245976.74|1995-10-03|2-HIGH|Clerk#000000078|0|ans integrate carefu| +3461|100|F|190960.69|1993-01-31|1-URGENT|Clerk#000000504|0|al, bold deposits cajole fluffily fluffily final foxes. pending ideas beli| +3462|133|O|63590.17|1997-05-17|3-MEDIUM|Clerk#000000657|0|uriously express asympto| +3463|89|F|85255.56|1993-08-18|1-URGENT|Clerk#000000545|0|ding to the carefully ironic deposits| +3488|148|F|92716.17|1995-01-08|3-MEDIUM|Clerk#000000694|0|cording to the carefully regular deposits. re| +3489|109|F|62453.97|1993-07-29|3-MEDIUM|Clerk#000000307|0|s detect. carefully even platelets across the fur| +3490|91|O|100106.96|1997-05-26|5-LOW|Clerk#000000703|0|gular ideas. furiously silent deposits across the unusual accounts boost i| +3491|83|O|50287.06|1998-06-24|1-URGENT|Clerk#000000560|0|nic orbits believe carefully across the | +3492|103|F|168721.45|1994-11-24|5-LOW|Clerk#000000066|0|packages along the regular foxes lose final dependencie| +3493|82|F|41686.10|1993-08-24|2-HIGH|Clerk#000000887|0|lyly special accounts use blithely across the furiously sil| +3494|49|F|136058.70|1993-04-04|5-LOW|Clerk#000000559|0|r instructions haggle. accounts cajole. carefully final requests at the | +3495|31|O|58666.79|1996-02-26|2-HIGH|Clerk#000000441|0|nticing excuses are carefully| +3520|125|O|151233.65|1997-08-04|1-URGENT|Clerk#000000023|0|hely. ideas nag; even, even fo| +3521|7|F|142029.67|1992-10-26|5-LOW|Clerk#000000812|0|y even instructions cajole carefully above the bli| +3522|26|F|151515.08|1994-09-26|5-LOW|Clerk#000000250|0|deposits-- slyly stealthy requests boost caref| +3523|149|O|129657.08|1998-04-07|2-HIGH|Clerk#000000688|0|are on the carefully even depe| +3524|94|F|22767.49|1992-05-03|2-HIGH|Clerk#000000607|0|efully unusual tithes among the foxes use blithely daringly bold deposits. re| +3525|109|O|100749.60|1995-12-22|4-NOT SPECIFIED|Clerk#000000084|0|s nag among the blithely e| +3526|56|F|53827.34|1995-03-16|5-LOW|Clerk#000000364|0|to the quickly special deposits print agai| +3527|56|O|145232.09|1997-06-21|5-LOW|Clerk#000000874|0|regular ideas across the quickly bold theodo| +3552|35|O|103656.44|1997-04-23|2-HIGH|Clerk#000000973|0| the ironic packages. furiously | +3553|91|F|119838.14|1994-05-18|3-MEDIUM|Clerk#000000270|0|counts mold furiously. slyly i| +3554|44|O|98335.61|1995-06-17|5-LOW|Clerk#000000931|0|hely ironic requests haggl| +3555|46|O|134442.37|1996-07-07|5-LOW|Clerk#000000585|0|s nag carefully regular, even pinto be| +3556|16|F|114681.55|1992-09-23|4-NOT SPECIFIED|Clerk#000000140|0|e. dependencies need to haggle alongs| +3557|121|F|85477.89|1992-11-09|2-HIGH|Clerk#000000291|0|ithely courts. furi| +3558|28|O|112912.00|1996-02-29|1-URGENT|Clerk#000000841|0|around the furiously even requests. quickl| +3559|106|F|30722.49|1992-10-24|3-MEDIUM|Clerk#000000634|0|sly deposits. fluffily final ideas cajole careful| +3584|13|O|80487.97|1997-08-11|1-URGENT|Clerk#000000760|0|fully bold packages. fluffily final braids haggle final, ironic dolphins. b| +3585|139|F|159015.39|1994-11-23|2-HIGH|Clerk#000000988|0|regular asymptotes. bold pains above the carefully pending asymptot| +3586|121|F|112845.04|1993-12-05|2-HIGH|Clerk#000000438|0|he quickly final courts. carefully regular requests nag unusua| +3587|79|O|174798.97|1996-05-10|4-NOT SPECIFIED|Clerk#000000443|0|ular patterns detect | +3588|119|F|207925.83|1995-03-19|4-NOT SPECIFIED|Clerk#000000316|0|ong the pains. evenly unusual | +3589|31|F|39103.37|1994-05-26|2-HIGH|Clerk#000000023|0|ithe deposits nag furiously. furiously pending packages sleep f| +3590|149|P|218482.70|1995-05-13|5-LOW|Clerk#000000986|0|lyly final deposits.| +3591|136|F|98140.86|1993-12-08|3-MEDIUM|Clerk#000000144|0|ual foxes haggle! unusual request| +3616|128|F|60933.29|1994-02-16|4-NOT SPECIFIED|Clerk#000000268|0|uickly about the quickly final requests. fluffily final packages wake evenly| +3617|40|O|126205.42|1996-03-19|3-MEDIUM|Clerk#000000886|0|the carefully regular platelets ha| +3618|10|O|136954.81|1997-12-13|3-MEDIUM|Clerk#000000894|0|. ideas run carefully. thin, pending | +3619|149|O|222274.54|1996-11-20|2-HIGH|Clerk#000000211|0|uests mold after the blithely ironic excuses. slyly pending pa| +3620|44|O|59291.75|1997-03-07|5-LOW|Clerk#000000124|0|le quickly against the epitaphs. requests sleep slyly according to the| +3621|142|F|106150.05|1993-05-06|3-MEDIUM|Clerk#000000643|0|kly unusual deposits. qu| +3622|91|O|109202.90|1995-11-27|5-LOW|Clerk#000000012|0|c deposits are fluffily about the blithely final theo| +3623|4|O|175017.68|1996-12-26|1-URGENT|Clerk#000000184|0|- ironic excuses boost quickly in place | +3648|125|F|180417.11|1993-06-17|5-LOW|Clerk#000000717|0|foxes. unusual deposits boost quickly. slyly regular asymptotes across t| +3649|40|F|124470.32|1994-07-06|5-LOW|Clerk#000000349|0|taphs boost above the final p| +3650|46|F|189547.57|1992-05-28|4-NOT SPECIFIED|Clerk#000000454|0|kages sleep fluffily slyly| +3651|100|O|113191.45|1998-04-27|1-URGENT|Clerk#000000222|0|ly unusual deposits thrash quickly after the ideas.| +3652|107|O|107732.23|1997-02-25|4-NOT SPECIFIED|Clerk#000000024|0|sly even requests after the | +3653|40|F|142866.39|1994-03-27|1-URGENT|Clerk#000000402|0| pearls. bold accounts are along the ironic,| +3654|7|F|222653.54|1992-06-03|5-LOW|Clerk#000000475|0|s cajole slyly carefully special theodolites. even deposits haggl| +3655|49|F|74882.22|1992-10-06|1-URGENT|Clerk#000000815|0|er the carefully unusual deposits sleep quickly according to| +3680|127|F|124402.59|1992-12-10|4-NOT SPECIFIED|Clerk#000000793|0|ular platelets. carefully regular packages cajole blithely al| +3681|52|F|36889.65|1992-04-04|1-URGENT|Clerk#000000566|0|. ironic deposits against the ironic, regular frets use pending plat| +3682|32|O|67525.43|1997-01-22|2-HIGH|Clerk#000000001|0|es haggle carefully. decoys nag | +3683|88|F|99960.46|1993-03-04|2-HIGH|Clerk#000000248|0|ze across the express foxes. carefully special acco| +3684|23|F|89509.91|1993-07-20|2-HIGH|Clerk#000000835|0|bold accounts affix along the carefully ironic requ| +3685|16|F|154958.89|1992-01-17|3-MEDIUM|Clerk#000000954|0| sleep fluffily special ide| +3686|40|O|82190.77|1998-07-07|2-HIGH|Clerk#000000175|0|s. furiously final pinto beans poach carefully among | +3687|43|F|99851.38|1993-02-03|1-URGENT|Clerk#000000585|0|gular accounts. slyly regular instructions can are final ide| +3712|64|F|127527.05|1992-01-02|2-HIGH|Clerk#000000032|0| promise according | +3713|149|O|215342.63|1998-05-07|3-MEDIUM|Clerk#000000325|0|s haggle quickly. ironic, regular Tiresi| +3714|40|O|84493.55|1998-05-01|3-MEDIUM|Clerk#000000595|0|nding accounts. ironic pinto beans wake slyly. furiously pendin| +3715|65|O|64000.93|1996-03-18|1-URGENT|Clerk#000000463|0| always silent requests wake pinto beans. slyly pending foxes are aga| +3716|43|O|146221.66|1997-08-19|4-NOT SPECIFIED|Clerk#000000748|0| pending ideas haggle. ironic,| +3717|28|O|176525.53|1998-06-03|4-NOT SPECIFIED|Clerk#000000974|0|t the carefully even ideas use sp| +3718|31|O|63195.54|1996-10-23|2-HIGH|Clerk#000000016|0|refully. furiously final packages use carefully slyly pending deposits! final,| +3719|118|O|139902.71|1997-02-16|2-HIGH|Clerk#000000034|0|, enticing accounts are blithely among the daringly final asymptotes. furious| +3744|65|F|33085.68|1992-01-10|3-MEDIUM|Clerk#000000765|0|osits sublate about the regular requests. fluffily unusual accou| +3745|112|F|19405.73|1993-09-29|5-LOW|Clerk#000000181|0|ckages poach slyly against the foxes. slyly ironic instructi| +3746|74|F|80018.54|1994-09-11|4-NOT SPECIFIED|Clerk#000000188|0|. express, special requests nag quic| +3747|149|O|204355.65|1996-08-20|1-URGENT|Clerk#000000226|0|refully across the final theodolites. carefully bold accounts cajol| +3748|53|O|83804.38|1998-02-28|1-URGENT|Clerk#000000156|0|slyly special packages| +3749|38|P|87073.89|1995-02-24|3-MEDIUM|Clerk#000000639|0|y regular instructions haggle blithel| +3750|97|P|177181.67|1995-04-30|3-MEDIUM|Clerk#000000885|0|y. express, even packages wake after the ide| +3751|10|F|202917.72|1994-04-27|4-NOT SPECIFIED|Clerk#000000925|0|sheaves. express, unusual t| +3776|85|F|150349.92|1992-11-20|2-HIGH|Clerk#000000698|0|efully even platelets slee| +3777|28|F|82467.29|1994-04-08|3-MEDIUM|Clerk#000000941|0| regular, special dolphins cajole enticingly ca| +3778|106|F|221036.31|1993-05-26|1-URGENT|Clerk#000000187|0| above the express requests. packages maintain fluffily according to| +3779|74|O|31538.94|1997-01-05|4-NOT SPECIFIED|Clerk#000000670|0| against the deposits. quickly bold instructions x-ray. pending fox| +3780|41|O|65385.42|1996-04-13|5-LOW|Clerk#000000967|0| around the brave, pendin| +3781|139|O|133864.82|1996-06-20|1-URGENT|Clerk#000000394|0|yly after the ruthless packages. pinto beans use slyly: never ironic dependenc| +3782|65|O|145096.17|1996-08-24|1-URGENT|Clerk#000000121|0|counts are. pending, regular asym| +3783|44|F|155017.92|1993-12-06|4-NOT SPECIFIED|Clerk#000000614|0| along the pinto beans. special packages use. regular theo| +3808|79|F|228054.01|1994-04-24|1-URGENT|Clerk#000000717|0|odolites. blithely ironic cour| +3809|148|O|143070.70|1996-05-01|5-LOW|Clerk#000000646|0| regular excuses. even theodolites are fluffily according to t| +3810|100|F|124675.27|1992-09-17|1-URGENT|Clerk#000000660|0|ters sleep across the carefully final | +3811|80|O|154967.89|1998-04-16|3-MEDIUM|Clerk#000000290|0|sits wake slyly abo| +3812|41|O|70502.52|1996-08-13|3-MEDIUM|Clerk#000000727|0|al, final requests cajole| +3813|146|O|77247.05|1998-06-29|1-URGENT|Clerk#000000531|0|g the furiously regular instructions| +3814|118|P|149451.88|1995-02-22|5-LOW|Clerk#000000669|0| the furiously pending theodo| +3815|104|O|14275.01|1997-08-26|1-URGENT|Clerk#000000249|0|es snooze carefully stealth| +3840|100|O|187156.38|1998-07-17|4-NOT SPECIFIED|Clerk#000000713|0|yly slow theodolites. enticingly | +3841|58|F|129033.13|1994-10-05|4-NOT SPECIFIED|Clerk#000000018|0| bold requests sleep quickly ironic packages. sometimes regular deposits nag | +3842|28|F|131447.03|1992-04-09|5-LOW|Clerk#000000418|0|silent ideas. final deposits use furiously. blithely express excuses cajole fu| +3843|10|O|34035.17|1997-01-04|4-NOT SPECIFIED|Clerk#000000693|0|eodolites; slyly unusual accounts nag boldly | +3844|79|F|6793.45|1994-12-29|1-URGENT|Clerk#000000686|0|r dolphins. slyly ironic theodolites ag| +3845|89|F|134333.33|1992-04-26|1-URGENT|Clerk#000000404|0|es among the pending, regular accounts sleep blithely blithely even de| +3846|49|O|123120.06|1998-02-05|2-HIGH|Clerk#000000877|0|y alongside of the slyl| +3847|34|F|7014.31|1993-03-12|5-LOW|Clerk#000000338|0|uriously even deposits. furiously pe| +3872|134|O|198538.68|1996-09-06|5-LOW|Clerk#000000943|0|counts boost slyly against the ironic platelets-- blithely p| +3873|55|O|95291.79|1998-03-30|4-NOT SPECIFIED|Clerk#000000791|0|express deposits-- even ideas | +3874|119|F|66455.34|1993-06-09|3-MEDIUM|Clerk#000000208|0|ular asymptotes sleep blithely ironic ideas. blithel| +3875|118|O|74483.95|1997-09-10|1-URGENT|Clerk#000000587|0| solve among the fluffily even | +3876|29|O|95126.32|1996-08-02|5-LOW|Clerk#000000708|0|into beans. blithely| +3877|17|F|178492.01|1993-05-21|5-LOW|Clerk#000000652|0|foxes. thinly bold reques| +3878|88|O|59989.66|1997-03-23|1-URGENT|Clerk#000000314|0|e carefully regular platelets. special, express dependencies slee| +3879|142|O|80274.22|1995-11-23|1-URGENT|Clerk#000000231|0|sts along the quickly ironic sentiments cajole carefully according to t| +3904|149|O|39338.44|1997-11-15|4-NOT SPECIFIED|Clerk#000000883|0|sits haggle furiously across the requests. theodolites ha| +3905|22|F|56227.04|1993-12-21|4-NOT SPECIFIED|Clerk#000000573|0|usly even accounts lose quietly above the slyly express p| +3906|46|F|145630.76|1992-05-28|3-MEDIUM|Clerk#000000867|0|ironic theodolites haggle blithely above the final re| +3907|67|F|240457.56|1992-08-19|3-MEDIUM|Clerk#000000084|0|gular pinto beans sleep f| +3908|43|F|57127.71|1993-03-09|3-MEDIUM|Clerk#000000490|0|ounts cajole. regularly| +3909|22|O|82746.74|1998-07-27|1-URGENT|Clerk#000000980|0|nic, special theodolites sleep furiously! furiously | +3910|64|O|47272.67|1996-08-26|3-MEDIUM|Clerk#000000270|0|ickly. furiously final packag| +3911|10|P|35019.95|1995-03-17|4-NOT SPECIFIED|Clerk#000000818|0|he fluffily final forges haggle slyly according to the blithely| +3936|32|O|168618.39|1996-11-07|2-HIGH|Clerk#000000200|0|iously express packages engage slyly fina| +3937|94|O|187516.29|1997-11-30|4-NOT SPECIFIED|Clerk#000000189|0|ckages boost carefully blithely q| +3938|31|F|46918.22|1993-03-03|1-URGENT|Clerk#000000199|0|. unusual, final foxes haggle| +3939|70|O|8720.45|1996-01-11|5-LOW|Clerk#000000647|0|ly ruthlessly silent requests. blithely regular requests haggle blithely wh| +3940|149|O|129012.84|1996-02-14|5-LOW|Clerk#000000363|0|e above the ideas. quickly even dependencies along the blithely ir| +3941|136|O|95453.80|1996-08-29|2-HIGH|Clerk#000000503|0|gular theodolites integrate quickly | +3942|76|F|38596.81|1993-06-28|4-NOT SPECIFIED|Clerk#000000608|0|eas cajole bold requests. idly silent instructions | +3943|40|O|60314.97|1996-10-09|5-LOW|Clerk#000000482|0|se alongside of the final pinto beans. regular packages boost across the ca| +3968|25|O|121704.45|1997-02-17|4-NOT SPECIFIED|Clerk#000000431|0| the slyly special accounts; | +3969|52|O|169797.40|1997-05-14|2-HIGH|Clerk#000000731|0|uriously final dependencies slee| +3970|76|F|163709.85|1992-03-27|3-MEDIUM|Clerk#000000190|0|luffily furiously regular deposits. blithely special requests cajole blithely| +3971|104|O|47925.47|1996-06-28|5-LOW|Clerk#000000287|0|alongside of the instructions ought to are | +3972|124|F|1861.19|1994-04-21|3-MEDIUM|Clerk#000000049|0|y regular requests haggle quickly. pending, express acco| +3973|103|F|91541.48|1992-03-24|4-NOT SPECIFIED|Clerk#000000114|0|somas according to the quickly even instructions wake fu| +3974|94|O|56779.06|1996-03-05|4-NOT SPECIFIED|Clerk#000000938|0|deposits are furiously beneath the bl| +3975|118|O|37804.43|1995-04-11|3-MEDIUM|Clerk#000000016|0|ts. regular, regular Tiresias play furiously. ironi| +4000|70|F|84053.93|1992-01-04|5-LOW|Clerk#000000339|0|le carefully closely even pinto beans. regular, ironic foxes against the| +4001|115|O|95929.46|1997-05-15|3-MEDIUM|Clerk#000000878|0|detect. asymptotes sleep furio| +4002|104|O|76518.11|1997-04-08|5-LOW|Clerk#000000097|0| regular braids are. furiously even patterns agains| +4003|112|F|17603.01|1993-01-27|1-URGENT|Clerk#000000177|0| blithe theodolites are slyly. slyly silent accounts toward| +4004|70|F|220715.14|1993-05-07|3-MEDIUM|Clerk#000000273|0|accounts among the blithely regular sentiments | +4005|140|O|129062.13|1996-11-20|2-HIGH|Clerk#000000341|0|ily according to the slyly iron| +4006|35|F|70557.05|1995-01-04|3-MEDIUM|Clerk#000000765|0|ly ironic packages integrate. regular requests alongside of | +4007|8|F|116193.97|1993-06-18|2-HIGH|Clerk#000000623|0|ecial packages. slyly regular accounts integrate | +4032|10|O|62497.51|1998-02-26|3-MEDIUM|Clerk#000000686|0|iresias sleep slyly regular ideas. quickly unusual| +4033|83|F|57740.74|1993-06-02|5-LOW|Clerk#000000181|0|ously bold instructions haggle furiously above the fluf| +4034|94|F|186912.51|1993-11-14|4-NOT SPECIFIED|Clerk#000000548|0|ts x-ray. express requests affix fluffily regular theodolites. pending, fina| +4035|118|F|22840.21|1992-02-19|5-LOW|Clerk#000000097|0|he ironic deposits sleep blith| +4036|47|O|82563.10|1997-04-26|3-MEDIUM|Clerk#000000398|0|ly express deposits nag slyly. ironic, final asymptotes boost bra| +4037|121|F|36389.43|1993-03-24|2-HIGH|Clerk#000000384|0|t carefully above the unusual the| +4038|94|O|155045.39|1996-01-06|1-URGENT|Clerk#000000272|0|re slyly. silent requests wake quickly. regular packages play quickly | +4039|29|O|143753.01|1997-11-16|1-URGENT|Clerk#000000358|0|ly ironic deposits. ironic reques| +4064|130|O|148500.71|1996-10-10|4-NOT SPECIFIED|Clerk#000000598|0|ccounts. furiously unusual theodolites wake carefully about| +4065|80|F|156345.64|1994-06-09|1-URGENT|Clerk#000000131|0|even foxes! slyly final deposits agai| +4066|32|O|176911.21|1997-01-27|4-NOT SPECIFIED|Clerk#000000286|0|yly ironic dinos. quickly regular accounts haggle. requests wa| +4067|16|F|136517.34|1992-10-07|2-HIGH|Clerk#000000027|0|tes boost furiously quick asymptotes. final deposits of the dolphins solv| +4068|125|O|71852.67|1996-09-18|3-MEDIUM|Clerk#000000203|0|lly even accounts wake furiously across the unusual platelets. unusu| +4069|73|F|198816.13|1992-05-13|3-MEDIUM|Clerk#000000359|0|deposits: slyly bold ideas detect furiously. f| +4070|29|O|98275.37|1995-06-12|2-HIGH|Clerk#000000713|0|xpress ideas poach ab| +4071|148|O|67789.42|1996-09-15|4-NOT SPECIFIED|Clerk#000000486|0|nal deposits. pending deposits d| +4096|139|F|81089.61|1992-07-03|4-NOT SPECIFIED|Clerk#000000706|0|sits. quickly thin deposits x-ray blith| +4097|10|O|134308.04|1996-05-24|1-URGENT|Clerk#000000475|0|ickly under the even accounts. even packages after the furiously express| +4098|23|O|48478.54|1996-11-05|4-NOT SPECIFIED|Clerk#000000491|0|otes. quickly final requests after the stealthily ironic pinto bean| +4099|17|F|207364.80|1992-08-21|1-URGENT|Clerk#000000379|0|r platelets. slyly regular requests cajole carefully against the| +4100|4|O|3892.77|1996-03-12|3-MEDIUM|Clerk#000000429|0|posits. carefully unusual packages use pending deposits. regular she| +4101|142|F|21640.10|1993-11-22|4-NOT SPECIFIED|Clerk#000000704|0|y around the express, careful epitaphs. accounts use fluffily. quickly p| +4102|22|O|128786.57|1996-03-17|1-URGENT|Clerk#000000675|0|nding dependencies was slyly about the bl| +4103|106|F|38164.23|1992-07-03|5-LOW|Clerk#000000679|0|fully ironic dependencies.| +4128|139|O|5472.17|1995-10-07|4-NOT SPECIFIED|Clerk#000000635|0|ctions. dependencies from the slyly regular accounts nag slyly fu| +4129|32|F|67226.28|1993-06-26|3-MEDIUM|Clerk#000000541|0|nwind. quickly final theodolites use packages. accounts| +4130|104|O|47823.04|1996-03-10|5-LOW|Clerk#000000609|0|omise alongside of the carefully final foxes. blithel| +4131|44|O|145971.60|1998-01-30|1-URGENT|Clerk#000000612|0| above the foxes hang | +4132|19|P|65601.08|1995-05-29|4-NOT SPECIFIED|Clerk#000000158|0|ld asymptotes solve alongside of the express, final packages. fluffily fi| +4133|101|F|31693.88|1992-08-07|4-NOT SPECIFIED|Clerk#000000268|0|al, express foxes. quickly pending deposits might cajole alongsi| +4134|97|F|125191.12|1995-01-12|1-URGENT|Clerk#000000171|0|fully even deposits. regular de| +4135|37|O|99577.55|1997-03-10|3-MEDIUM|Clerk#000000627|0|ly quietly even ideas. deposits haggle blithely| +4160|55|O|82493.07|1996-08-20|5-LOW|Clerk#000000283|0|the carefully special accounts. furiously regular dugouts alongs| +4161|118|F|198995.21|1993-08-21|5-LOW|Clerk#000000047|0|nts. fluffily regular foxes above the quickly daring reques| +4162|22|F|72359.55|1992-02-10|5-LOW|Clerk#000000179|0|r packages are slyly accounts. furiously special foxes detect carefully re| +4163|64|F|11493.80|1992-12-21|2-HIGH|Clerk#000000268|0| integrate furiously slyly regular depende| +4164|94|O|8709.16|1998-07-03|2-HIGH|Clerk#000000720|0| regularly busy theodolites boost furiously quickly bold packages. express, s| +4165|4|O|11405.40|1997-07-25|3-MEDIUM|Clerk#000000621|0|special foxes affix never blithely ironic pinto beans; blithely | +4166|43|F|100671.06|1993-02-28|5-LOW|Clerk#000000757|0|quickly sly forges impress. careful foxes across the blithely even a| +4167|28|O|62108.45|1998-06-17|1-URGENT|Clerk#000000917|0|kly furiously even deposits. unu| +4192|146|O|197192.95|1998-04-19|1-URGENT|Clerk#000000369|0|equests above the slyly regular pinto beans unwi| +4193|4|F|143191.54|1994-01-09|2-HIGH|Clerk#000000201|0|ng accounts haggle quickly. packages use fluffily ironic excu| +4194|106|F|62972.29|1994-10-16|3-MEDIUM|Clerk#000000385|0| instructions are quickly even pinto beans. courts boost furiously regular, ev| +4195|104|F|54478.95|1993-05-29|4-NOT SPECIFIED|Clerk#000000777|0| pinto beans cajole furiously theodolites-- slyly regular deposits doub| +4196|106|O|201455.98|1998-05-15|3-MEDIUM|Clerk#000000532|0|affix carefully. quickly final requests | +4197|92|O|217709.03|1996-08-13|4-NOT SPECIFIED|Clerk#000000264|0| pinto beans according| +4198|143|O|105789.01|1997-06-16|3-MEDIUM|Clerk#000000583|0|g the special packages haggle pen| +4199|5|F|30494.62|1992-02-13|1-URGENT|Clerk#000000309|0|e blithely. special deposits haggle slyly final foxes. carefully even| +4224|70|O|150655.44|1997-07-14|1-URGENT|Clerk#000000034|0|jole quickly final dolphins. slyly pending foxes wake furiously bold pl| +4225|128|O|72533.07|1997-06-03|3-MEDIUM|Clerk#000000992|0|r the platelets nag among the special deposits. ironic, ironic re| +4226|92|F|29827.44|1993-03-09|5-LOW|Clerk#000000203|0|phins wake slyly regular packages. deposits haggle slowl| +4227|133|F|92261.08|1995-02-24|1-URGENT|Clerk#000000063|0|ng the requests; ideas haggle fluffily. slyly unusual ideas c| +4228|110|O|22072.16|1997-03-28|5-LOW|Clerk#000000309|0|pecial requests aft| +4229|14|O|75145.87|1998-03-03|1-URGENT|Clerk#000000301|0|p furiously: final excuses hagg| +4230|140|F|219709.60|1992-03-04|1-URGENT|Clerk#000000364|0|lly ironic deposits integrate carefully about the fu| +4231|86|O|111403.66|1997-11-20|4-NOT SPECIFIED|Clerk#000000630|0|ly final accounts cajole furiously accounts. bravely ironic platelets am| +4256|118|F|23067.48|1992-04-05|4-NOT SPECIFIED|Clerk#000000043|0|y alongside of the fluffily iro| +4257|17|P|41723.86|1995-03-25|3-MEDIUM|Clerk#000000682|0|r ideas cajole along the blithely regular gifts.| +4258|92|O|133829.35|1996-10-27|4-NOT SPECIFIED|Clerk#000000364|0|efully final platelets around the blit| +4259|104|O|12918.70|1997-10-09|5-LOW|Clerk#000000781|0|es snooze slyly against the furiously unusual ideas. furious| +4260|142|F|18566.14|1992-05-16|4-NOT SPECIFIED|Clerk#000000919|0|e among the fluffily bold accounts.| +4261|118|F|83665.20|1992-10-03|1-URGENT|Clerk#000000662|0| about the even, pending packages. slyly bold deposits boost| +4262|88|O|176278.57|1996-08-04|3-MEDIUM|Clerk#000000239|0| of the furious accounts. furiously regular accounts w| +4263|4|O|158885.83|1998-03-16|1-URGENT|Clerk#000000265|0|sly ruthless deposits. final packages are instructions. fu| +4288|34|F|75030.81|1992-12-04|4-NOT SPECIFIED|Clerk#000000823|0|usly carefully even theodolites: slyly express pac| +4289|125|F|20752.62|1993-10-07|3-MEDIUM|Clerk#000000912|0|e carefully close instructions. slyly special reques| +4290|41|F|26128.99|1995-01-15|3-MEDIUM|Clerk#000000688|0| slyly quickly bold requests. final deposits haggle pending ideas! som| +4291|89|F|71822.86|1993-11-29|3-MEDIUM|Clerk#000000655|0| sleep fluffily between the bold packages. bold| +4292|25|F|145906.24|1992-01-09|3-MEDIUM|Clerk#000000794|0| ruthlessly. slyly bo| +4293|103|O|198322.91|1996-08-20|2-HIGH|Clerk#000000750|0|ly packages. regular packages nag according to t| +4294|49|F|232194.74|1992-08-15|3-MEDIUM|Clerk#000000407|0|ng pinto beans breach. slyly express requests bo| +4295|5|O|77754.62|1996-02-10|3-MEDIUM|Clerk#000000023|0|e boldly bold dependencies| +4320|115|O|67049.37|1996-12-08|4-NOT SPECIFIED|Clerk#000000223|0|ages haggle after the slowly bold se| +4321|16|F|118896.95|1994-07-18|3-MEDIUM|Clerk#000000041|0|ending deposits are carefully carefully regular packa| +4322|142|O|149671.92|1998-03-13|3-MEDIUM|Clerk#000000433|0|totes nag across the fluffily special instructions. quickly silent hockey | +4323|104|F|27598.17|1994-01-23|2-HIGH|Clerk#000000282|0|lve after the slyly regular multipliers. even, regular excus| +4324|73|O|178249.05|1995-07-17|1-URGENT|Clerk#000000800|0|ccounts. slyly stealthy requests shall have t| +4325|130|O|20214.49|1996-07-18|2-HIGH|Clerk#000000591|0|y around the always ev| +4326|29|O|39048.94|1996-10-27|4-NOT SPECIFIED|Clerk#000000869|0|packages. carefully express deposit| +4327|146|P|126235.35|1995-03-16|2-HIGH|Clerk#000000571|0|yly pending braids. final requests abo| +4352|14|O|18653.09|1997-11-26|2-HIGH|Clerk#000000620|0|ly final platelets integrate carefully even requ| +4353|73|O|21815.30|1997-12-12|2-HIGH|Clerk#000000790|0|uickly even ideas cajole| +4354|145|F|179827.12|1994-09-30|4-NOT SPECIFIED|Clerk#000000046|0|pending notornis. requests serve | +4355|4|O|186370.23|1996-11-16|1-URGENT|Clerk#000000362|0|ndencies use furiously across the regular | +4356|97|F|39828.51|1994-04-11|5-LOW|Clerk#000000956|0| asymptotes sleep blithely. asymptotes sleep. blithely regul| +4357|47|O|67045.94|1997-10-23|4-NOT SPECIFIED|Clerk#000000031|0|ages nag between the| +4358|25|O|46298.53|1997-08-12|1-URGENT|Clerk#000000692|0|according to the fluffily special asymptotes | +4359|16|F|107824.40|1993-03-03|1-URGENT|Clerk#000000393|0|sts. special, unusual deposits across the ironic theodo| +4384|25|F|52562.16|1992-07-13|1-URGENT|Clerk#000000192|0|onic platelets. furiously regular asymptotes according to the special pac| +4385|122|O|39190.62|1996-08-06|2-HIGH|Clerk#000000597|0|ully final requests. ironic, even dolphins above the regular | +4386|61|O|134413.58|1998-02-06|5-LOW|Clerk#000000070|0| dolphins. silent, idle pinto beans | +4387|110|O|116740.67|1995-10-23|1-URGENT|Clerk#000000025|0|ter the regular pinto beans. special, final gifts above the requests wi| +4388|10|O|69668.22|1996-03-28|2-HIGH|Clerk#000000715|0|ts wake against the carefully final accounts. sly| +4389|55|F|120324.82|1994-05-05|3-MEDIUM|Clerk#000000403|0|wly express excuses after the permanently even instructions are| +4390|7|P|140608.69|1995-05-23|1-URGENT|Clerk#000000691|0|inal pinto beans. exp| +4391|38|F|48284.06|1992-02-18|2-HIGH|Clerk#000000880|0|regular accounts. even depo| +4416|149|F|76067.10|1992-06-30|5-LOW|Clerk#000000391|0| deposits. ideas cajole express theodolites: | +4417|67|O|60868.39|1998-07-09|1-URGENT|Clerk#000000365|0|ideas are alongside of the blithely final reque| +4418|61|F|47099.71|1993-03-25|3-MEDIUM|Clerk#000000731|0|pecial pinto beans. close foxes affix iron| +4419|104|O|94030.43|1996-06-12|4-NOT SPECIFIED|Clerk#000000410|0|ages wake furiously slyly thin theodolit| +4420|109|F|6088.41|1994-06-18|1-URGENT|Clerk#000000706|0|lly bold deposits along the bold, pending foxes detect blithely after the acco| +4421|10|O|258779.02|1997-04-04|3-MEDIUM|Clerk#000000246|0|t the pending warhorses. express waters a| +4422|70|P|107140.22|1995-05-22|3-MEDIUM|Clerk#000000938|0|ly bold accounts sleep special, regular foxes. doggedly regular in| +4423|64|F|4913.06|1995-02-17|5-LOW|Clerk#000000888|0|excuses are ruthless| +4448|70|O|127191.47|1998-05-21|2-HIGH|Clerk#000000428|0|. deposits haggle around the silent packages; slyly unusual packages| +4449|10|O|48206.14|1998-02-08|5-LOW|Clerk#000000035|0|ourts are carefully even deposits. pending | +4450|106|O|110194.31|1997-07-15|1-URGENT|Clerk#000000867|0|quests boost. furiously even realms are blithely bold requests. bl| +4451|4|F|92851.80|1994-10-01|1-URGENT|Clerk#000000181|0|. carefully final foxes along the quickly express T| +4452|13|F|64838.66|1994-06-21|5-LOW|Clerk#000000985|0|oxes are slyly. express, ironic pinto beans wake after the quickly pending re| +4453|65|O|137030.40|1997-04-01|3-MEDIUM|Clerk#000000603|0|ages could have to nag slyly furiously even asymptotes! slowly regular | +4454|142|F|159578.94|1994-02-02|5-LOW|Clerk#000000411|0|uriously regular pint| +4455|19|F|102534.63|1993-10-11|3-MEDIUM|Clerk#000000924|0|even requests. bravely regular foxes according to the carefully unusual | +4480|85|F|28658.26|1994-03-31|4-NOT SPECIFIED|Clerk#000000534|0|press, bold deposits boost blit| +4481|148|O|77705.40|1996-03-30|5-LOW|Clerk#000000443|0|press sheaves cajole furio| +4482|82|P|63535.56|1995-05-15|4-NOT SPECIFIED|Clerk#000000534|0|ravely bold accounts. furiously ironic instructions affix quickly. pend| +4483|52|F|126597.21|1992-03-07|3-MEDIUM|Clerk#000000615|0|its. blithely idle accounts run; theodolites wake carefully around the fi| +4484|131|O|237947.61|1996-12-24|1-URGENT|Clerk#000000392|0|ct across the pinto beans. quickly pending excuses engage furiously.| +4485|53|F|182432.17|1994-11-13|3-MEDIUM|Clerk#000000038|0|es wake slyly even packages. blithely brave requests nag above the regul| +4486|37|O|135613.18|1998-03-03|2-HIGH|Clerk#000000656|0|ffily according to the carefully pending acc| +4487|46|F|109469.90|1993-02-23|3-MEDIUM|Clerk#000000017|0|s up the never pending excuses wake furiously special pinto beans. furiously i| +4512|70|O|148682.82|1995-10-25|5-LOW|Clerk#000000393|0|ending instructions maintain fu| +4513|85|O|119820.38|1996-03-15|5-LOW|Clerk#000000154|0|ests. final, final ideas| +4514|97|F|143899.85|1994-04-30|3-MEDIUM|Clerk#000000074|0|deposits according to the carefull| +4515|140|F|161745.44|1992-03-17|1-URGENT|Clerk#000000191|0|quests among the accounts sleep boldly about the regular f| +4516|130|F|35949.14|1994-03-29|3-MEDIUM|Clerk#000000739|0|ing packages sleep slyly regular attainments| +4517|113|O|47614.08|1998-03-07|4-NOT SPECIFIED|Clerk#000000231|0|uriously final deposits doze furiously furiously reg| +4518|125|O|25861.74|1997-05-01|3-MEDIUM|Clerk#000000187|0|luffily against the spec| +4519|136|F|68885.66|1993-03-30|4-NOT SPECIFIED|Clerk#000000938|0|ccording to the final | +4544|112|O|151148.81|1997-08-07|3-MEDIUM|Clerk#000000435|0|g dependencies dazzle slyly ironic somas. carefu| +4545|59|F|143276.28|1993-01-17|4-NOT SPECIFIED|Clerk#000000303|0|ep. requests use sly| +4546|43|O|39906.87|1995-07-29|5-LOW|Clerk#000000373|0|ns sleep. regular, regular instructions maintai| +4547|109|F|52114.01|1993-08-23|3-MEDIUM|Clerk#000000519|0|uctions thrash platelets. slyly final foxes wake slyly against th| +4548|127|O|139915.23|1996-06-28|5-LOW|Clerk#000000798|0| in place of the blithely express sentiments haggle slyly r| +4549|64|O|43889.17|1998-03-05|4-NOT SPECIFIED|Clerk#000000965|0|ully even deposits dazzle. fluffily pending ideas against the requests| +4550|118|F|27461.48|1994-12-29|2-HIGH|Clerk#000000748|0|s haggle carefully acco| +4551|109|O|82824.14|1996-02-09|2-HIGH|Clerk#000000462|0|ts. slyly quick theodolite| +4576|139|O|56936.10|1996-08-14|5-LOW|Clerk#000000798|0|e pending deposits. | +4577|79|O|104259.88|1998-05-02|5-LOW|Clerk#000000409|0|ly. unusual platelets are alw| +4578|91|F|95761.93|1992-09-13|5-LOW|Clerk#000000121|0| to the furiously ironic instructions? furiou| +4579|106|O|85927.85|1995-12-01|2-HIGH|Clerk#000000951|0|its wake quickly blithely specia| +4580|82|F|118464.65|1993-11-15|4-NOT SPECIFIED|Clerk#000000086|0|rs wake blithely regular requests. fluffily ev| +4581|79|F|89592.11|1992-09-04|4-NOT SPECIFIED|Clerk#000000687|0|ges. carefully pending accounts use furiously abo| +4582|19|O|18247.86|1996-07-04|1-URGENT|Clerk#000000638|0|g the furiously regular pac| +4583|22|F|206495.43|1994-09-25|3-MEDIUM|Clerk#000000240|0|equests. slyly even platelets was qui| +4608|80|F|157767.86|1994-06-17|1-URGENT|Clerk#000000259|0|y even instructions detect slyly asymptotes. blithely final packa| +4609|133|O|70462.84|1996-12-05|3-MEDIUM|Clerk#000000239|0|hang slyly slyly expre| +4610|26|F|135934.60|1993-06-18|5-LOW|Clerk#000000616|0|e carefully express pinto| +4611|29|F|166506.22|1993-01-10|2-HIGH|Clerk#000000152|0|. furiously regular instructions haggle dolphins. even instructions det| +4612|61|F|82598.87|1993-09-20|3-MEDIUM|Clerk#000000397|0|bove the deposits. even deposits dazzle. slyly express packages haggle sl| +4613|133|O|212339.55|1998-03-05|3-MEDIUM|Clerk#000000541|0|furiously blithely pending dependen| +4614|61|O|151801.06|1996-04-22|1-URGENT|Clerk#000000974|0| sauternes wake thinly special accounts. fur| +4615|29|F|10500.27|1993-08-27|3-MEDIUM|Clerk#000000982|0|jole after the fluffily pending foxes. packages affix carefully acco| +4640|97|O|81138.17|1996-01-01|5-LOW|Clerk#000000902|0|requests. deposits do detect above the blithely iron| +4641|134|F|98485.21|1993-01-20|4-NOT SPECIFIED|Clerk#000000755|0|ronic, final requests integrate slyly: specia| +4642|148|F|117537.87|1995-02-27|1-URGENT|Clerk#000000295|0|cial requests wake carefully around the regular, unusual ideas. furi| +4643|67|O|52414.19|1995-06-30|2-HIGH|Clerk#000000292|0|ously regular packages. unusual, special platel| +4644|94|O|85901.70|1998-01-17|5-LOW|Clerk#000000961|0|requests. fluffily even ideas bo| +4645|44|F|231012.22|1994-09-20|1-URGENT|Clerk#000000764|0|fully even instructions. final gifts sublate quickly final requests. bl| +4646|83|O|124637.19|1996-06-18|1-URGENT|Clerk#000000036|0|n place of the blithely qu| +4647|28|F|110958.36|1994-05-14|3-MEDIUM|Clerk#000000626|0|out the deposits. slyly final pinto beans haggle idly. slyly s| +4672|79|O|199593.71|1995-11-07|1-URGENT|Clerk#000000475|0|lyly final dependencies caj| +4673|82|O|58094.75|1996-08-13|4-NOT SPECIFIED|Clerk#000000914|0|c deposits are slyly. bravely ironic deposits cajole carefully after the | +4674|37|F|115411.37|1994-04-19|1-URGENT|Clerk#000000122|0|careful hockey players. carefully pending deposits caj| +4675|86|F|68817.08|1993-11-25|4-NOT SPECIFIED|Clerk#000000741|0|al deposits haggle slyly final| +4676|14|O|182025.95|1995-09-01|2-HIGH|Clerk#000000407|0|s. slyly bold accounts sleep furiously special| +4677|40|O|25661.87|1998-02-21|3-MEDIUM|Clerk#000000245|0|ly pending deposits after the carefully regular foxes sleep blithely after t| +4678|88|O|131752.07|1998-08-02|4-NOT SPECIFIED|Clerk#000000175|0|side of the bold platelets detect slyly blithely ironic e| +4679|88|F|7211.59|1993-01-20|2-HIGH|Clerk#000000905|0|ely regular accounts affix slyly. final dolphins are. furiously final de| +4704|2|O|63873.14|1996-08-16|4-NOT SPECIFIED|Clerk#000000256|0|lithely final requests about the fluffily regular | +4705|98|F|173340.09|1992-03-22|4-NOT SPECIFIED|Clerk#000000522|0| special instructions poa| +4706|25|F|101709.52|1992-12-29|4-NOT SPECIFIED|Clerk#000000722|0| packages above the never regular packages nag packages. deposits c| +4707|91|F|61052.10|1995-02-27|2-HIGH|Clerk#000000943|0|ully enticing accounts behind the regular| +4708|85|F|56998.36|1994-10-01|1-URGENT|Clerk#000000383|0|ly thinly even accounts. unusu| +4709|26|O|49903.57|1996-01-08|3-MEDIUM|Clerk#000000785|0|he furiously even deposits! ironic theodolites haggle blithely. r| +4710|100|F|88966.68|1994-12-08|4-NOT SPECIFIED|Clerk#000000734|0|the final, regular foxes. carefully ironic pattern| +4711|142|O|129546.56|1998-05-06|1-URGENT|Clerk#000000818|0|mptotes. unusual packages wake furiously qui| +4736|139|O|67572.73|1995-11-20|2-HIGH|Clerk#000000563|0|blithely regular courts affix into the carefully ironic deposits. slyly exp| +4737|79|F|62014.51|1993-03-11|4-NOT SPECIFIED|Clerk#000000275|0|ents use slyly among the unusual, ironic pearls. furiously pending | +4738|5|F|149466.62|1992-04-08|2-HIGH|Clerk#000000150|0|deposits. thin acco| +4739|148|F|68255.82|1993-02-21|5-LOW|Clerk#000000872|0|ing to the pending attainments: pending, express account| +4740|68|O|42579.40|1996-07-05|2-HIGH|Clerk#000000420|0| dependencies haggle about the| +4741|127|F|180692.90|1992-07-07|4-NOT SPECIFIED|Clerk#000000983|0|ly bold deposits are slyly about the r| +4742|64|P|155356.80|1995-03-23|3-MEDIUM|Clerk#000000058|0|n packages. quickly regular ideas cajole blithely| +4743|97|F|65702.39|1993-03-31|5-LOW|Clerk#000000048|0|pinto beans above the bold, even idea| +4768|136|F|4820.55|1993-11-22|2-HIGH|Clerk#000000875|0|ctions snooze idly beneath the quick waters. fluffily u| +4769|121|P|136765.03|1995-04-14|4-NOT SPECIFIED|Clerk#000000116|0|pon the asymptotes. idle, final account| +4770|59|O|72150.68|1995-06-20|2-HIGH|Clerk#000000461|0|cial instructions believe carefully. | +4771|95|F|49625.21|1992-12-14|1-URGENT|Clerk#000000571|0|lly express deposits serve furiously along the f| +4772|28|F|64102.93|1994-09-14|1-URGENT|Clerk#000000708|0|es sleep. regular requests haggle furiously slyly | +4773|122|O|196080.26|1995-12-23|1-URGENT|Clerk#000000327|0|ptotes was slyly along the| +4774|52|F|124380.73|1993-04-20|3-MEDIUM|Clerk#000000299|0|eposits use blithely bold deposits. carefully regular gifts about the fin| +4775|128|O|112444.42|1995-08-13|4-NOT SPECIFIED|Clerk#000000609|0|s integrate slyly slyly final instructions. carefully bold pack| +4800|37|F|91795.13|1992-01-06|5-LOW|Clerk#000000625|0|ggle furiously along the pending pinto beans. deposits use: final foxe| +4801|88|O|108353.08|1996-01-25|1-URGENT|Clerk#000000553|0|r the final sentiments. pending theodolites sleep doggedly across t| +4802|130|O|5978.65|1997-01-23|3-MEDIUM|Clerk#000000400|0| ironic, thin packages wake furiously ironic, ironic deposits. the| +4803|124|O|158776.68|1996-02-08|5-LOW|Clerk#000000892|0|lly unusual courts are ironic| +4804|37|F|111547.31|1992-01-28|2-HIGH|Clerk#000000614|0|ly final accounts. blithely unusual theodolite| +4805|16|F|172102.96|1992-04-25|4-NOT SPECIFIED|Clerk#000000514|0|even accounts wake furiously slyly final accounts; blithel| +4806|7|F|35390.15|1993-04-21|5-LOW|Clerk#000000625|0|ave accounts. furiously pending wa| +4807|53|O|138902.23|1997-01-09|3-MEDIUM|Clerk#000000310|0|kly. slyly special accounts| +4832|34|O|84954.79|1997-12-04|3-MEDIUM|Clerk#000000548|0|final accounts sleep among the blithe| +4833|133|O|84800.44|1996-05-12|3-MEDIUM|Clerk#000000256|0|r deposits against the slyly final excuses slee| +4834|19|O|124539.00|1996-09-12|2-HIGH|Clerk#000000284|0|lar accounts. furiously ironic accounts haggle slyly | +4835|146|F|70857.51|1994-10-25|1-URGENT|Clerk#000000250|0|s integrate furiously blithely expr| +4836|65|O|78711.40|1996-12-18|1-URGENT|Clerk#000000691|0|c packages cajole carefully through the accounts. careful| +4837|130|O|68519.84|1998-04-24|4-NOT SPECIFIED|Clerk#000000517|0|n accounts are regular, bold accounts. even instructions use request| +4838|44|F|61811.33|1992-08-02|1-URGENT|Clerk#000000569|0|ffily bold sentiments. carefully close dolphins cajole across the | +4839|25|F|71241.63|1994-05-10|1-URGENT|Clerk#000000925|0| even somas. slyly express ideas lose carefully. blithely unusu| +4864|88|F|149614.34|1992-11-11|5-LOW|Clerk#000000423|0|ests nag within the quickly ironic asymptotes. ironic| +4865|85|O|162113.46|1997-06-07|3-MEDIUM|Clerk#000000418|0|sits boost stealthily above the bl| +4866|53|O|25767.07|1997-08-07|2-HIGH|Clerk#000000663|0|kages. unusual packages nag fluffily. qui| +4867|10|F|9741.03|1992-05-21|1-URGENT|Clerk#000000891|0|ss the slyly regular dependencies. fluffily regular deposits within the car| +4868|76|O|159005.35|1997-03-02|5-LOW|Clerk#000000729|0|regular asymptotes. regular packages sublate carefully al| +4869|58|F|175422.13|1994-09-26|5-LOW|Clerk#000000802|0|boost! ironic packages un| +4870|103|F|94534.07|1994-08-06|3-MEDIUM|Clerk#000000911|0|nto beans about the blithely regular d| +4871|46|O|129636.99|1995-06-12|1-URGENT|Clerk#000000531|0|ven, special instructions across t| +4896|85|F|93206.35|1992-08-22|1-URGENT|Clerk#000000622|0|sly pending deposits. final accounts boost above the sly, even| +4897|80|F|115688.85|1992-09-17|5-LOW|Clerk#000000184|0|s. bold pinto beans sleep. evenly final accounts daz| +4898|14|F|40572.64|1994-07-11|4-NOT SPECIFIED|Clerk#000000841|0|final patterns. special theodolites haggle ruthlessly at the blithely spec| +4899|61|F|12291.83|1993-10-18|4-NOT SPECIFIED|Clerk#000000348|0| instructions. furiously even packages are furiously speci| +4900|137|F|221320.76|1992-06-30|4-NOT SPECIFIED|Clerk#000000878|0|sleep quickly unusual | +4901|79|O|146298.28|1997-12-31|4-NOT SPECIFIED|Clerk#000000980|0|inal dependencies cajole furiously. carefully express accounts na| +4902|139|O|26011.20|1998-07-04|3-MEDIUM|Clerk#000000874|0| the slyly express dolphins. | +4903|92|F|34363.63|1992-03-22|4-NOT SPECIFIED|Clerk#000000907|0|yly. multipliers within the fo| +4928|4|F|59931.42|1993-10-04|4-NOT SPECIFIED|Clerk#000000952|0|slyly brave instructions after the ironic excuses haggle ruthlessly about| +4929|149|O|135187.33|1996-02-29|3-MEDIUM|Clerk#000000109|0|uests. furiously special ideas poach. pending | +4930|149|F|176867.34|1994-05-06|5-LOW|Clerk#000000593|0| haggle slyly quietly final theodolites. packages are furious| +4931|50|F|115759.13|1994-11-17|1-URGENT|Clerk#000000356|0|leep. slyly express dolphins nag slyly. furiously regular s| +4932|122|F|42927.07|1993-08-10|1-URGENT|Clerk#000000830|0|onic foxes. enticingly reg| +4933|94|O|42945.82|1995-07-14|3-MEDIUM|Clerk#000000848|0|y special sauternes integr| +4934|40|O|180478.16|1997-02-17|1-URGENT|Clerk#000000372|0|nes cajole; carefully special accounts haggle. special pinto beans nag | +4935|40|F|162088.30|1993-05-25|4-NOT SPECIFIED|Clerk#000000601|0|c foxes. fluffily pendin| +4960|124|F|153259.41|1995-02-26|5-LOW|Clerk#000000229|0|uriously even excuses. fluffily regular instructions along the furiously ironi| +4961|58|O|89224.24|1998-04-06|3-MEDIUM|Clerk#000000731|0| braids. furiously even theodolites | +4962|104|F|44781.32|1993-07-28|3-MEDIUM|Clerk#000000008|0| breach never ironic | +4963|34|O|54175.35|1996-11-07|3-MEDIUM|Clerk#000000754|0|ully unusual epitaphs nod s| +4964|101|O|204163.10|1997-07-28|4-NOT SPECIFIED|Clerk#000000144|0|ithely final theodolites. blithely regu| +4965|52|F|110626.82|1993-10-21|5-LOW|Clerk#000000638|0|dependencies poach packages. sometim| +4966|70|O|59186.02|1996-09-07|2-HIGH|Clerk#000000243|0|accounts. blithely ironic courts wake boldly furiously express | +4967|98|O|103814.27|1997-02-17|3-MEDIUM|Clerk#000000397|0|e theodolites; furiously b| +4992|62|F|203904.80|1992-05-10|1-URGENT|Clerk#000000166|0|telets nag carefully am| +4993|13|F|145730.19|1994-08-04|4-NOT SPECIFIED|Clerk#000000258|0|ing instructions nag furiously. un| +4994|43|O|216071.76|1996-06-29|4-NOT SPECIFIED|Clerk#000000868|0|oxes wake above the asymptotes. bold requests sleep br| +4995|40|O|189651.76|1996-01-06|4-NOT SPECIFIED|Clerk#000000748|0|s. even deposits boost along the express, even theodolites. stealthily ir| +4996|133|F|100750.67|1992-09-14|3-MEDIUM|Clerk#000000433|0|foxes. carefully special packages haggle quickly fluffi| +4997|47|O|122611.05|1998-03-18|5-LOW|Clerk#000000040|0|egrate final pinto beans. fluffily special notornis use blith| +4998|32|F|129096.80|1992-01-11|4-NOT SPECIFIED|Clerk#000000054|0|alongside of the quickly final requests hang always| +4999|85|F|98643.17|1993-06-26|2-HIGH|Clerk#000000504|0| dolphins cajole blithely above the sly | +5024|124|O|116127.69|1996-10-25|3-MEDIUM|Clerk#000000659|0|r foxes. regular excuses are about the quickly regular theodolites. regular, | +5025|121|O|20099.43|1997-02-03|5-LOW|Clerk#000000805|0|ackages are slyly about the quickly | +5026|28|O|13197.78|1997-09-06|1-URGENT|Clerk#000000955|0|y final requests us| +5027|148|O|181346.56|1997-08-30|2-HIGH|Clerk#000000751|0|e-- final, pending requests along t| +5028|13|F|30755.69|1992-04-17|2-HIGH|Clerk#000000180|0|ickly blithely express deposits. b| +5029|11|F|19811.69|1992-11-14|3-MEDIUM|Clerk#000000469|0|. regular accounts haggle slyly. regul| +5030|106|O|71781.23|1998-05-25|4-NOT SPECIFIED|Clerk#000000564|0| wake slyly furiously thin requests. ironic pinto beans ha| +5031|139|F|91438.59|1994-12-02|3-MEDIUM|Clerk#000000788|0|lar instructions haggle blithely pending foxes? sometimes final excuses h| +5056|52|O|62258.18|1997-02-15|5-LOW|Clerk#000000828|0|lithely above the express ideas. blithely final deposits are fluffily spec| +5057|64|O|76164.41|1997-08-03|1-URGENT|Clerk#000000955|0|r ironic requests of the carefully ironic dependencies wake slyly a| +5058|119|O|17031.01|1998-03-23|1-URGENT|Clerk#000000367|0| the pending packages wake after the quickly speci| +5059|43|F|67173.82|1993-11-10|2-HIGH|Clerk#000000058|0|latelets. final, regular accounts cajole furiously ironic pinto beans? do| +5060|112|F|65218.47|1992-07-07|4-NOT SPECIFIED|Clerk#000000333|0|e according to the excuses. express theodo| +5061|101|F|52190.52|1993-08-14|1-URGENT|Clerk#000000009|0|e packages use fluffily according to the carefully ironic deposits. bol| +5062|61|F|109247.00|1992-10-08|3-MEDIUM|Clerk#000000012|0|ithely. blithely bold theodolites affix. blithely final deposits haggle ac| +5063|23|O|98753.57|1997-05-17|2-HIGH|Clerk#000000745|0|lyly after the pending foxes. express theodolites breach across t| +5088|130|F|101616.44|1993-01-06|5-LOW|Clerk#000000930|0|ole slyly since the quickly ironic br| +5089|130|F|109246.54|1992-07-29|1-URGENT|Clerk#000000677|0|cial platelets. quiet, final ideas cajole carefully. unusu| +5090|89|O|132838.49|1997-03-09|1-URGENT|Clerk#000000953|0|ress accounts affix silently carefully quick accounts. carefully f| +5091|148|O|47852.06|1998-05-21|3-MEDIUM|Clerk#000000311|0|egular decoys mold carefully fluffily unus| +5092|22|O|195834.96|1995-10-30|5-LOW|Clerk#000000194|0|are blithely along the pin| +5093|79|F|190693.92|1993-09-03|3-MEDIUM|Clerk#000000802|0|ully ironic theodolites sleep above the furiously ruthless instructions. bli| +5094|106|F|74892.08|1993-03-29|4-NOT SPECIFIED|Clerk#000000406|0|uickly pending deposits haggle quickly ide| +5095|97|F|184583.99|1992-04-22|2-HIGH|Clerk#000000964|0|accounts are carefully! slyly even packages wake slyly a| +5120|16|O|28007.73|1996-06-05|1-URGENT|Clerk#000000332|0|against the slyly express requests. furiousl| +5121|133|F|150334.57|1992-05-11|4-NOT SPECIFIED|Clerk#000000736|0|gular requests. furiously final pearls against the permanent, thin courts s| +5122|70|O|79863.84|1996-02-10|5-LOW|Clerk#000000780|0|blithely. slyly ironic deposits nag. excuses s| +5123|10|O|11850.45|1998-02-10|1-URGENT|Clerk#000000776|0|ic requests. furiously ironic packages grow above the express, ironic inst| +5124|25|O|159170.80|1997-04-04|4-NOT SPECIFIED|Clerk#000000749|0|kly even courts. bold packages solve. | +5125|28|O|38065.28|1998-02-07|5-LOW|Clerk#000000834|0|ructions. dolphins wake slowly carefully unusual | +5126|112|F|92123.32|1992-10-12|4-NOT SPECIFIED|Clerk#000000270|0|s. unusual deposits | +5127|73|O|48024.99|1997-01-15|5-LOW|Clerk#000000829|0|fully express pinto beans. slyly final accounts along the ironic dugouts use s| +5152|44|O|60568.34|1997-01-04|3-MEDIUM|Clerk#000000963|0| for the blithely reg| +5153|113|O|193832.28|1995-08-26|1-URGENT|Clerk#000000954|0| the furiously ironic foxes. express packages shall cajole carefully across| +5154|8|O|28070.86|1997-04-13|3-MEDIUM|Clerk#000000316|0|inal requests. slyly regular deposits nag. even deposits haggle agains| +5155|77|F|70183.29|1994-06-12|2-HIGH|Clerk#000000108|0|y pending deposits are ag| +5156|125|O|59439.44|1996-11-04|5-LOW|Clerk#000000117|0|ngside of the multipliers solve slyly requests. regu| +5157|142|O|167056.34|1997-07-06|4-NOT SPECIFIED|Clerk#000000689|0|closely above the unusual deposits. furiously| +5158|76|O|240284.95|1997-01-21|1-URGENT|Clerk#000000541|0| regular foxes. even foxes wake blithely | +5159|106|O|147543.26|1996-09-25|1-URGENT|Clerk#000000303|0|tegrate slyly around the slyly sly sauternes. final pa| +5184|85|O|209155.48|1998-07-20|5-LOW|Clerk#000000250|0|nding accounts detect final, even| +5185|148|O|206179.68|1997-07-25|3-MEDIUM|Clerk#000000195|0| regular ideas about the even ex| +5186|52|O|208892.63|1996-08-03|1-URGENT|Clerk#000000332|0|pecial platelets. slyly final ac| +5187|55|O|46380.69|1997-07-16|3-MEDIUM|Clerk#000000682|0|ckly according to t| +5188|140|P|66268.86|1995-03-02|4-NOT SPECIFIED|Clerk#000000029|0|counts. finally ironic requests ab| +5189|71|F|184172.31|1993-11-26|5-LOW|Clerk#000000940|0|e after the pending accounts. asymptotes boost. re| +5190|58|F|89684.31|1992-04-26|5-LOW|Clerk#000000888|0|equests. slyly unusual| +5191|77|F|119910.04|1994-12-11|4-NOT SPECIFIED|Clerk#000000318|0|ing, regular deposits alongside of the deposits boost fluffily quickly ev| +5216|59|O|16763.95|1997-08-14|3-MEDIUM|Clerk#000000418|0|des boost across the platelets. slyly busy theodolit| +5217|35|O|135745.58|1995-10-13|2-HIGH|Clerk#000000873|0|ons might wake quickly according to th| +5218|82|F|73882.37|1992-07-30|4-NOT SPECIFIED|Clerk#000000683|0|y ruthless packages according to the bold, ironic package| +5219|88|O|21267.72|1997-02-27|1-URGENT|Clerk#000000510|0|aggle always. foxes above the ironic deposits | +5220|10|F|24844.39|1992-07-30|2-HIGH|Clerk#000000051|0| final packages. ideas detect slyly around| +5221|13|O|71968.10|1995-06-09|4-NOT SPECIFIED|Clerk#000000324|0|lar accounts above the sl| +5222|80|F|1051.15|1994-05-27|4-NOT SPECIFIED|Clerk#000000613|0|along the bold ideas. furiously final foxes snoo| +5223|149|F|105561.21|1994-06-30|1-URGENT|Clerk#000000745|0|e. theodolites serve blithely unusual, final foxes. carefully pending packag| +5248|70|P|86958.28|1995-04-15|2-HIGH|Clerk#000000737|0|theodolites cajole according to the silent packages. quickly ironic packages a| +5249|103|F|123586.03|1994-09-06|3-MEDIUM|Clerk#000000019|0|refully bold accounts | +5250|97|O|29673.73|1995-07-16|2-HIGH|Clerk#000000307|0|. carefully final instructions sleep among the finally regular dependen| +5251|34|O|34004.48|1995-04-12|3-MEDIUM|Clerk#000000687|0| ironic dugouts detect. reque| +5252|91|O|173145.37|1996-02-17|1-URGENT|Clerk#000000724|0| ironic accounts among the silent asym| +5253|148|P|108361.46|1995-04-11|2-HIGH|Clerk#000000275|0|egular requests! blithely regular deposits alongside of t| +5254|112|F|196989.09|1992-07-26|4-NOT SPECIFIED|Clerk#000000527|0|he express, even ideas cajole blithely special requests| +5255|64|O|75074.07|1996-07-12|5-LOW|Clerk#000000591|0|ly slow forges. express foxes haggle. regular, even asymp| +5280|34|O|68052.70|1997-12-03|3-MEDIUM|Clerk#000000604|0|riously ironic instructions. ironic ideas according to the accounts boost fur| +5281|124|O|179418.31|1995-11-02|2-HIGH|Clerk#000000158|0|ackages haggle slyly a| +5282|50|O|94446.69|1998-01-30|1-URGENT|Clerk#000000030|0|rding to the unusual, bold accounts. regular instructions| +5283|131|F|18594.66|1994-06-04|3-MEDIUM|Clerk#000000579|0|ests. even, final ideas alongside of t| +5284|61|O|40548.99|1995-07-09|4-NOT SPECIFIED|Clerk#000000155|0| careful dependencies use sly| +5285|70|F|99377.51|1994-01-18|2-HIGH|Clerk#000000976|0|p across the furiously ironic deposits.| +5286|116|O|79646.89|1997-09-26|5-LOW|Clerk#000000606|0|structions are furiously quickly ironic asymptotes. quickly iro| +5287|25|F|30045.95|1993-12-22|5-LOW|Clerk#000000406|0|regular packages. bold instructions sleep always. carefully final p| +5312|65|F|66697.95|1995-02-24|2-HIGH|Clerk#000000690|0|ter the even, bold foxe| +5313|13|O|159870.44|1997-06-17|4-NOT SPECIFIED|Clerk#000000896|0|le. final courts haggle furiously according to the | +5314|34|O|26999.83|1995-06-02|2-HIGH|Clerk#000000617|0|ions across the quickly special d| +5315|139|F|55554.97|1992-10-29|4-NOT SPECIFIED|Clerk#000000035|0| furiously. quickly unusual packages use. sly| +5316|100|F|62316.61|1994-01-31|1-URGENT|Clerk#000000734|0| requests haggle across the regular, pending deposits. furiously regular requ| +5317|37|F|228002.51|1994-09-09|5-LOW|Clerk#000000687|0|jole quickly at the slyly pend| +5318|59|F|106935.19|1993-04-04|2-HIGH|Clerk#000000663|0|efully regular dolphins. even ideas nag fluffily furiously even packa| +5319|98|O|68619.29|1996-01-21|1-URGENT|Clerk#000000237|0|lent requests. quickly pe| +5344|109|O|88216.32|1998-06-21|3-MEDIUM|Clerk#000000569|0|s. ironic excuses cajole across the| +5345|31|O|111924.56|1997-08-24|1-URGENT|Clerk#000000057|0|r the slyly silent packages. pending, even pinto b| +5346|37|F|149536.20|1993-12-26|2-HIGH|Clerk#000000220|0|gly close packages against the even, regular escapades boost evenly accordi| +5347|49|F|173024.71|1995-02-22|3-MEDIUM|Clerk#000000180|0|onic, regular deposits. packag| +5348|53|O|119164.96|1997-11-08|5-LOW|Clerk#000000497|0|totes. accounts after the furiously| +5349|67|O|38038.84|1996-09-01|1-URGENT|Clerk#000000960|0|le along the carefully bold dolphins. carefully special packa| +5350|76|F|113417.03|1993-10-10|5-LOW|Clerk#000000604|0|ccounts after the carefully pending requests believe | +5351|122|O|76799.25|1998-05-11|1-URGENT|Clerk#000000443|0|to beans sleep furiously after the carefully even| +5376|149|F|98422.83|1994-07-04|5-LOW|Clerk#000000392|0|. quickly ironic deposits integrate along| +5377|64|O|117728.37|1997-04-24|2-HIGH|Clerk#000000917|0|ons nag blithely furiously regula| +5378|43|F|101899.93|1992-10-25|1-URGENT|Clerk#000000520|0|n ideas. regular accounts haggle. ironic ideas use along the bold ideas. blith| +5379|89|O|47010.15|1995-08-08|2-HIGH|Clerk#000000503|0|he unusual accounts. carefully special instructi| +5380|148|O|123014.83|1997-10-12|1-URGENT|Clerk#000000481|0|le slyly about the slyly final dolphins. fu| +5381|32|F|223995.46|1993-01-29|5-LOW|Clerk#000000531|0|arefully bold packages are slyly furiously ironic foxes. fluffil| +5382|35|F|138423.03|1992-01-13|5-LOW|Clerk#000000809|0|lent deposits are according to the reg| +5383|31|O|11474.95|1995-05-26|5-LOW|Clerk#000000409|0|ly bold requests hang furiously furiously unusual accounts. evenly unusu| +5408|23|F|123477.05|1992-07-21|5-LOW|Clerk#000000735|0|egular requests according to the| +5409|13|F|145040.38|1992-01-09|5-LOW|Clerk#000000171|0|eans. regular accounts are regul| +5410|22|O|139104.17|1998-07-28|4-NOT SPECIFIED|Clerk#000000117|0|final deposits: pending excuses boost. ironic theodolites cajole furi| +5411|61|O|62541.27|1997-05-16|3-MEDIUM|Clerk#000000800|0|equests cajole slyly furious| +5412|142|O|109979.71|1998-01-20|2-HIGH|Clerk#000000151|0|ets boost furiously regular accounts. regular foxes above th| +5413|94|O|224382.57|1997-10-17|1-URGENT|Clerk#000000066|0|e even excuses. always final depen| +5414|100|F|167017.39|1993-03-25|4-NOT SPECIFIED|Clerk#000000242|0|lent dependencies? carefully express requests sleep furiously ac| +5415|23|F|176864.83|1992-08-05|3-MEDIUM|Clerk#000000998|0|ly even ideas nag blithely above the final instructions| +5440|130|O|3223.17|1997-01-12|1-URGENT|Clerk#000000154|0|posits boost regularly ironic packages. regular, ironic deposits wak| +5441|41|F|131891.05|1994-07-21|4-NOT SPECIFIED|Clerk#000000257|0|after the furiously ironic | +5442|43|O|139332.94|1998-01-13|4-NOT SPECIFIED|Clerk#000000954|0|ully. quickly express accounts against the| +5443|131|O|124950.79|1996-10-10|4-NOT SPECIFIED|Clerk#000000492|0|al foxes could detect. blithely stealthy asymptotes kind| +5444|130|P|172908.01|1995-03-18|1-URGENT|Clerk#000000677|0| asymptotes. asymptotes cajole quickly quickly bo| +5445|115|F|114990.63|1993-07-26|5-LOW|Clerk#000000623|0|s. even, special requests cajole furiously even, | +5446|7|F|29920.80|1994-06-21|5-LOW|Clerk#000000304|0| furiously final pac| +5447|13|O|29029.84|1996-03-16|3-MEDIUM|Clerk#000000597|0|uternes around the furiously bold accounts wake after | +5472|70|F|221636.83|1993-04-11|5-LOW|Clerk#000000552|0|counts. deposits about the slyly dogged pinto beans cajole slyly| +5473|65|F|63041.33|1992-03-25|4-NOT SPECIFIED|Clerk#000000306|0|te the quickly stealthy ideas. even, regular deposits above| +5474|55|F|131079.52|1992-06-01|4-NOT SPECIFIED|Clerk#000000487|0|gle blithely enticing ideas. final, exp| +5475|139|O|10645.48|1996-07-07|5-LOW|Clerk#000000856|0|es shall boost slyly. furiously even deposits lose. instruc| +5476|91|O|26906.38|1997-11-06|1-URGENT|Clerk#000000189|0|furiously final ideas. furiously bold dependencies sleep care| +5477|107|O|130125.64|1997-12-30|5-LOW|Clerk#000000689|0|ckages. ironic deposits caj| +5478|116|O|97502.23|1996-05-17|1-URGENT|Clerk#000000272|0|ckages. quickly pending deposits thrash furiously: bl| +5479|70|F|70553.45|1993-12-22|3-MEDIUM|Clerk#000000335|0|ng asymptotes. pinto beans sleep care| +5504|19|F|41492.25|1993-01-06|2-HIGH|Clerk#000000221|0|y pending packages. furiousl| +5505|95|O|147329.51|1997-10-04|5-LOW|Clerk#000000719|0| final, regular packages according to the slyly ironic accounts nag ironica| +5506|91|F|8413.31|1993-11-08|1-URGENT|Clerk#000000292|0|nusual theodolites. sly| +5507|2|O|140363.70|1998-05-28|5-LOW|Clerk#000000692|0|the carefully ironic instructions are quickly iro| +5508|56|O|3808.05|1996-06-21|1-URGENT|Clerk#000000128|0|y express packages cajole furiously. slyly unusual requests | +5509|80|F|135335.96|1994-04-08|5-LOW|Clerk#000000164|0|usual deposits use packages. furiously final requests wake slyly about th| +5510|37|F|126948.81|1993-01-08|3-MEDIUM|Clerk#000000819|0| nag slyly. carefully eve| +5511|79|F|151089.96|1994-11-29|1-URGENT|Clerk#000000438|0|ng instructions integrate fluffily among the fluffily silent accounts. bli| +5536|116|O|108196.56|1998-03-16|4-NOT SPECIFIED|Clerk#000000076|0| carefully final dolphins. ironic, ironic deposits lose. bold, | +5537|118|O|102207.20|1996-10-03|3-MEDIUM|Clerk#000000742|0|ng to the daring, final | +5538|139|F|90981.28|1993-12-25|1-URGENT|Clerk#000000992|0|ttainments. slyly final ideas are about the furiously silent excuses.| +5539|119|F|39397.60|1994-07-31|5-LOW|Clerk#000000675|0|structions. slyly regular patterns solve above the carefully expres| +5540|130|O|90707.58|1996-10-12|4-NOT SPECIFIED|Clerk#000000120|0|y ironic packages cajole blithely| +5541|143|O|37526.68|1997-09-30|3-MEDIUM|Clerk#000000217|0|encies among the silent accounts sleep slyly quickly pending deposits| +5542|49|O|6402.41|1996-04-20|4-NOT SPECIFIED|Clerk#000000100|0|riously among the regularly regular pac| +5543|115|F|118201.53|1993-09-25|3-MEDIUM|Clerk#000000644|0|ckly regular epitaphs. carefully bold accounts haggle furiously| +5568|31|O|105421.09|1995-06-07|3-MEDIUM|Clerk#000000491|0| nag. fluffily pending de| +5569|109|F|126113.32|1993-04-30|4-NOT SPECIFIED|Clerk#000000759|0|e regular dependencies. furiously unusual ideas b| +5570|112|O|78567.55|1996-08-12|2-HIGH|Clerk#000000795|0|eans. ironic, even requests doze | +5571|103|F|79248.35|1992-12-19|4-NOT SPECIFIED|Clerk#000000184|0|ts cajole furiously carefully regular sheaves. un| +5572|8|F|182966.39|1994-07-17|2-HIGH|Clerk#000000163|0|e fluffily express deposits cajole slyly across th| +5573|37|O|158479.37|1996-08-15|3-MEDIUM|Clerk#000000055|0|lites. slyly final pinto beans about the carefully regul| +5574|28|F|129803.03|1992-03-10|4-NOT SPECIFIED|Clerk#000000002|0|n deposits. special, regular t| +5575|103|O|51839.94|1995-07-24|5-LOW|Clerk#000000948|0|uriously express frays breach| +5600|95|O|53649.35|1997-02-08|4-NOT SPECIFIED|Clerk#000000019|0|lly regular deposits. car| +5601|11|F|118570.79|1992-01-06|2-HIGH|Clerk#000000827|0|gular deposits wake platelets? blithe| +5602|130|O|67979.49|1997-07-30|3-MEDIUM|Clerk#000000395|0|onic asymptotes haggl| +5603|71|F|145100.47|1992-06-20|4-NOT SPECIFIED|Clerk#000000535|0| asymptotes. fluffily ironic instructions are. pending pinto bean| +5604|46|O|98987.51|1998-04-14|4-NOT SPECIFIED|Clerk#000000123|0|ously across the blithely ironic pinto beans. sile| +5605|35|O|172899.84|1996-08-22|2-HIGH|Clerk#000000538|0|sleep carefully final packages. dependencies wake slyly. theodol| +5606|149|O|219959.08|1996-11-12|5-LOW|Clerk#000000688|0|uriously express pinto beans. packages sh| +5607|92|F|24660.06|1992-01-01|4-NOT SPECIFIED|Clerk#000000137|0|c requests promise quickly fluffily ironic deposits. caref| +5632|79|O|89503.11|1996-02-05|1-URGENT|Clerk#000000508|0|ons. blithely pending pinto beans thrash. furiously busy theodoli| +5633|79|O|207119.83|1998-05-31|3-MEDIUM|Clerk#000000841|0|cial deposits wake final, final| +5634|68|O|99494.67|1996-07-31|3-MEDIUM|Clerk#000000915|0|out the accounts. carefully ironic ideas are slyly. sheaves could h| +5635|70|F|192217.86|1992-08-16|3-MEDIUM|Clerk#000000734|0|nal platelets sleep daringly. idle, final accounts about | +5636|122|F|143350.75|1995-02-16|3-MEDIUM|Clerk#000000916|0|. boldly even Tiresias sleep. blithely ironic packages among the ca| +5637|103|O|128776.90|1996-06-17|3-MEDIUM|Clerk#000000183|0|nic dolphins are regular packages. ironic pinto beans hagg| +5638|109|F|79197.77|1994-01-17|1-URGENT|Clerk#000000355|0|enly bold deposits eat. special realms play against the regular, speci| +5639|145|F|9669.46|1994-06-02|3-MEDIUM|Clerk#000000005|0|ending packages use after the blithely regular accounts. regular package| +5664|119|O|186215.81|1998-07-23|2-HIGH|Clerk#000000789|0|the quickly ironic dolp| +5665|100|F|129821.09|1993-06-28|4-NOT SPECIFIED|Clerk#000000513|0| carefully special instructions. ironic pinto beans nag slyly blithe| +5666|14|F|121663.68|1994-02-02|2-HIGH|Clerk#000000396|0|mptotes. quickly final instructions are | +5667|44|O|37301.25|1995-08-10|1-URGENT|Clerk#000000358|0|s print upon the quickly ironic packa| +5668|109|F|13679.32|1995-03-22|4-NOT SPECIFIED|Clerk#000000047|0|p slyly slyly express accoun| +5669|74|O|113156.30|1996-05-06|1-URGENT|Clerk#000000336|0|ng packages nag fluffily furio| +5670|7|F|101429.61|1993-04-21|5-LOW|Clerk#000000922|0|he carefully final packages. deposits are slyly among the requests. | +5671|43|O|176647.54|1998-02-06|2-HIGH|Clerk#000000838|0|k dependencies. slyly | +5696|142|P|198723.30|1995-05-04|1-URGENT|Clerk#000000447|0|e quickly unusual pack| +5697|55|F|99177.69|1992-10-05|1-URGENT|Clerk#000000112|0|pendencies impress furiously. bold, final requests solve ab| +5698|95|F|154936.43|1994-05-21|3-MEDIUM|Clerk#000000455|0|he furiously silent accounts haggle blithely against the carefully unusual| +5699|142|F|226314.91|1992-07-30|5-LOW|Clerk#000000311|0|o beans. ironic asymptotes boost. blithe, final courts integrate| +5700|143|O|79901.18|1997-12-25|1-URGENT|Clerk#000000618|0|ly pending dolphins sleep carefully slyly pending i| +5701|43|O|16689.19|1997-02-07|5-LOW|Clerk#000000798|0| blithely final pinto beans. blit| +5702|97|F|153024.28|1993-09-07|4-NOT SPECIFIED|Clerk#000000743|0|ironic accounts. final accounts wake express deposits. final pac| +5703|121|F|1816.28|1993-05-16|3-MEDIUM|Clerk#000000647|0|ly special instructions. slyly even reque| +5728|80|F|85397.04|1994-12-11|4-NOT SPECIFIED|Clerk#000000426|0|furiously express pin| +5729|44|F|88080.33|1994-10-10|2-HIGH|Clerk#000000843|0|uffily sly accounts about| +5730|11|O|10934.84|1997-12-18|1-URGENT|Clerk#000000181|0|l platelets. ironic pinto beans wake slyly. quickly b| +5731|8|O|57823.37|1997-05-17|5-LOW|Clerk#000000841|0| silent excuses among the express accounts wake | +5732|37|O|28330.42|1997-08-03|1-URGENT|Clerk#000000910|0|he quickly bold asymptotes: final platelets wake quickly. blithely final pinto| +5733|101|F|38545.97|1993-03-17|2-HIGH|Clerk#000000873|0|osits. pending accounts boost quickly. furiously permanent acco| +5734|94|O|45860.94|1997-10-12|3-MEDIUM|Clerk#000000084|0|efully even braids detect blithely alo| +5735|40|F|39358.51|1994-12-11|3-MEDIUM|Clerk#000000600|0| bold realms cajole slyly fu| +5760|25|F|59404.77|1994-05-25|4-NOT SPECIFIED|Clerk#000000498|0|s among the blithely regular frays haggle ironically bold theodolites. al| +5761|16|O|130345.90|1998-07-06|3-MEDIUM|Clerk#000000208|0|s asymptotes cajole boldly. regular, | +5762|49|O|165019.32|1997-02-14|1-URGENT|Clerk#000000901|0|ly bold packages: slyly ironic deposits sleep quietly foxes. express a| +5763|8|O|140838.11|1998-06-26|4-NOT SPECIFIED|Clerk#000000633|0|according to the furiously regular pinto beans. even accounts wake fu| +5764|131|F|53212.95|1993-10-03|4-NOT SPECIFIED|Clerk#000000363|0| furiously regular deposits haggle fluffily around th| +5765|52|F|249900.42|1994-12-15|5-LOW|Clerk#000000959|0|longside of the quickly final packages. instructions so| +5766|49|F|47940.51|1993-09-27|5-LOW|Clerk#000000753|0|. quickly final packages print slyly. fu| +5767|118|F|135643.87|1992-04-29|2-HIGH|Clerk#000000225|0|ts wake fluffily above the r| +5792|26|F|158991.89|1993-04-04|2-HIGH|Clerk#000000731|0|packages. doggedly bold deposits integrate furiously across the| +5793|37|O|119887.47|1997-07-13|2-HIGH|Clerk#000000294|0|thely. fluffily even instructi| +5794|8|F|122823.78|1993-04-05|5-LOW|Clerk#000000855|0|t accounts kindle about the gifts. as| +5795|37|F|35514.45|1992-05-05|2-HIGH|Clerk#000000581|0| even instructions x-ray ironic req| +5796|149|O|23280.61|1996-01-23|3-MEDIUM|Clerk#000000326|0|eodolites. slyly ironic pinto beans at the silent, special request| +5797|122|O|15313.61|1997-10-15|4-NOT SPECIFIED|Clerk#000000381|0|ng! packages against the blithely b| +5798|106|O|125011.92|1998-03-30|5-LOW|Clerk#000000343|0|lent accounts affix quickly! platelets run slyly slyly final packages. f| +5799|26|O|71381.21|1995-08-03|1-URGENT|Clerk#000000238|0| unusual deposits sleep blithely along the carefully even requests. care| +5824|56|O|169107.85|1996-12-03|2-HIGH|Clerk#000000171|0|unusual packages. even ideas along the even requests are along th| +5825|61|F|23020.62|1995-02-21|5-LOW|Clerk#000000494|0|regular packages use bravely.| +5826|22|O|21119.86|1998-06-13|1-URGENT|Clerk#000000087|0|even, regular dependenc| +5827|31|O|137297.71|1998-07-23|3-MEDIUM|Clerk#000000660|0|hely furiously blithe dolphins. slyly | +5828|127|F|62172.34|1994-03-06|5-LOW|Clerk#000000377|0|ages boost never during the final packa| +5829|125|O|183734.56|1997-01-11|1-URGENT|Clerk#000000196|0|gular accounts. bold accounts are blithely furiously ironic r| +5830|85|F|28223.57|1993-03-25|3-MEDIUM|Clerk#000000233|0|lites haggle. ironic, ironic instructions maintain blit| +5831|139|O|113505.19|1996-11-17|5-LOW|Clerk#000000585|0|s final, final pinto beans. unusual depos| +5856|37|F|71460.49|1994-11-06|2-HIGH|Clerk#000000634|0|special excuses. slyly final theodolites cajole blithely furiou| +5857|124|O|158345.31|1997-11-06|4-NOT SPECIFIED|Clerk#000000267|0|gage blithely. quickly special ac| +5858|64|F|181320.50|1992-07-14|4-NOT SPECIFIED|Clerk#000000580|0|lyly pending dugouts believe through the ironic deposits. silent s| +5859|5|O|210643.96|1997-04-23|1-URGENT|Clerk#000000993|0|requests boost. asymptotes across the deposits solve slyly furiously pendin| +5860|13|F|9495.28|1992-02-20|4-NOT SPECIFIED|Clerk#000000079|0| beans. bold, special foxes sleep about the ir| +5861|139|O|41450.19|1997-04-10|3-MEDIUM|Clerk#000000094|0|rthogs cajole slyly. express packages sleep blithely final | +5862|64|O|30550.90|1997-02-20|1-URGENT|Clerk#000000039|0|leep beneath the quickly busy excuses. ironic theodolit| +5863|65|F|67941.54|1993-11-22|3-MEDIUM|Clerk#000000774|0|ets about the slyly pending ideas sleep according to the blithely | +5888|46|O|67167.19|1996-09-28|3-MEDIUM|Clerk#000000748|0|quickly against the furiously final requests. evenly fi| +5889|22|O|15417.57|1995-05-23|5-LOW|Clerk#000000690|0|ites wake across the slyly ironic| +5890|49|F|41162.24|1992-11-04|2-HIGH|Clerk#000000013|0|packages. final, final reques| +5891|46|F|41760.00|1992-12-29|3-MEDIUM|Clerk#000000302|0|ounts haggle furiously abo| +5892|101|P|92340.77|1995-05-09|5-LOW|Clerk#000000639|0| pending instruction| +5893|2|F|44777.63|1992-07-08|4-NOT SPECIFIED|Clerk#000000560|0|final sentiments. instructions boost above the never speci| +5894|71|F|70377.31|1994-08-13|2-HIGH|Clerk#000000776|0|regular deposits wake| +5895|64|O|201419.83|1997-01-01|4-NOT SPECIFIED|Clerk#000000747|0| ironic, unusual requests cajole blithely special, special deposits. s| +5920|119|F|142767.26|1994-11-20|2-HIGH|Clerk#000000081|0|ns: even ideas cajole slyly among the packages. never ironic patterns| +5921|58|F|152940.00|1994-04-07|5-LOW|Clerk#000000125|0|kly special requests breach.| +5922|143|O|142494.99|1996-11-14|5-LOW|Clerk#000000625|0| ironic instructions haggle furiously blithely regular accounts: even platele| +5923|101|O|157968.27|1997-05-27|2-HIGH|Clerk#000000304|0|o beans haggle slyly above the regular, even dependencies| +5924|31|O|106823.97|1995-10-10|3-MEDIUM|Clerk#000000433|0|arefully after the pains. blithely ironic pinto | +5925|146|O|242588.87|1995-11-13|5-LOW|Clerk#000000602|0|ourts. boldly regular foxes might sleep. slyly express tithes against | +5926|76|F|105770.53|1994-05-20|5-LOW|Clerk#000000071|0| carefully after the furiously even re| +5927|116|O|84983.90|1997-08-28|4-NOT SPECIFIED|Clerk#000000972|0|endencies according to the slyly ironic foxes detect furiously about the furio| +5952|148|O|128624.99|1997-04-14|3-MEDIUM|Clerk#000000950|0| regular, final pla| +5953|7|F|95312.81|1992-03-28|1-URGENT|Clerk#000000049|0|ages are furiously. slowly bold requests| +5954|28|F|167262.34|1992-12-03|1-URGENT|Clerk#000000968|0|requests along the blith| +5955|94|P|67944.38|1995-03-27|5-LOW|Clerk#000000340|0|deas integrate. fluffily regular pa| +5956|22|O|118036.54|1998-05-18|1-URGENT|Clerk#000000587|0|le even, express platelets.| +5957|89|F|230949.45|1993-12-27|2-HIGH|Clerk#000000020|0| dependencies are slyly. bold accounts according to the carefully regular r| +5958|115|O|145060.41|1995-09-16|3-MEDIUM|Clerk#000000787|0|e final requests detect alongside of the qu| +5959|23|F|195515.26|1992-05-15|3-MEDIUM|Clerk#000000913|0|into beans use ironic, unusual foxes. carefully regular excuses boost caref| +5984|70|F|83413.30|1994-06-18|5-LOW|Clerk#000000023|0|ickly final pains haggle along the furiously ironic pinto bea| +5985|143|F|3942.73|1995-01-12|3-MEDIUM|Clerk#000000417|0|as nag fluffily slyly permanent accounts. regular depo| +5986|115|F|92187.80|1992-04-22|2-HIGH|Clerk#000000674|0|iously unusual notornis are | +5987|64|O|98956.82|1996-08-03|1-URGENT|Clerk#000000464|0| ideas. quietly final accounts haggle blithely pending escapade| +5988|31|F|41655.51|1993-11-22|4-NOT SPECIFIED|Clerk#000000867|0|fully express accounts. final pi| diff --git a/zetasql/examples/tpch/catalog/part.tbl b/zetasql/examples/tpch/catalog/part.tbl new file mode 100644 index 000000000..f8b7696dd --- /dev/null +++ b/zetasql/examples/tpch/catalog/part.tbl @@ -0,0 +1,200 @@ +1|goldenrod lavender spring chocolate lace|Manufacturer#1|Brand#13|PROMO BURNISHED COPPER|7|JUMBO PKG|901.00|ly. slyly ironi| +2|blush thistle blue yellow saddle|Manufacturer#1|Brand#13|LARGE BRUSHED BRASS|1|LG CASE|902.00|lar accounts amo| +3|spring green yellow purple cornsilk|Manufacturer#4|Brand#42|STANDARD POLISHED BRASS|21|WRAP CASE|903.00|egular deposits hag| +4|cornflower chocolate smoke green pink|Manufacturer#3|Brand#34|SMALL PLATED BRASS|14|MED DRUM|904.00|p furiously r| +5|forest brown coral puff cream|Manufacturer#3|Brand#32|STANDARD POLISHED TIN|15|SM PKG|905.00| wake carefully | +6|bisque cornflower lawn forest magenta|Manufacturer#2|Brand#24|PROMO PLATED STEEL|4|MED BAG|906.00|sual a| +7|moccasin green thistle khaki floral|Manufacturer#1|Brand#11|SMALL PLATED COPPER|45|SM BAG|907.00|lyly. ex| +8|misty lace thistle snow royal|Manufacturer#4|Brand#44|PROMO BURNISHED TIN|41|LG DRUM|908.00|eposi| +9|thistle dim navajo dark gainsboro|Manufacturer#4|Brand#43|SMALL BURNISHED STEEL|12|WRAP CASE|909.00|ironic foxe| +10|linen pink saddle puff powder|Manufacturer#5|Brand#54|LARGE BURNISHED STEEL|44|LG CAN|910.01|ithely final deposit| +11|spring maroon seashell almond orchid|Manufacturer#2|Brand#25|STANDARD BURNISHED NICKEL|43|WRAP BOX|911.01|ng gr| +12|cornflower wheat orange maroon ghost|Manufacturer#3|Brand#33|MEDIUM ANODIZED STEEL|25|JUMBO CASE|912.01| quickly| +13|ghost olive orange rosy thistle|Manufacturer#5|Brand#55|MEDIUM BURNISHED NICKEL|1|JUMBO PACK|913.01|osits.| +14|khaki seashell rose cornsilk navajo|Manufacturer#1|Brand#13|SMALL POLISHED STEEL|28|JUMBO BOX|914.01|kages c| +15|blanched honeydew sky turquoise medium|Manufacturer#1|Brand#15|LARGE ANODIZED BRASS|45|LG CASE|915.01|usual ac| +16|deep sky turquoise drab peach|Manufacturer#3|Brand#32|PROMO PLATED TIN|2|MED PACK|916.01|unts a| +17|indian navy coral pink deep|Manufacturer#4|Brand#43|ECONOMY BRUSHED STEEL|16|LG BOX|917.01| regular accounts| +18|turquoise indian lemon lavender misty|Manufacturer#1|Brand#11|SMALL BURNISHED STEEL|42|JUMBO PACK|918.01|s cajole slyly a| +19|chocolate navy tan deep brown|Manufacturer#2|Brand#23|SMALL ANODIZED NICKEL|33|WRAP BOX|919.01| pending acc| +20|ivory navy honeydew sandy midnight|Manufacturer#1|Brand#12|LARGE POLISHED NICKEL|48|MED BAG|920.02|are across the asympt| +21|lemon floral azure frosted lime|Manufacturer#3|Brand#33|SMALL BURNISHED TIN|31|MED BAG|921.02|ss packages. pendin| +22|medium forest blue ghost black|Manufacturer#4|Brand#43|PROMO POLISHED BRASS|19|LG DRUM|922.02| even p| +23|coral lavender seashell rosy burlywood|Manufacturer#3|Brand#35|MEDIUM BURNISHED TIN|42|JUMBO JAR|923.02|nic, fina| +24|seashell coral metallic midnight floral|Manufacturer#5|Brand#52|MEDIUM PLATED STEEL|20|MED CASE|924.02| final the| +25|aquamarine steel firebrick light turquoise|Manufacturer#5|Brand#55|STANDARD BRUSHED COPPER|3|JUMBO BAG|925.02|requests wake| +26|beige frosted moccasin chocolate snow|Manufacturer#3|Brand#32|SMALL BRUSHED STEEL|32|SM CASE|926.02| instructions i| +27|saddle puff beige linen yellow|Manufacturer#1|Brand#14|LARGE ANODIZED TIN|20|MED PKG|927.02|s wake. ir| +28|navajo yellow drab white misty|Manufacturer#4|Brand#44|SMALL PLATED COPPER|19|JUMBO PKG|928.02|x-ray pending, iron| +29|lemon sky grey salmon orchid|Manufacturer#3|Brand#33|PROMO PLATED COPPER|7|LG DRUM|929.02| carefully fluffi| +30|cream misty steel spring medium|Manufacturer#4|Brand#42|PROMO ANODIZED TIN|17|LG BOX|930.03|carefully bus| +31|slate seashell steel medium moccasin|Manufacturer#5|Brand#53|STANDARD BRUSHED TIN|10|LG BAG|931.03|uriously s| +32|sandy wheat coral spring burnished|Manufacturer#4|Brand#42|ECONOMY PLATED BRASS|31|LG CASE|932.03|urts. carefully fin| +33|spring bisque salmon slate pink|Manufacturer#2|Brand#22|ECONOMY PLATED NICKEL|16|LG PKG|933.03|ly eve| +34|khaki steel rose ghost salmon|Manufacturer#1|Brand#13|LARGE BRUSHED STEEL|8|JUMBO BOX|934.03|riously ironic| +35|green blush tomato burlywood seashell|Manufacturer#4|Brand#43|MEDIUM ANODIZED BRASS|14|JUMBO PACK|935.03|e carefully furi| +36|chiffon tan forest moccasin dark|Manufacturer#2|Brand#25|SMALL BURNISHED COPPER|3|JUMBO CAN|936.03|olites o| +37|royal coral orange burnished navajo|Manufacturer#4|Brand#45|LARGE POLISHED TIN|48|JUMBO BOX|937.03|silent | +38|seashell papaya white mint brown|Manufacturer#4|Brand#43|ECONOMY ANODIZED BRASS|11|SM JAR|938.03|structions inte| +39|rose medium floral salmon powder|Manufacturer#5|Brand#53|SMALL POLISHED TIN|43|JUMBO JAR|939.03|se slowly above the fl| +40|lemon midnight metallic sienna steel|Manufacturer#2|Brand#25|ECONOMY BURNISHED COPPER|27|SM CASE|940.04|! blithely specia| +41|burlywood goldenrod pink peru sienna|Manufacturer#2|Brand#23|ECONOMY ANODIZED TIN|7|WRAP JAR|941.04|uriously. furiously cl| +42|midnight turquoise lawn beige thistle|Manufacturer#5|Brand#52|MEDIUM BURNISHED TIN|45|LG BOX|942.04|the slow| +43|medium lace midnight royal chartreuse|Manufacturer#4|Brand#44|PROMO POLISHED STEEL|5|WRAP CASE|943.04|e slyly along the ir| +44|saddle cream wheat lemon burnished|Manufacturer#4|Brand#45|MEDIUM PLATED TIN|48|SM PACK|944.04|pinto beans. carefully| +45|lawn peru ghost khaki maroon|Manufacturer#4|Brand#43|SMALL BRUSHED NICKEL|9|WRAP BAG|945.04|nts bo| +46|honeydew turquoise aquamarine spring tan|Manufacturer#1|Brand#11|STANDARD POLISHED TIN|45|WRAP CASE|946.04|the blithely unusual | +47|honeydew red azure magenta brown|Manufacturer#4|Brand#45|LARGE BURNISHED BRASS|14|JUMBO PACK|947.04| even plate| +48|slate thistle cornsilk pale forest|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|27|JUMBO CASE|948.04|ng to the depo| +49|light firebrick cyan puff blue|Manufacturer#2|Brand#24|SMALL BURNISHED TIN|31|MED DRUM|949.04|ar pack| +50|linen blanched tomato slate medium|Manufacturer#3|Brand#33|LARGE ANODIZED TIN|25|WRAP PKG|950.05|kages m| +51|lime frosted indian dodger linen|Manufacturer#4|Brand#45|ECONOMY BURNISHED NICKEL|34|JUMBO PACK|951.05|n foxes| +52|lemon midnight lace sky deep|Manufacturer#3|Brand#35|STANDARD BURNISHED TIN|25|WRAP CASE|952.05| final deposits. fu| +53|bisque rose cornsilk seashell purple|Manufacturer#2|Brand#23|ECONOMY BURNISHED NICKEL|32|MED BAG|953.05|mptot| +54|blanched mint yellow papaya cyan|Manufacturer#2|Brand#21|LARGE BURNISHED COPPER|19|WRAP CASE|954.05|e blithely| +55|sky cream deep tomato rosy|Manufacturer#2|Brand#23|ECONOMY BRUSHED COPPER|9|MED BAG|955.05|ly final pac| +56|antique beige brown deep dodger|Manufacturer#1|Brand#12|MEDIUM PLATED STEEL|20|WRAP DRUM|956.05|ts. blithel| +57|purple blue light sienna deep|Manufacturer#3|Brand#32|MEDIUM BURNISHED BRASS|49|MED PKG|957.05|lly abov| +58|linen hot cornsilk drab bisque|Manufacturer#5|Brand#53|STANDARD POLISHED TIN|44|LG PACK|958.05| fluffily blithely reg| +59|misty brown medium mint salmon|Manufacturer#5|Brand#53|MEDIUM POLISHED TIN|2|LG BAG|959.05|regular exc| +60|snow spring sandy olive tomato|Manufacturer#1|Brand#11|LARGE POLISHED COPPER|27|JUMBO CASE|960.06| integ| +61|light tan linen tomato peach|Manufacturer#5|Brand#54|SMALL BURNISHED NICKEL|18|WRAP DRUM|961.06|es. blithely en| +62|tan cornsilk spring grey chocolate|Manufacturer#3|Brand#35|STANDARD BRUSHED BRASS|39|JUMBO BOX|962.06|ckly across the carefu| +63|burnished puff coral light papaya|Manufacturer#3|Brand#32|STANDARD BURNISHED NICKEL|10|JUMBO CAN|963.06| quickly | +64|aquamarine coral lemon ivory gainsboro|Manufacturer#2|Brand#21|MEDIUM ANODIZED BRASS|1|JUMBO CAN|964.06|efully regular pi| +65|slate drab medium puff gainsboro|Manufacturer#5|Brand#53|MEDIUM BRUSHED COPPER|3|MED CAN|965.06|posits after the quic| +66|cornflower pale almond lemon linen|Manufacturer#3|Brand#35|PROMO ANODIZED NICKEL|46|SM CASE|966.06|haggle blithely iro| +67|slate salmon rose spring seashell|Manufacturer#2|Brand#21|SMALL BRUSHED TIN|31|WRAP DRUM|967.06| regular, p| +68|bisque ivory mint purple almond|Manufacturer#1|Brand#11|PROMO ANODIZED STEEL|10|WRAP BOX|968.06|eposits shall h| +69|lace burnished rosy antique metallic|Manufacturer#5|Brand#52|MEDIUM POLISHED BRASS|2|SM BOX|969.06|ely final depo| +70|violet seashell firebrick dark navajo|Manufacturer#1|Brand#11|STANDARD BRUSHED STEEL|42|LG PACK|970.07|inal gifts. sl| +71|violet firebrick cream peru white|Manufacturer#3|Brand#33|STANDARD PLATED BRASS|26|WRAP DRUM|971.07| packages alongside| +72|hot spring yellow azure dodger|Manufacturer#2|Brand#23|STANDARD ANODIZED TIN|25|JUMBO PACK|972.07|efully final the| +73|cream moccasin royal dim chiffon|Manufacturer#2|Brand#21|SMALL BRUSHED COPPER|35|WRAP DRUM|973.07|ts haggl| +74|frosted grey aquamarine thistle papaya|Manufacturer#5|Brand#55|ECONOMY ANODIZED BRASS|25|JUMBO CASE|974.07|ent foxes| +75|aquamarine maroon wheat salmon metallic|Manufacturer#3|Brand#35|SMALL BURNISHED NICKEL|39|SM JAR|975.07|s sleep furiou| +76|rosy light lime puff sandy|Manufacturer#3|Brand#34|MEDIUM BRUSHED COPPER|9|SM PKG|976.07|n accounts sleep qu| +77|mint bisque chiffon snow firebrick|Manufacturer#5|Brand#52|STANDARD BRUSHED COPPER|13|MED PKG|977.07|uests.| +78|blush forest slate seashell puff|Manufacturer#1|Brand#14|ECONOMY POLISHED STEEL|24|LG JAR|978.07|icing deposits wake| +79|gainsboro pink grey tan almond|Manufacturer#4|Brand#45|PROMO ANODIZED BRASS|22|JUMBO BAG|979.07| foxes are slyly regu| +80|tomato chartreuse coral turquoise linen|Manufacturer#4|Brand#44|PROMO PLATED BRASS|28|MED CAN|980.08|unusual dependencies i| +81|misty sandy cornsilk dodger blush|Manufacturer#5|Brand#53|ECONOMY BRUSHED TIN|21|MED BAG|981.08|ove the furiou| +82|khaki tomato purple almond tan|Manufacturer#1|Brand#15|ECONOMY POLISHED TIN|12|WRAP BOX|982.08|ial requests haggle | +83|blush green dim lawn peru|Manufacturer#1|Brand#12|PROMO BURNISHED NICKEL|47|SM CAN|983.08|ly regul| +84|salmon floral cream rose dark|Manufacturer#4|Brand#45|SMALL ANODIZED NICKEL|26|JUMBO PACK|984.08|ideas nag| +85|dim deep aquamarine smoke pale|Manufacturer#5|Brand#55|PROMO ANODIZED NICKEL|16|LG BAG|985.08| silent| +86|green blanched firebrick dim cream|Manufacturer#4|Brand#44|STANDARD PLATED TIN|37|LG CASE|986.08| daring sheaves | +87|purple lace seashell antique orange|Manufacturer#4|Brand#41|LARGE PLATED STEEL|41|WRAP PACK|987.08|yly final| +88|lime orange bisque chartreuse lemon|Manufacturer#4|Brand#44|PROMO PLATED COPPER|16|SM CASE|988.08|e regular packages. | +89|ghost lace lemon sienna saddle|Manufacturer#5|Brand#53|STANDARD BURNISHED STEEL|7|MED JAR|989.08|y final pinto | +90|hot rosy violet plum pale|Manufacturer#5|Brand#51|ECONOMY POLISHED STEEL|49|JUMBO CAN|990.09|caref| +91|misty bisque lavender spring turquoise|Manufacturer#2|Brand#21|STANDARD BRUSHED TIN|32|JUMBO PKG|991.09|counts dete| +92|blush magenta ghost tomato rose|Manufacturer#2|Brand#22|STANDARD ANODIZED TIN|35|JUMBO PKG|992.09|he ironic accounts. sp| +93|pale yellow cornsilk dodger moccasin|Manufacturer#2|Brand#24|LARGE ANODIZED TIN|2|WRAP DRUM|993.09| platel| +94|blanched pink frosted mint snow|Manufacturer#3|Brand#35|STANDARD POLISHED BRASS|32|SM BOX|994.09|s accounts cajo| +95|dodger beige wheat orchid navy|Manufacturer#3|Brand#33|LARGE BRUSHED TIN|36|WRAP DRUM|995.09| final pinto beans | +96|chocolate light firebrick rose indian|Manufacturer#5|Brand#53|STANDARD BRUSHED STEEL|32|SM CASE|996.09|ng to the bli| +97|coral dodger beige black chartreuse|Manufacturer#3|Brand#33|MEDIUM POLISHED BRASS|49|WRAP CAN|997.09|ss excuses sleep am| +98|frosted peru chiffon yellow aquamarine|Manufacturer#5|Brand#54|STANDARD ANODIZED BRASS|22|MED JAR|998.09|e the q| +99|mint grey purple sienna metallic|Manufacturer#2|Brand#21|SMALL BURNISHED STEEL|11|JUMBO PKG|999.09|press| +100|cyan orchid indian cornflower saddle|Manufacturer#3|Brand#33|ECONOMY ANODIZED TIN|4|LG BAG|1000.10|of the steal| +101|powder deep lavender violet gainsboro|Manufacturer#3|Brand#32|LARGE ANODIZED STEEL|26|JUMBO JAR|1001.10|ly even,| +102|papaya maroon blush powder sky|Manufacturer#3|Brand#31|MEDIUM BURNISHED BRASS|17|SM DRUM|1002.10|ular packa| +103|navy sky spring orchid forest|Manufacturer#2|Brand#25|MEDIUM PLATED BRASS|45|WRAP DRUM|1003.10|e blithely blith| +104|plum cyan cornflower midnight royal|Manufacturer#1|Brand#13|MEDIUM ANODIZED STEEL|36|JUMBO BAG|1004.10|ites sleep quickly| +105|dodger slate pale mint navajo|Manufacturer#1|Brand#15|SMALL POLISHED COPPER|27|LG DRUM|1005.10|odolites was | +106|cornsilk bisque seashell lemon frosted|Manufacturer#3|Brand#31|MEDIUM PLATED BRASS|28|WRAP DRUM|1006.10|unts maintain | +107|violet honeydew bisque sienna orchid|Manufacturer#5|Brand#53|SMALL BURNISHED TIN|12|MED BOX|1007.10|slyly special depos| +108|bisque peach magenta tomato yellow|Manufacturer#1|Brand#12|PROMO PLATED NICKEL|41|MED PKG|1008.10|after the carefully | +109|lemon black indian cornflower pale|Manufacturer#3|Brand#33|ECONOMY POLISHED TIN|11|LG PACK|1009.10|instruction| +110|firebrick navy rose beige black|Manufacturer#3|Brand#33|STANDARD BURNISHED COPPER|46|LG DRUM|1010.11|t quickly a| +111|orange cornflower mint snow peach|Manufacturer#5|Brand#54|LARGE BRUSHED COPPER|28|JUMBO JAR|1011.11|kly bold epitaphs | +112|hot aquamarine tomato lace indian|Manufacturer#4|Brand#43|PROMO BRUSHED STEEL|42|JUMBO CAN|1012.11|the express, | +113|almond seashell azure blanched light|Manufacturer#3|Brand#31|PROMO POLISHED TIN|23|LG CAN|1013.11|finally even | +114|pink black blanched lace chartreuse|Manufacturer#5|Brand#51|MEDIUM POLISHED NICKEL|41|MED PACK|1014.11|ully final foxes. pint| +115|spring chiffon cream orchid dodger|Manufacturer#4|Brand#45|STANDARD POLISHED STEEL|24|MED CAN|1015.11|counts nag! caref| +116|goldenrod black slate forest red|Manufacturer#5|Brand#53|PROMO POLISHED NICKEL|33|SM PACK|1016.11|usly final courts | +117|tomato honeydew pale red yellow|Manufacturer#1|Brand#14|SMALL BRUSHED TIN|25|LG BAG|1017.11|ages acc| +118|ghost plum brown coral cornsilk|Manufacturer#2|Brand#25|PROMO ANODIZED TIN|31|MED PACK|1018.11|ly ironic pinto| +119|olive metallic slate peach green|Manufacturer#4|Brand#43|LARGE POLISHED STEEL|30|WRAP CASE|1019.11|out the quickly r| +120|pink powder mint moccasin navajo|Manufacturer#1|Brand#14|SMALL ANODIZED NICKEL|45|WRAP JAR|1020.12|lly a| +121|bisque royal goldenrod medium thistle|Manufacturer#1|Brand#14|ECONOMY BRUSHED COPPER|13|SM PKG|1021.12|deposi| +122|gainsboro royal forest dark lace|Manufacturer#2|Brand#21|MEDIUM ANODIZED TIN|8|LG DRUM|1022.12|sts c| +123|deep dim peach light beige|Manufacturer#1|Brand#12|SMALL BURNISHED TIN|31|JUMBO PKG|1023.12|ray regula| +124|wheat blush forest metallic navajo|Manufacturer#3|Brand#32|PROMO ANODIZED STEEL|1|LG BOX|1024.12|g the expr| +125|mint ivory saddle peach midnight|Manufacturer#1|Brand#12|STANDARD BRUSHED BRASS|17|WRAP BAG|1025.12|kages against| +126|burnished black blue metallic orchid|Manufacturer#4|Brand#45|MEDIUM BRUSHED NICKEL|4|LG BAG|1026.12|es sleep al| +127|royal coral orchid spring sky|Manufacturer#5|Brand#52|SMALL BURNISHED NICKEL|14|LG JAR|1027.12|lithely expr| +128|dark burlywood burnished snow sky|Manufacturer#2|Brand#22|PROMO PLATED TIN|5|SM BAG|1028.12|e of the furiously ex| +129|grey spring chiffon thistle lime|Manufacturer#1|Brand#15|LARGE POLISHED TIN|20|SM JAR|1029.12| careful| +130|gainsboro powder cyan pale rosy|Manufacturer#2|Brand#23|SMALL PLATED NICKEL|26|LG BOX|1030.13|ake slyly| +131|tomato moccasin cyan brown goldenrod|Manufacturer#5|Brand#52|STANDARD ANODIZED BRASS|43|MED DRUM|1031.13|nts wake dar| +132|seashell papaya tomato lime hot|Manufacturer#4|Brand#45|STANDARD BURNISHED BRASS|2|WRAP DRUM|1032.13|ckly expre| +133|firebrick black dodger pink salmon|Manufacturer#1|Brand#13|SMALL BRUSHED NICKEL|19|LG PKG|1033.13| final pinto beans| +134|steel beige mint maroon indian|Manufacturer#4|Brand#42|SMALL POLISHED STEEL|35|SM PKG|1034.13|es. bold pa| +135|thistle chocolate ghost gainsboro peru|Manufacturer#2|Brand#21|MEDIUM BURNISHED STEEL|24|JUMBO CASE|1035.13|l frets | +136|cornsilk maroon blanched thistle rosy|Manufacturer#2|Brand#22|SMALL PLATED STEEL|2|WRAP BAG|1036.13|kages print carefully| +137|cornsilk drab ghost sandy royal|Manufacturer#3|Brand#31|ECONOMY PLATED STEEL|25|MED PACK|1037.13|the t| +138|dark aquamarine tomato medium puff|Manufacturer#1|Brand#13|ECONOMY BURNISHED COPPER|42|JUMBO DRUM|1038.13|ts solve acro| +139|floral steel burlywood navy cream|Manufacturer#3|Brand#32|MEDIUM BRUSHED STEEL|7|SM BOX|1039.13|ter t| +140|aquamarine lavender maroon slate hot|Manufacturer#5|Brand#53|STANDARD PLATED STEEL|45|SM BOX|1040.14|oss the carefu| +141|honeydew magenta tomato spring medium|Manufacturer#3|Brand#35|STANDARD ANODIZED STEEL|23|SM PKG|1041.14|ans nag furiously pen| +142|chartreuse linen grey slate saddle|Manufacturer#5|Brand#55|STANDARD ANODIZED BRASS|36|MED JAR|1042.14|he accounts. pac| +143|bisque dodger blanched steel maroon|Manufacturer#3|Brand#34|ECONOMY PLATED TIN|44|MED BAG|1043.14|nts across the| +144|hot midnight orchid dim steel|Manufacturer#1|Brand#14|SMALL ANODIZED TIN|26|SM BOX|1044.14|owly | +145|navajo lavender chocolate deep hot|Manufacturer#5|Brand#53|PROMO BRUSHED COPPER|24|SM BAG|1045.14|es wake furiously blit| +146|azure smoke mint cream burlywood|Manufacturer#3|Brand#34|STANDARD BRUSHED COPPER|11|WRAP PACK|1046.14|unts cajole| +147|honeydew orange dodger linen lace|Manufacturer#1|Brand#11|MEDIUM PLATED COPPER|29|JUMBO PKG|1047.14|wake never bold | +148|yellow white ghost lavender salmon|Manufacturer#3|Brand#31|STANDARD PLATED STEEL|20|SM BOX|1048.14|platelets wake fu| +149|tan thistle frosted indian lawn|Manufacturer#2|Brand#24|MEDIUM BURNISHED NICKEL|6|MED PKG|1049.14|leep requests. dog| +150|pale rose navajo firebrick aquamarine|Manufacturer#3|Brand#35|LARGE BRUSHED TIN|21|SM BAG|1050.15|ironic foxes| +151|chartreuse linen violet ghost thistle|Manufacturer#3|Brand#34|LARGE PLATED BRASS|45|MED CAN|1051.15|ccounts nag i| +152|white sky antique tomato chartreuse|Manufacturer#5|Brand#53|MEDIUM POLISHED STEEL|48|MED CASE|1052.15|thely regular t| +153|linen frosted slate coral peru|Manufacturer#1|Brand#11|STANDARD PLATED TIN|20|MED BAG|1053.15|thlessly. silen| +154|peru moccasin peach pale spring|Manufacturer#1|Brand#11|ECONOMY ANODIZED TIN|1|JUMBO BAG|1054.15|posits | +155|puff yellow cyan tomato purple|Manufacturer#2|Brand#21|SMALL BRUSHED NICKEL|28|WRAP CASE|1055.15|lly ironic, r| +156|almond ghost powder blush forest|Manufacturer#4|Brand#43|SMALL POLISHED NICKEL|2|LG PKG|1056.15| pinto beans. eve| +157|navajo linen coral brown forest|Manufacturer#1|Brand#11|ECONOMY ANODIZED STEEL|26|JUMBO PACK|1057.15|ial courts. ru| +158|magenta light misty navy honeydew|Manufacturer#4|Brand#45|MEDIUM BURNISHED COPPER|47|LG JAR|1058.15| ideas detect slyl| +159|white orange antique beige aquamarine|Manufacturer#4|Brand#43|SMALL ANODIZED BRASS|46|SM BAG|1059.15| ironic requests-- pe| +160|frosted cornflower khaki salmon metallic|Manufacturer#5|Brand#55|STANDARD POLISHED COPPER|47|JUMBO CAN|1060.16|nts are carefully| +161|metallic khaki navy forest cyan|Manufacturer#2|Brand#22|STANDARD PLATED TIN|17|SM PACK|1061.16|r the bl| +162|burlywood cornflower aquamarine misty snow|Manufacturer#3|Brand#33|MEDIUM ANODIZED COPPER|35|JUMBO PACK|1062.16|e slyly around th| +163|blush metallic maroon lawn forest|Manufacturer#2|Brand#21|ECONOMY PLATED TIN|34|WRAP DRUM|1063.16|nly s| +164|orange cyan magenta navajo indian|Manufacturer#2|Brand#23|LARGE PLATED BRASS|35|JUMBO BAG|1064.16|mong th| +165|white dim cornflower sky seashell|Manufacturer#1|Brand#15|STANDARD PLATED STEEL|24|SM CAN|1065.16| carefully fin| +166|linen bisque tomato gainsboro goldenrod|Manufacturer#5|Brand#52|LARGE POLISHED COPPER|4|MED BAG|1066.16|ss the| +167|almond floral grey dim sky|Manufacturer#3|Brand#32|LARGE ANODIZED STEEL|46|WRAP BOX|1067.16|ic ac| +168|lace gainsboro burlywood smoke tomato|Manufacturer#1|Brand#13|SMALL BRUSHED COPPER|20|JUMBO DRUM|1068.16|ss package| +169|bisque misty sky cornflower peach|Manufacturer#5|Brand#55|STANDARD POLISHED BRASS|10|JUMBO CASE|1069.16|lets alongside of| +170|peru grey blanched goldenrod yellow|Manufacturer#3|Brand#33|LARGE POLISHED COPPER|28|LG DRUM|1070.17|yly s| +171|beige violet black magenta chartreuse|Manufacturer#1|Brand#11|STANDARD BURNISHED COPPER|40|LG JAR|1071.17| the r| +172|medium goldenrod linen sky coral|Manufacturer#5|Brand#53|PROMO PLATED NICKEL|28|MED CASE|1072.17|quick as| +173|chartreuse seashell powder navy grey|Manufacturer#1|Brand#12|ECONOMY BURNISHED TIN|17|LG CASE|1073.17|sly bold excuses haggl| +174|hot cornflower slate saddle pale|Manufacturer#1|Brand#15|ECONOMY BRUSHED COPPER|25|LG CASE|1074.17| accounts nag ab| +175|magenta blue chartreuse tan green|Manufacturer#1|Brand#11|PROMO ANODIZED TIN|45|JUMBO JAR|1075.17|ole against the| +176|pink drab ivory papaya grey|Manufacturer#2|Brand#24|SMALL ANODIZED STEEL|40|MED CAN|1076.17|blithely. ironic| +177|indian turquoise purple green spring|Manufacturer#2|Brand#21|MEDIUM BRUSHED STEEL|42|LG BAG|1077.17|ermanently eve| +178|lace blanched magenta yellow almond|Manufacturer#1|Brand#13|STANDARD POLISHED TIN|10|LG JAR|1078.17|regular instructions.| +179|deep puff brown blue burlywood|Manufacturer#4|Brand#43|ECONOMY BRUSHED STEEL|20|LG JAR|1079.17|ely regul| +180|seashell maroon lace burnished lavender|Manufacturer#3|Brand#33|STANDARD BURNISHED NICKEL|7|WRAP BAG|1080.18|oss the | +181|antique plum smoke pink dodger|Manufacturer#2|Brand#24|MEDIUM PLATED STEEL|19|WRAP CAN|1081.18|al deposits | +182|beige cyan burlywood chiffon light|Manufacturer#3|Brand#31|MEDIUM ANODIZED COPPER|11|JUMBO CAN|1082.18|bits are | +183|ivory white burnished papaya cornflower|Manufacturer#5|Brand#52|PROMO POLISHED STEEL|35|LG PKG|1083.18|ly regular excus| +184|ghost honeydew cyan lawn powder|Manufacturer#5|Brand#53|SMALL POLISHED TIN|42|LG BOX|1084.18|ding courts. idly iro| +185|firebrick black ivory spring medium|Manufacturer#4|Brand#44|ECONOMY POLISHED TIN|4|WRAP BAG|1085.18|even foxe| +186|grey purple chocolate turquoise plum|Manufacturer#2|Brand#23|ECONOMY BRUSHED TIN|15|JUMBO PKG|1086.18|ly reg| +187|white red lace deep pale|Manufacturer#4|Brand#45|PROMO ANODIZED BRASS|45|MED CAN|1087.18|leep slyly s| +188|moccasin steel rosy drab white|Manufacturer#5|Brand#54|ECONOMY ANODIZED BRASS|9|MED CAN|1088.18| above the silent p| +189|dodger moccasin lemon purple thistle|Manufacturer#2|Brand#22|MEDIUM BRUSHED BRASS|13|WRAP DRUM|1089.18|en requests. sauternes| +190|chartreuse goldenrod midnight cornflower blush|Manufacturer#5|Brand#53|LARGE BURNISHED NICKEL|23|WRAP BAG|1090.19| furiously even d| +191|mint midnight puff forest peach|Manufacturer#3|Brand#31|MEDIUM POLISHED BRASS|36|WRAP BOX|1091.19| asymptote| +192|thistle puff pink cream orange|Manufacturer#3|Brand#34|STANDARD BRUSHED COPPER|17|MED BAG|1092.19|uickly regular, expr| +193|turquoise lime royal metallic azure|Manufacturer#4|Brand#45|ECONOMY BURNISHED BRASS|31|SM PKG|1093.19|final ideas wake furi| +194|brown black cream navy plum|Manufacturer#5|Brand#51|ECONOMY POLISHED STEEL|7|SM CAN|1094.19|y special accoun| +195|bisque sienna hot goldenrod khaki|Manufacturer#4|Brand#41|STANDARD BRUSHED NICKEL|40|MED CASE|1095.19|oxes sleep care| +196|pale peru linen hot maroon|Manufacturer#3|Brand#33|SMALL BURNISHED NICKEL|3|JUMBO JAR|1096.19|uickly special | +197|lawn lemon khaki rosy blue|Manufacturer#5|Brand#52|SMALL ANODIZED COPPER|18|SM JAR|1097.19|lithely after the eve| +198|orange cornflower indian aquamarine white|Manufacturer#4|Brand#41|PROMO BRUSHED NICKEL|43|SM PACK|1098.19|ackages? carefully re| +199|ivory slate lavender tan royal|Manufacturer#3|Brand#31|ECONOMY PLATED STEEL|23|JUMBO DRUM|1099.19|ickly regul| +200|peach cornsilk navy rosy red|Manufacturer#5|Brand#54|MEDIUM POLISHED BRASS|22|LG PKG|1100.20|furiously even depo| diff --git a/zetasql/examples/tpch/catalog/partsupp.tbl b/zetasql/examples/tpch/catalog/partsupp.tbl new file mode 100644 index 000000000..d8e5856ee --- /dev/null +++ b/zetasql/examples/tpch/catalog/partsupp.tbl @@ -0,0 +1,800 @@ +1|2|3325|771.64|, even theodolites. regular, final theodolites eat after the carefully pending foxes. furiously regular deposits sleep slyly. carefully bold realms above the ironic dependencies haggle careful| +1|4|8076|993.49|ven ideas. quickly even packages print. pending multipliers must have to are fluff| +1|6|3956|337.09|after the fluffily ironic deposits? blithely special dependencies integrate furiously even excuses. blithely silent theodolites could have to haggle pending, express requests; fu| +1|8|4069|357.84|al, regular dependencies serve carefully after the quickly final pinto beans. furiously even deposits sleep quickly final, silent pinto beans. fluffily reg| +2|3|8895|378.49|nic accounts. final accounts sleep furiously about the ironic, bold packages. regular, regular accounts| +2|5|4969|915.27|ptotes. quickly pending dependencies integrate furiously. fluffily ironic ideas impress blithely above the express accounts. furiously even epitaphs need to wak| +2|7|8539|438.37|blithely bold ideas. furiously stealthy packages sleep fluffily. slyly special deposits snooze furiously carefully regular accounts. regular deposits according to the accounts nag carefully slyl| +2|9|3025|306.39|olites. deposits wake carefully. even, express requests cajole. carefully regular ex| +3|4|4651|920.92|ilent foxes affix furiously quickly unusual requests. even packages across the carefully even theodolites nag above the sp| +3|6|4093|498.13|ending dependencies haggle fluffily. regular deposits boost quickly carefully regular requests. deposits affix furiously around the pinto beans. ironic, unusual platelets across the p| +3|8|3917|645.40|of the blithely regular theodolites. final theodolites haggle blithely carefully unusual ideas. blithely even f| +3|10|9942|191.92| unusual, ironic foxes according to the ideas detect furiously alongside of the even, express requests. blithely regular the| +4|5|1339|113.97| carefully unusual ideas. packages use slyly. blithely final pinto beans cajole along the furiously express requests. regular orbits haggle carefully. care| +4|7|6377|591.18|ly final courts haggle carefully regular accounts. carefully regular accounts could integrate slyly. slyly express packages about the accounts wake slyly| +4|9|2694|51.37|g, regular deposits: quick instructions run across the carefully ironic theodolites-- final dependencies haggle into the dependencies. f| +4|1|2480|444.37|requests sleep quickly regular accounts. theodolites detect. carefully final depths w| +5|6|3735|255.88|arefully even requests. ironic requests cajole carefully even dolphin| +5|8|9653|50.52|y stealthy deposits. furiously final pinto beans wake furiou| +5|10|1329|219.83|iously regular deposits wake deposits. pending pinto beans promise ironic dependencies. even, regular pinto beans integrate| +5|2|6925|537.98|sits. quickly fluffy packages wake quickly beyond the blithely regular requests. pending requests cajole among the final pinto beans. carefully busy theodolites affix quickly stealthily | +6|7|8851|130.72|usly final packages. slyly ironic accounts poach across the even, sly requests. carefully pending request| +6|9|1627|424.25| quick packages. ironic deposits print. furiously silent platelets across the carefully final requests are slyly along the furiously even instructi| +6|1|3336|642.13|final instructions. courts wake packages. blithely unusual realms along the multipliers nag | +6|3|6451|175.32| accounts alongside of the slyly even accounts wake carefully final instructions-- ruthless platelets wake carefully ideas. even deposits are quickly final,| +7|8|7454|763.98|y express tithes haggle furiously even foxes. furiously ironic deposits sleep toward the furiously unusual| +7|10|2770|149.66|hould have to nag after the blithely final asymptotes. fluffily spe| +7|2|3377|68.77|usly against the daring asymptotes. slyly regular platelets sleep quickly blithely regular deposits. boldly regular deposits wake blithely ironic accounts| +7|4|9460|299.58|. furiously final ideas hinder slyly among the ironic, final packages. blithely ironic dependencies cajole pending requests: blithely even packa| +8|9|6834|249.63|lly ironic accounts solve express, unusual theodolites. special packages use quickly. quickly fin| +8|1|396|957.34|r accounts. furiously pending dolphins use even, regular platelets. final| +8|3|9845|220.62|s against the fluffily special packages snooze slyly slyly regular p| +8|5|8126|916.91|final accounts around the blithely special asymptotes wake carefully beyond the bold dugouts. regular ideas haggle furiously after| +9|10|7054|84.20|ts boost. evenly regular packages haggle after the quickly careful accounts. | +9|2|7542|811.84|ate after the final pinto beans. express requests cajole express packages. carefully bold ideas haggle furiously. blithely express accounts eat carefully among the evenly busy accounts. carefully un| +9|4|9583|381.31|d foxes. final, even braids sleep slyly slyly regular ideas. unusual ideas above| +9|6|3063|291.84| the blithely ironic instructions. blithely express theodolites nag furiously. carefully bold requests shall have to use slyly pending requests. carefully regular instr| +10|1|2952|996.12| bold foxes wake quickly even, final asymptotes. blithely even depe| +10|3|3335|673.27|s theodolites haggle according to the fluffily unusual instructions. silent realms nag carefully ironic theodolites. furiously unusual instructions would detect fu| +10|5|5691|164.00|r, silent instructions sleep slyly regular pinto beans. furiously unusual gifts use. silently ironic theodolites cajole final deposits! express dugouts are furiously. packages sleep | +10|7|841|374.02|refully above the ironic packages. quickly regular packages haggle foxes. blithely ironic deposits a| +11|2|4540|709.87|thely across the blithely unusual requests. slyly regular instructions wake slyly ironic theodolites. requests haggle blithely above the blithely brave p| +11|5|4729|894.90|ters wake. sometimes bold packages cajole sometimes blithely final instructions. carefully ironic foxes after the furiously unusual foxes cajole carefully acr| +11|8|3708|818.74|inal accounts nag quickly slyly special frays; bold, final theodolites play slyly after the furiously pending packages. f| +11|1|3213|471.98|nusual, regular requests use carefully. slyly final packages haggle quickly. slyly express packages impress blithely across the blithely regular ideas. regular depe| +12|3|3610|659.73|jole bold theodolites. final packages haggle! carefully regular deposits play furiously among the special ideas. quickly ironic packages detect quickly carefully final| +12|6|7606|332.81|luffily regular courts engage carefully special realms. regular accounts across the blithely special pinto beans use carefully at the silent request| +12|9|824|337.06|es are unusual deposits. fluffily even deposits across the blithely final theodolites doubt across the unusual accounts. regular, | +12|2|5454|901.70|s across the carefully regular courts haggle fluffily among the even theodolites. blithely final platelets x-ray even ideas. fluffily express pinto beans sleep slyly. carefully even a| +13|4|612|169.44|s. furiously even asymptotes use slyly blithely express foxes. pending courts integrate blithely among the ironic requests! blithely pending deposits integrate slyly furiously final packa| +13|7|7268|862.70|s sleep slyly packages. final theodolites to the express packages haggle quic| +13|10|864|38.64|s after the slyly pending instructions haggle even, express requests. permanently regular pinto beans are. slyly pending req| +13|3|9736|327.18|tect after the express instructions. furiously silent ideas sleep blithely special ideas. attainments sleep furiously. carefully bold requests ab| +14|5|5278|650.07|e quickly among the furiously ironic accounts. special, final sheaves against the| +14|8|5334|889.50|ss dependencies are furiously silent excuses. blithely ironic pinto beans affix quickly according to the slyly ironic asymptotes. final packag| +14|1|3676|893.39|sits are according to the fluffily silent asymptotes. final ideas are slyly above the regular instructions. furiousl| +14|4|4947|310.13| final deposits boost slyly regular packages; carefully pending theodolites | +15|6|7047|835.70|blithely quick requests sleep carefully fluffily regular pinto beans. ironic pinto beans around the slyly regular foxe| +15|9|3336|784.55|slyly. fluffily bold accounts cajole furiously. furiously regular dependencies wak| +15|2|3316|265.89|e express instructions. ironic requests haggle fluffily along the carefully even packages. furiously final acco| +15|5|5255|458.67|refully bold instructions among the silent grouches must boost against the express deposits:| +16|7|5282|709.16|lithely ironic theodolites should have to are furiously-- | +16|10|9412|887.53|ly special accounts wake. fluffily bold ideas believe blith| +16|3|854|781.91| unusual excuses. requests after the carefully regular pinto | +16|6|1491|918.51|unts cajole furiously across the fluffily pending instructions. slyly special accounts could have to boost b| +17|8|8555|995.35|are furiously final accounts. carefully unusual accounts snooze across the requests. carefully special dolphins| +17|1|7737|648.75|e blithely express accounts. foxes kindle slyly unusual dinos. quickly special f| +17|4|3123|555.04|ly bold accounts. regular packages use silently. quickly unusual sentiments around the quickly ironic theodolites haggle furiously pending requests. care| +17|7|3203|64.40|bold packages nag fluffily after the regular accounts. furiously ironic asymptotes sleep quickly enticing pinto beans. carefully pending accounts use about the | +18|9|1125|664.17|. ironic, regular accounts across the furiously express | +18|2|8132|52.44| final packages wake quickly across the blithely ironic instructions. regular pains integrate slyly across the deposits. carefully regular pinto beans among the close| +18|5|3133|568.61|riously bold accounts. packages boost daringly. blithely regular requests cajole. regular foxes wake carefully final accounts. blithely unusual excuses det| +18|8|6475|386.29|. furiously regular accounts cajole slyly across the pending| +19|10|1416|144.80|o beans. even packages nag boldly according to the bold, special deposits. ironic packages after the pinto beans nag above the quickly ironic requests. bl| +19|3|5467|405.70|nstructions use furiously. fluffily regular excuses wake. slyly special grouches are carefully regular Tiresias. regular requests use about the quickly furio| +19|6|8800|635.66|sual requests sleep carefully. deposits cajole carefully over the regular, regular requests. quickly unusual asymptotes use some| +19|9|1340|346.92| requests. final, pending realms use carefully; slyly dogged foxes impress fluffily above the blithely regular deposits. ironic, regular courts wake carefully. bold requests impress| +20|1|2927|675.54|s, ironic deposits haggle across the quickly bold asymptotes. express, ironic pinto beans wake carefully enticingly special foxes. requests are at the c| +20|4|2723|305.84|nal, bold frets cajole slyly regular, unusual platelets. slyly permanent deposits wake carefully carefully silent accounts. even, even requests wake quickly. furiously pending packages are| +20|7|5905|546.66|ing deposits use furiously. ironically final pinto bea| +20|10|4271|115.89|xcuses wake at the deposits. regular pinto beans nag slyly fluffi| +21|2|6571|944.44|ing instructions impress bold foxes. ironic pinto beans use. thinly even asymptotes cajole ironic packages. quickly ironic pinto beans detect slyly regular deposits. ruthlessly even deposits are. sl| +21|6|1704|139.05|posits cajole; quickly even requests sleep furiously. ironic theodolites sleep pending, express instructions. stealthily even platelets cajole carefully after the final, ironic p| +21|10|7153|664.50|blithely enticing instructions use alongside of the carefully thin deposits. blithely bold requests are fluffily| +21|4|367|584.86|ong the even theodolites. pending, pending accounts sleep-- courts boost quickly at the accounts. quickly fin| +22|3|4410|786.18|even accounts. final excuses try to sleep regular, even packages. carefully express dolphins cajole; furiously special pinto bea| +22|7|9779|635.84|l instructions cajole across the blithely special deposits. blithely pending accounts use thinly slyly final requests. instructions haggle. pinto beans sleep along the slyly pen| +22|1|7834|359.16|sits wake fluffily carefully stealthy accounts. furiously ironic requests x-ray fluffily alongside of the pending asymptotes. slyly silent packages use along the instructions. fu| +22|5|1434|597.21|ix across the blithely express packages. carefully regular pinto beans boost across the special, pending d| +23|4|2739|460.12|platelets against the furiously bold Tiresias dazzle quickly into the special, bold courts. silent, regular instructions wake blithely ironic multipliers. ideas| +23|8|5739|103.13| theodolites need to nag blithely final notornis. slyly idle packages cajole after the furiously stealthy packages. slyly regular accounts use furiously. carefully final accounts affix | +23|2|9898|233.94|l, express packages wake permanently. quickly even deposits sleep quickly slyly silent id| +23|6|7035|51.75|xcuses; decoys wake after the pending packages. final instructions are furi| +24|5|5180|905.41|heodolites above the ironic requests poach fluffily carefully unusual pinto beans. even packages acc| +24|9|2227|511.20|, silent packages boost around the instructions. special requests sleep slyly against the slyly regular deposits. final, final accounts haggle fluffily among the final requests. regular | +24|3|7182|582.03| the final, ironic asymptotes. regular requests nag instead of the carefully unusual asymptotes. furiously pending attainments among the slyly final packages boost after th| +24|7|5318|62.15| careful requests cajole blithely realms. special asymptotes sleep. pinto beans sleep carefully furiously ironic packages. furiously | +25|6|9029|832.74|fully fluffily regular frets. sometimes even requests after the requests wake slyly at the quickly ruthless requests. a| +25|10|9062|928.96|he foxes. final, final accounts sleep. boldly ironic excuses thrash quick| +25|4|9946|694.35|ld, ironic requests. furiously special packages cajole furiously enticing instructions.| +25|8|7340|746.59|dly final packages haggle blithely according to the pending packages. slyly regula| +26|7|5020|683.96|es. fluffily express deposits kindle slyly accounts. slyly ironic requests wake blithely bold ideas| +26|1|6577|892.20|riously pending pinto beans. furiously express instructions detect slyly according to the b| +26|5|3499|382.11|imes even pinto beans among the busily ironic accounts doubt blithely quickly final courts. furiously fluffy packages despite the carefully even plate| +26|9|9702|821.89| behind the blithely regular courts impress after the silent sheaves. bravely final ideas haggle | +27|8|2111|444.01|the even, ironic deposits. theodolites along the ironic, final dolphins cajole slyly quickly bold asymptotes. furiously regular theodolites integrate furiously furiously bold requests. carefully| +27|2|9080|157.03|ole express, final requests. carefully regular packages lose about the regular pinto beans. blithely re| +27|6|3407|151.34|ironic theodolites are by the furiously bold ideas. ironic requests shall have to sublate final packages. furiously quick foxes alongside of the express, special deposits was boldly according | +27|10|4283|348.61|ound the final foxes detect furiously across the even warhorses. quickly t| +28|9|6643|204.86|y ironic deposits above the slyly final deposits sleep furiously above the final deposits. quickly even i| +28|3|2452|744.57|ully regular theodolites haggle about the blithely pending packages. carefully ironic sentiments use quickly around the blithely silent requests. slyly ironic frays bo| +28|7|302|690.30|uickly unusual requests alongside of the final courts integrate slyly | +28|1|9988|666.53|beans haggle carefully around the slyly ironic acco| +29|10|3506|799.27|leep fluffily according to the quietly regular requests: accounts integrate carefully bold foxes. carefully silent| +29|4|8106|981.33|the ironic, bold asymptotes! blithely regular packages hang furiously above the dependencies. blithely permanent dependencies are furiously furiously ironic acco| +29|8|9193|734.44|ly unusual packages. foxes cajole. theodolites nag| +29|2|6252|186.21|thely carefully even packages. even, final packages cajole after the quickly bold accounts. fluffily quick accounts in place of the theodolites doze slyly f| +30|1|4767|989.05|ts. slyly final pinto beans cajole ironic accounts. blithely final accounts use among the request| +30|5|535|743.26|sual instructions wake carefully blithely even hockey playe| +30|9|7756|568.86| special foxes across the dependencies cajole quickly against the slyly express packages! furiously unusual pinto beans boost blithely ironic Tir| +30|3|7945|583.84| sleep. bold, regular deposits hang doggedly furiously bold requests. slyly bold excuses detect busily above the even gifts. blithely express courts are carefully. blithely final packages until th| +31|2|9685|620.84|he blithely regular ideas. blithely unusual requests haggle fluffily. platelets| +31|7|1951|120.99|refully regular pinto beans. ironic requests integrate furiously since the quickly ruthless platelets. quickly ironic attainments ha| +31|2|1402|761.64|r platelets nag blithely regular deposits. ironic, bold requests | +31|7|137|849.11|blithely ironic accounts. slyly ironic asymptotes sleep ironic, even accounts. regular accounts thrash quickly| +32|3|2203|406.03|es? slyly enticing dugouts haggle carefully. regular packages alongside of the asymptotes are carefull| +32|8|467|109.34|ainst the unusual braids nod fluffily packages. regular packages nod among the slyly express| +32|3|7975|747.14|final foxes boost furiously pending packages. quickly regular depths promise blithely accoun| +32|8|7938|856.09|s integrate according to the even dependencies. carefully regular reque| +33|4|4028|891.46|, pending requests affix slyly. slyly ironic deposits wake accounts. express accounts sleep slowly. ironic, express accounts run carefully fluffily final dependencies. furiously unusual ideas| +33|9|4410|929.05| packages sleep carefully. slyly final instructions boost. slyly even requests among the carefully pending platelets wake along the final accounts. quickly expre| +33|4|1287|310.76|dolites above the slyly express deposits try to haggle blithely special gifts. blithely ironic reque| +33|9|6006|327.19|ly. ironic dependencies haggle carefully silent instructions. furiously ironic dolphins are fluffily furiously even theo| +34|5|9934|848.75|ven instructions besides the gifts are furiously among the slyly regular packages! instructions use carefully. even requests sleep quickl| +34|10|4749|265.31|ckly regular theodolites eat above the bravely regular courts. ironic requests wake slyly.| +34|5|5459|824.69|ong the slyly silent requests. express, even requests haggle slyly| +34|10|5884|609.69|ully final tithes. slyly ironic deposits hang furiously about the regular, regular deposits| +35|6|2500|451.58|nic packages boost carefully carefully even theodolites. blithely fina| +35|1|8875|537.72|ully regular deposits: special accounts use. slyly final deposits wake slyly unusual, special ideas. asymptotes | +35|6|596|669.19|slyly against the daring, pending accounts. fluffily special pinto beans integrate slyly after the carefully unusual packages. slyly bold accounts besides| +35|1|2025|411.17|s cajole fluffily final deposits. furiously express packages after the blithely special realms boost evenly even requests. slow requests use above the unusual accoun| +36|7|3907|630.91|al deposits detect fluffily fluffily unusual sauternes. carefully regular requests against the car| +36|2|174|434.47|permanently express instructions. unusual accounts nag toward the accou| +36|7|2625|569.91|ctions. pending requests are fluffily across the furiously regular notornis. unusu| +36|2|8209|289.15|arefully regular requests cajole. special, express foxes sleep slowly. quickly unusual in| +37|8|7171|824.96|usly into the slyly final requests. ironic accounts are furiously furiously ironic i| +37|3|5542|126.59|ven deposits. ironic foxes cajole. slyly final deposits are furiously after the furiously even packages. slyly ironic platelets toward the slyl| +37|8|7113|15.72|re bravely along the furiously express requests. blithely special asymptotes are quickly. fluffily regular packages alo| +37|3|1449|745.64|y after the ironic accounts. blithely final instructions affix blithely. bold packages sleep carefully regular instructions. regular packages affix carefully. stealthy fo| +38|9|1226|570.11| slyly even pinto beans. blithely special requests nag slyly about the ironic packages. | +38|4|4237|662.75|lar warhorses cajole evenly against the attainments. requests cajole furiously furiously express requests. carefully regular platelets use fluffily after the silent, unusual ideas: bl| +38|9|1135|160.70|express accounts haggle. carefully even pinto beans according to the slyly final foxes nag slyly about the enticingly express dol| +38|4|3516|847.09|nal accounts. furiously pending hockey players solve slyly after the furiously final dependencies. deposits are blithely. carefully regular packages unwind busily at the deposits. fluffily | +39|10|3633|463.10|kages are slyly above the slyly pending pinto beans. bold, ironic pinto beans sleep against the blithely regular requests. fluffily even pinto beans use. regular theodolites haggle against the quic| +39|5|3682|300.43|ng requests are according to the packages. regular packages boost quickly. express Tiresias sleep silently across the even, regular ideas! blithely iro| +39|10|5475|532.26| beans cajole carefully carefully express requests. instructions sleep furiously bold deposits. furiously regular depos| +39|5|6259|737.86|y. special, even asymptotes cajole carefully ironic accounts. regular, final pinto beans cajole quickly. regular requests use warhorses. special, special accounts hinder boldly across the| +40|1|7690|776.13|lets use fluffily carefully final deposits. blithely ironic instructions sublate against the furiously final ideas; slyly bold courts x-ray silent foxes. regular foxes wake blithely. slyl| +40|6|1704|565.82|riously furiously silent asymptotes. final deposits cajole blithely ironic requests. furiously special pains into the blithely final instru| +40|1|4521|374.71|ptotes haggle. slyly even requests nag fluffily silent packages. blith| +40|6|6617|196.64|he slyly unusual epitaphs? ironic deposits at the furiously unusual instructions thrash blithely requests. requests are carefully blithely pending waters.| +41|2|9040|488.55|ss the dinos wake along the blithely regular theodolites. foxes cajole quickly ironic, final foxes. blithely ironic packages haggle against | +41|8|5946|391.81| slyly slyly regular requests. final deposits sleep fluffily. blithely bold instructions detect carefully. blithely pending requests are furiously ironically final ideas. regul| +41|4|1550|916.55| the blithely final ideas. furiously regular asymptotes could cajole furious| +41|10|560|37.59|special pinto beans against the unusual accounts cajole slyly final foxes. close, ironic| +42|3|2893|716.81|requests nag. furiously brave packages boost at the furiously even waters. slyly pending ideas nag carefully caref| +42|9|2927|709.06|g dugouts. carefully careful ideas are fluffily. carefully final pinto beans snooze. ironic deposits wake evenly along | +42|5|3500|200.00|against the ironic, ironic forges. slyly final deposits wake blithely. ironic courts sleep furiously ab| +42|1|3662|29.46|es sleep slyly among the slyly final requests. bold theodolites use silently against the final foxes. carefully pending requests use furiously. dogged, unusual asymptotes use | +43|4|3211|805.78|gular accounts. bold theodolites nag slyly. quickly express excuses use blithely. blithely even ideas boost fluffily! blithely unusual ideas detect bli| +43|10|6770|493.19|ing to the quickly even theodolites. quickly bold excuses haggle. sometimes unusua| +43|6|9506|493.65|riously! slyly ironic sauternes affix. ironic theodolites sleep furiously about the express packages. slyly ironic deposits are blithely against the regular package| +43|2|3232|307.12|counts: express, final platelets use slyly bold ideas. ironic theodolites about the blithely s| +44|5|486|164.22| final notornis throughout the unusual pinto beans are about the special accounts. bold packages sleep fluffily above the| +44|1|5310|114.37|quests. quickly unusual requests against the carefully final somas detect slyly bold a| +44|7|3534|383.01|r the pending pinto beans! requests wake furiously after the special deposits. silent deposits mold quickly along the express, special | +44|3|4798|833.15| run. ironic, special dolphins according to the even, ironic deposits haggle carefully alongside of the carefully regular excuses. regular frays haggle carefully ironic dependenc| +45|6|1685|919.63|he doggedly final accounts; carefully regular packages cajole idly regular idea| +45|2|5202|877.29|ngage blithely after the final requests. bold accounts sleep blithely blithely express dependencies. pinto beans through the carefully regular hockey players wake| +45|8|5669|532.70|es play carefully doggedly unusual requests. bold grouches against the furiously ironic dugouts sleep furiously qu| +45|4|1872|155.32| ironic, even pinto beans. bold theodolites haggle after the furiously ironic accounts. slyly bold courts| +46|7|4171|244.65|lly quiet instructions. furiously express requests among the final ideas cajole carefully bold waters. furiously regular pac| +46|3|8518|106.80|e unusual instructions shall have to detect slyly blithely ironic foxes. bold requests impress silent foxes. ironic, quiet realms haggle quickly pending, express pinto be| +46|9|7225|14.78|ously about the fluffily pending accounts. fluffily even dugouts are quickly slyly express platelets; quickly bold pearls sleep slyly even instructions. furiously ironic packages poach quic| +46|5|1381|985.88|ending platelets are carefully regular accounts. fluffily even accounts against the dependencies nag carefully final, | +47|8|6989|292.52|even ideas. blithely final requests boost blithely. final, ironic instruct| +47|4|4458|539.47|; finally enticing theodolites cajole enticing, silent warhorses! slyly bold pains c| +47|10|2896|74.54|grate final asymptotes. pending requests kindle carefully final frets. ironic deposits above the slyly e| +47|6|5873|296.63|after the regular dependencies. final, bold pains sleep quickly pend| +48|9|5052|611.16|posits are blithely blithely final foxes. blithely even deposits haggle fluffily express requests. furiously final theodolites use sl| +48|5|9451|191.36|ckages cajole never even, special foxes. regular dependencies wake after the blithely ironic instructions. thinly ironic reque| +48|1|5564|668.19|al pinto beans. furiously final frays use slyly according to the ironic theodolites. regular ideas cajole furiously after the slyly even deposits. | +48|7|1719|606.16|forges lose. packages cajole regular, bold accounts. never ironic accounts may promise about the permanently bold deposits. always express requests cajole fluffily regular c| +49|10|9056|35.11| bold deposits? final, bold pinto beans are furiously slyly regular packages. sly| +49|6|6646|908.15|ts sleep across the fluffily final deposits. carefully express accounts around the regular, express excuses x-ray inside the ironic theodolites. expre| +49|2|5336|713.25|ld accounts. furiously blithe waters use furiously blithely idle dependencies. pending deposits along the permanently re| +49|8|597|812.62|n foxes snooze furiously. courts integrate never. carefully unusual requests are carefully. quickly ironic deposits ha| +50|1|1832|565.54|liers above the dolphins dazzle across the regular foxes. furiously regular packages haggle furiously blithely ironic grouches. ironic, even accounts haggle pending, furious instruction| +50|7|43|690.87|aggle daringly along the close, express deposits. final requests snooze carefully carefully bold deposits. carefully unusual ideas doze furiously after the furious| +50|3|6160|301.06|arefully ironic requests use. furiously pending waters play carefully carefully regular platelets. sly requests cajole furiously slyly regular pinto beans. bold packages boost fluffily. furiously i| +50|9|2104|107.17|t blithely unusual theodolites. quickly final accounts affix fluffily regular requests. c| +51|2|837|310.74|ly dogged, regular dependencies. express, even packages are | +51|9|7318|85.03|al foxes. carefully ironic accounts detect carefully-- slyly even accounts use. furiously final platelets shall haggle sometimes after the blithely regu| +51|6|138|728.95|requests according to the carefully unusual deposits promise slyly ironic packages. slyly ironic dependencies are accordin| +51|3|8062|901.04|le ruthlessly furiously slow requests. fluffily slow depende| +52|3|6533|54.92|efully. slyly special deposits haggle along the quick deposits. slyly pending requests use quickly packages. final, final dolphins doubt according to the quickly unusual excuses| +52|10|1937|210.44|s. never even asymptotes nag carefully! regularly unusual foxes along the unusual requests haggle accounts. fluffily express pinto | +52|7|4084|628.53| deposits wake slyly pending asymptotes. ironic asymptotes haggle. blithely ironic requests are qui| +52|4|5524|424.93|cial, ironic packages. even dolphins boost. slyly final deposits integrate. final sheaves along the silent excuses use at the slyly close foxes; bold accounts are finally even packages. ironi| +53|4|6443|192.78|carefully ironic accounts. blithely bold deposits detect furiously against the flu| +53|1|5319|563.44|ly. fluffily final pearls boost carefully. special sauternes nod furiously even instructions. carefully regular dependencies across the slyly regular deposits| +53|8|8200|388.08|fully requests. furiously final accounts cajole express, regular pearls. special deposits wake fluffily express accounts. quic| +53|5|6929|224.83|xes. carefully ruthless asymptotes impress slyly. fluffily final deposits sleep against the ideas. slyly final packages wake. pending, express packages sleep quickly.| +54|5|2515|686.51|ly along the packages. blithely close pinto beans are blithely alongside of the unusual packages. carefully even platelets boost alongside of the even foxes. ironic de| +54|2|7079|798.98|he carefully unusual packages wake according to the ironic dolphins. permanently regular sheaves nag quickly. regular, ironic| +54|9|2386|23.78|kly ironic foxes. final instructions hinder doggedly. carefull| +54|6|536|259.24| furiously along the fluffily regular requests. carefully unusual accounts use fluffily final platelets. pending deposits integrate furiou| +55|6|7874|611.04|ly special packages. furiously even warhorses integrate. silen| +55|3|8460|236.27|round the special, bold asymptotes cajole alongside of the instructions. qui| +55|10|8278|134.62|gedly silent pinto beans! furiously regular sentiments was furiously across the silent pinto beans. pending warthogs along the slyly | +55|7|1289|130.33|ut the blithely final requests. requests nag blithely. | +56|7|241|855.39|nto beans. finally regular sauternes are. carefully bold deposits according to the blithely express requests wake carefully ironic excuses? furiously final deposit| +56|4|9104|54.79|tructions above the blithely pending foxes cajole blithely furiously even sentiments. special, exp| +56|1|1330|52.29|xpress instructions haggle furiously regular deposits. quickly unusual packages sleep furiously final pinto| +56|8|5799|926.25|ades grow around the dependencies. carefully special ideas cajole furiously across the blithely express requests. unusual tithes are caref| +57|8|2972|123.11| asymptotes use carefully furiously final deposits. quickly regular deposits are furiously slyly ironic requests. blithely even excuses haggle: blithely special ideas| +57|5|4721|411.08|instructions. quickly unusual deposits about the furiously special ideas believe among the furiously bold theodolites. unusual, even ideas nag: slow, special theodolites hagg| +57|2|3788|211.66|ly according to the ironic requests-- slyly final accounts print carefully depths? pending, unusual accounts solve | +57|9|4583|137.68|ts. blithely bold theodolites can boost carefully carefully even instr| +58|9|4328|542.52|ven deposits wake requests. quickly bold platelets sleep furiously after the ironic requests. even accounts haggle quickly bold | +58|6|4307|448.31|quickly carefully ironic foxes. bold platelets nag furiously regular packages. slyly specia| +58|3|4136|512.24|packages cajole slyly quickly pending depths. special, bold realms cajole slyly. slyly ir| +58|10|9689|25.09|long the unusual, express asymptotes. ironic ideas boost bold, special deposits? ironic foxes among the fin| +59|10|8374|357.22|c decoys. carefully even pinto beans wake slyly alongside of the express accounts. regular grouches haggle.| +59|7|4226|80.98|lar packages. regular depths use slyly after the fluffily regular packages; theodolites around the furiously ironic asy| +59|4|99|598.55|he special pinto beans. fluffily even accounts cajole. fluffily regular foxes haggle among the| +59|1|8184|45.50|ependencies. ironic dependencies wake carefully according to the blithely bold packages. quickly unusual ideas about th| +60|1|6642|800.72| blithely. slyly final realms alongside of the excuses use quickly blithely bold foxes. final theodolites are slyly after the slyly regular excuses. never thin foxes about | +60|8|5017|314.81| even pinto beans wake carefully. quickly regular deposits hinder along the furiously regular pack| +60|5|148|504.10|s use fluffily. furiously regular deposits boost furiously against the even instructions. blithely final platelets wake. carefully pending asymptotes sleep blithely. regular, s| +60|2|5792|92.64|s the carefully pending deposits. slyly regular pinto beans against the furiously regular grouches lose carefully around the enticingly final ideas. furiously express packages cajole bold pa| +61|2|1540|858.64| could have to use upon the packages. fluffily special packages integrate slyly final theodolites. pending warhorses wake quickly after the blithely final fo| +61|10|9170|771.26|ly. pinto beans sleep blithely about the patterns. slyly final accounts wake according to the furiously bold requests. slyly regular packages wake according to the ironic packages. requests acros| +61|8|4762|633.74|final theodolites haggle. fluffily express ideas about the silent theodolites cajole ideas; fluffily special instructions are accordin| +61|6|7312|153.74|gly final instructions. pending theodolites will wake furiously. slyly bold instructions run. furiously special foxes cajole f| +62|3|1780|692.42|s around the even ideas cajole furiously somas. silent asym| +62|1|5896|348.82| final accounts. furious deposits wake slyly. idly regular packages haggle blithely pending grouches. ironic accounts boost blithely. carefully express pa| +62|9|9127|620.08|totes. unusual requests after the unusual accounts sleep fluffily bold notornis. slowly careful requests use according to the final ideas. pinto beans sleep. foxes are furiously furiously pe| +62|7|9542|255.78|lly express requests haggle carefully. idle, pending pinto beans are furiously regular excuses. quickly sly attainments are furiously; even accounts are slyly quickl| +63|4|1804|498.84|leep bravely. final accounts nag. forges sleep against the slyly ironic pa| +63|2|1998|509.16|yly express theodolites. slyly bold ideas sleep furiously accordi| +63|10|6839|274.15| among the carefully ironic accounts. carefully even accounts against the regular, final deposits detec| +63|8|6325|463.69|arly express accounts. express, unusual escapades haggle. special packages must wake. express, regular requests sleep furiously ironic packages| +64|5|5567|228.61|y even instructions. unusual requests serve slyly. special foxes sleep quickly. fluffily ir| +64|3|4542|398.92|. quickly final ideas cajole carefully among the blithely silent requests. sometimes ironic accounts nag furiously against the pending instructions. f| +64|1|9110|602.65| ironic accounts are carefully carefully final accounts. slyly ironic packa| +64|9|2064|25.77| quickly regular ideas. carefully final requests snooze carefully regular, regular instructions. stealthily final pi| +65|6|2918|846.26|inal, even foxes cajole. furiously final dolphins hang quickly ironic foxes. furiously special packages alongside of the bold foxes solve above the carefully final instructio| +65|4|1779|393.63|ully after the quickly regular ideas. ironic, final multipliers above the carefully bold deposits breach slyly furiously express deposits. unusual accounts haggle carefully idea| +65|2|2054|503.10|e express excuses. ironic, even accounts across the reg| +65|10|2188|288.73|lent requests nag quickly. blithely silent platelets haggle ironic accounts. slyly bold instructions boost carefully final accounts. carefully even dependencies must nag blithely; qui| +66|7|3077|809.13|nod carefully besides the furiously final theodolites. slyly final requests haggle. furiously silent excuses detect quickly. ironic deposits detect above the furiously final | +66|5|1076|785.75|its across the blithely regular theodolites wake furiously among the furiously regular accounts. pains are slyly care| +66|3|2568|447.08|ously even accounts boost slyly daring requests. even, regular realms kindle blithely. unusual, ironic ins| +66|1|296|797.27|s nag enticingly outside the furiously final foxes. final accounts haggle fluffily accord| +67|8|9923|306.37|ly according to the quickly ironic requests. express instructions after the slyly even instructions x-ray blith| +67|6|7908|546.75|furiously express dolphins integrate carefully regular notor| +67|4|3368|625.62|le slyly regular requests: regular platelets wake quickly across the quickly regular accounts. reg| +67|2|5826|397.34|en, ironic deposits affix quickly unusual requests. busily ironic accounts are finally never even sauternes. ironic depos| +68|9|3444|31.37|es impress furiously pending packages. always silent instructions above the fluffily bold packages haggle slyly blit| +68|7|6762|5.16|lithely. carefully even grouches along the bold deposits might sleep slyly requests. blithel| +68|5|8300|80.86|nooze according to the furiously even ideas. blithely regular accounts wake blithely. furiously regular Tiresias cajole regular deposits. regular theodolites eat alongside of the| +68|3|5399|683.59|. finally final pinto beans play carefully unusual requests. never pending accounts are. regular, final theodolites wake furiously excuses. special request| +69|10|6197|694.24|eep across the packages. regular, final foxes boost fluffily regular pinto beans. packages sleep along the final requests. bold, unusual packages cajo| +69|8|8235|846.49|nt fluffily. carefully ironic instructions wake. blithely express foxes cajole slyly. unusual requests sleep quickly. final packages affix slyly according to the spec| +69|6|9294|386.96|ar packages. blithely regular dependencies are dolphins. slyly ironic excuses nag quickly pending, regular ideas. furiously special sheaves haggle. close, regular pinto beans about the slyly bold| +69|4|7017|344.28|heodolites. unusual, regular requests boost slyly pending deposits. slyly daring instruct| +70|1|4536|348.27|ructions. blithely final packages cajole carefully after the express, even requests. furiously final theodolites cajole | +70|9|8063|452.80|y regular deposits nag about the carefully regular instructions; furiously express accounts along the final, express instruct| +70|7|2990|940.81|s deposits. unusual foxes are carefully according to the carefully even deposits. carefully ironic foxes cajole fluffily against the carefully pending deposits. slyly special depo| +70|5|9074|182.58|ions after the fluffily regular foxes wake above the furiously regular requests: slyly regular deposits wake slyly daringly even Tiresias. express, express deposits are. always unusual pa| +71|2|508|842.21|es cajole carefully around the furiously pending instructions. | +71|1|8329|239.57|ins sleep carefully slyly express accounts! quickly even accounts boost carefully about the carefully regular excuses. dogged, even dolphins against the sometimes ironic packages believe bl| +71|10|6768|744.67|ructions. daring requests solve carefully about the furiously pending pinto| +71|9|5179|329.13|usly at the packages. blithely regular deposits haggle regular packages. quickly special theodolites at the blithely ironic instructions wake| +72|3|9855|497.26|tithes. quickly pending foxes haggle enticingly according to the accounts. accounts detect slyly: final packages wake. fina| +72|2|9346|41.04| pending instructions before the even, silent dep| +72|1|2654|762.61|nusual packages: blithely bold Tiresias sleep furiously. slyly brave accounts according to the final, | +72|10|4526|154.47|use across the never ironic packages. express, regular accounts above the pending, fluffy deposits are carefully across the slyly even pinto be| +73|4|9873|947.99|tes use pending packages. final foxes wake final, unusual packages. blithely blithe ideas haggle sometimes slyly express accounts. express instructions nag furiously quickly| +73|3|7729|920.66|ecial accounts sleep according to the slyly sly accounts. slyly express instructions nag. accounts cajole furiously quickly even foxes. furiously regular requests wake. carefully even frets haggle | +73|2|5327|108.96| beans are furiously between the regular ideas! unusual pinto beans use. furiously silent requests against the carefully even somas wake care| +73|1|3928|309.57|longside of the blithely final ideas. carefully ironic courts sleep along the enticingly pending requests. fluffily regular accounts use fluffily bold ideas. slyly ironic packa| +74|5|3128|345.92|ic theodolites. express deposits haggle blithely pending packages. quickly express foxes could are slyly. deposits sleep deposits. final dependencies sleep ab| +74|4|2479|930.97|o beans sleep dependencies. regular accounts use blithely asymptotes. u| +74|3|9473|496.36| haggle carefully alongside of the regular requests. slyly regular accounts belie| +74|2|6234|849.66| slyly regular foxes. silent accounts integrate. even deposits are quick| +75|6|7086|624.39|sits are furiously fluffily even courts. furiously pending requests are blithely. pending, regular accounts play carefully slyly unusual platelets. blithely final requests against the ru| +75|5|6308|759.36|refully ironic dependencies. pinto beans use according to the packages. regular platelets wake around the blithely p| +75|4|9080|433.59|sits. permanent packages breach. carefully final waters wake. bold, pending foxes haggle furiously evenly express instructions. even deposits about the final| +75|3|5439|884.01|ding excuses snooze special accounts. tithes alongside of the regular dep| +76|7|6754|494.83|gular accounts solve. ironic deposits sleep slyly even packages. slyly pending accounts detect slyly express accounts. ironic forges can play furiously carefully express fox| +76|6|2009|108.97|n packages. blithely even accounts sleep carefully furiously ironic accounts. carefully express requests| +76|5|6371|552.38|ts use against the quickly ironic ideas. quickly even deposits are carefully a| +76|4|7986|252.03| packages across the furiously ironic platelets cajole across the regular, ironic accounts. carefully enticing accounts among the blithely regular instructions detect regular pinto be| +77|8|552|254.92|e after the carefully pending packages. carefully even dependencies cajole pending | +77|7|8170|875.83|xcuses. blithely even foxes use fluffily. blithely even requests use. slyl| +77|6|8541|936.13|e slyly express instructions haggle about the sometimes regula| +77|5|1713|402.14|the even ideas kindle after the requests. regular theodolites cajole carefully about the blithely final ideas. carefully even dependencies at the flu| +78|9|9915|729.94|around the special excuses. furiously even deposits serve boldly according to the platelets. carefully express accounts at the blithely unusual pinto beans sleep furiously against the u| +78|8|7246|577.23|regular dependencies cajole doggedly ironic accounts. bold theodolites doze about the accounts. quickly final requests boost slyly final asymptotes. carefully final dolphins ha| +78|7|1801|434.34|nts kindle furiously according to the even packages. blithely ironic platelets are slyly silent foxes. final, final packages would sleep. pinto beans a| +78|6|9599|382.82| carefully special theodolites cajole among the quickly even asymptotes. foxes wake blithely across the carefully | +79|10|4248|765.34|nusual, express asymptotes wake furiously. ironic pinto beans detect above the carefully express theodolites: even, dogged instructions nag. spe| +79|9|465|28.33|uriously special frays cajole across the finally ironic pinto beans. ironic accounts sleep blithely. fluffily silent accounts are slyly at the slyly unusual ideas. even deposits nag slyly | +79|8|3309|880.23|tect final, thin accounts? furiously ironic accounts boost regular deposits. carefully ironic attainments sleep. furiously special ins| +79|7|8627|891.18|r dolphins grow blithely against the slyly ironic packages. deposits about the regular, ironic decoys are slyly around the carefully regular packages. slyly pending excuses sle| +80|1|8893|127.65|ld accounts detect carefully. carefully bold courts along the regular deposits could have to affix ca| +80|10|2243|775.79|endencies. bold, regular pinto beans wake furiously above| +80|9|5385|945.72|cial asymptotes believe after the blithely unusual deposits. furiously silent pinto beans cajole quickly inside the slyly even deposits. regular, f| +80|8|4034|797.05|ptotes cajole carefully. express ideas cajole carefully even somas. final pinto beans print fluffily across the | +81|2|1605|550.29|es haggle blithely fluffily final requests. furiously regular foxes use. furiously unusual requests outside the furiously regular requests| +81|2|5923|220.23|the final, quick accounts are blithely above the s| +81|2|2942|409.73|accounts boost. fluffily unusual requests cajole fluffily slyly ironic requests. foxes cajole quick| +81|2|58|492.19| instructions boost furiously across the foxes-- final depo| +82|3|7793|697.31|he accounts cajole quickly after the even patterns. ironic platelets sublate regular, even asymptotes. quick courts affix according to| +82|3|7698|585.86|pinto beans. slyly express excuses haggle. blithely even pinto beans about the quick inst| +82|3|8268|604.25|e after the carefully even theodolites. regular, pending accounts boost. quickly final asymptotes haggle slyly. requests use final, bold pinto beans. bold, ruthle| +82|3|5532|900.07| slyly? fluffily special dependencies haggle among the slyly special requests. regular, bold packages after the blithely ironic packages are slyly ironic packages. slyly final deposits w| +83|4|3010|745.51|l foxes along the bold, regular packages integrate carefully express courts! final excuses sleep carefully ironic| +83|4|8200|399.64|y final platelets are carefully carefully special platelets. carefully ironic requests wake blithely alongside of the slyly even accounts. bold, regular requests sleep | +83|4|5974|657.22| even packages boost furiously. slyly regular gifts above the accounts are quickly express packages. slyly pending deposits besides the express, even asymptotes haggle after the ironic ins| +83|4|3890|24.73|deposits. carefully even dependencies across the dependencies haggl| +84|5|5711|233.61|arefully final platelets cajole blithely; quickly final accounts use furiously. furiously reg| +84|5|208|469.80|carefully express dolphins nag about the slyly bold requests. slyly even packages wake among the furiously special attainments.| +84|5|2909|969.44|silent requests cajole slowly bold ideas. special, special deposits according to the always silent packages are against the furiously silent packages. even, blithe accounts sleep slyly across | +84|5|903|707.77|gly regular dependencies boost. slyly even accounts sleep. furiously final hockey players wake carefully with the reg| +85|6|2628|608.77|xes wake furiously after the carefully even platelets. blithe theodolites are furi| +85|6|118|917.83| against the even deposits. furiously bold ideas along the furious requ| +85|6|2074|491.20|encies-- slyly regular requests about the quiet accounts detect quickly at the | +85|6|8289|73.81|s cajole slyly along the slyly special accounts. regular, special deposits wake. furiously special foxes boost. blithely even packa| +86|7|806|65.98|ackages. blithely pending accounts are slyly furiously pending theodolites. furiously eve| +86|7|2773|250.04|ding accounts. slyly special requests will have to affix carefully along the furiously unusual packages. regular theodol| +86|7|5546|816.53|s. slyly final requests wake. furious deposits must wake blithely among the blithely ironic instructions. special hockey players try to are bli| +86|7|1418|332.65|press theodolites sleep carefully about the blithely unusual requests. quickly final deposits breach slyly | +87|8|5679|688.33|t the carefully regular asymptotes. blithely stealthy pinto beans within the furiously expres| +87|8|1272|435.42|ronic foxes sleep along the special foxes. final ideas wake quickly about the carefully special theodolites. blithely ironic packages are blithely. regular, regular pint| +87|8|9041|617.20|furiously final deposits. furiously special dependencies solve across the regular, special ideas. carefully silent requests haggle furiously after the special, specia| +87|8|1892|868.60|arhorses are. unusual requests use blithely furiously final ideas. final requests sleep theodoli| +88|9|6116|334.58|ect furiously around the regular deposits. special, final platelets boost furiously. blithely unusu| +88|9|395|71.50| the regular accounts-- furiously even accounts use quickly after the regular, regular deposits. furiously e| +88|9|9979|81.82|f the regular, regular requests believe fluffily along the final, quiet decoys. furiously even accounts cajole. carefully express requests wake quickly among the ideas. quickly silent | +88|9|276|821.43|gular pinto beans. slyly pending excuses breach blithely express accounts. thin deposits sleep slyly around the even accounts; fluffily busy patterns kindle. slyly final deposits along the | +89|10|3430|744.87| integrate slyly dolphins. bold, final frets use beside the carefully even accounts. slyly close dependencies sleep quickly carefully final pinto beans. foxes promi| +89|10|8599|776.53|ress packages use furiously. furiously regular packages thrash blithely about the slyly pe| +89|10|7876|417.61|nstructions: furiously even requests are quietly unusual accounts. regular requests are after the blithely regular deposits. sl| +89|10|924|920.02|ickly unusual asymptotes after the slyly unusual accounts are carefully doggedly ironic accounts. even, final accounts use furiousl| +90|1|8037|409.38|eas. unusual, pending packages boost quietly final accounts. slyly final packages serve. slyly even instructions sleep carefully. quickly even foxes wake quickly. | +90|1|9683|498.43| accounts! fluffily regular deposits x-ray about the unusual, final packages. furiously final deposits alongside of the caref| +90|1|7849|666.13|carefully ironic accounts are around the slyly bold asymptotes. carefully regular packages use furiously. ironic platelets affix carefully final accounts-- fluffily final pinto beans across the fina| +90|1|7629|50.84|onic requests wake fluffily unusual packages. furiously even frays after the daringly pending requests wake furiously alongside of the bold requests. fluffily ironic ideas nag. ironic,| +91|2|7986|528.64|luffily final instructions. furiously unusual foxes haggle | +91|3|3257|906.20|ackages cajole slyly. blithely bold deposits cajole. blithely | +91|4|483|823.21|n: slyly ironic foxes nag blithely according to the furiously bold foxes. regular, regular accounts a| +91|5|1265|703.41| quickly silent deposits use attainments. final requests along the carefully ironic accounts wake blithely about the carefully ironic excuses. furiously bold excuses wake final, final ex| +92|3|9337|224.01| requests are slyly along the deposits. fluffy pains alongside of the deposits | +92|4|2246|985.03|jole enticingly regular asymptotes. carefully unusual pinto beans nag carefully ironic ideas. quickly un| +92|5|3199|91.63|ake carefully: carefully ironic requests sleep careful| +92|6|1044|854.89|l instructions are fluffily silently regular accounts. quickly final dolphins w| +93|4|3008|615.98|sits promise blithely fluffily special decoys. slyly regular packages along the slyly final deposits wake accord| +93|5|5275|376.47|ounts boost fluffily along the thinly regular realms. busily regular a| +93|6|3869|868.81|ly among the furiously silent accounts. closely regular pinto beans nag slyly! slyly e| +93|7|7188|805.90|y furiously bold pinto beans. express asymptotes was quickly. carefully final accounts affix slyly! platelets according to the ca| +94|5|5433|365.56| even excuses wake carefully. quickly unusual requests wake accounts. regularly pending packages are regular | +94|6|7784|358.08|ironic packages wake slyly carefully regular accounts. quickly regular warhorses against the blithely ironic packages haggle doggedly sly| +94|7|7232|478.94|y regular requests. carefully final asymptotes haggle carefully against the slyly unusual requests: blithely brave grouches are fu| +94|8|3261|824.08|quests. enticingly final accounts sleep fluffily. quickly express asymptotes around th| +95|6|5186|291.03|ites across the blithely pending theodolites do affix across the unusual, bold Tiresias. bold packages| +95|7|6552|456.36|tes; final, final accounts boost blithely ironic pinto beans. blithely ironic deposits cajole above the quickly pending requests? i| +95|8|367|987.22| express requests detect furiously. requests cajole carefully| +95|9|7379|973.74| above the furiously unusual deposits haggle ironic ideas. express, even packages haggle slyly slyly special asymp| +96|7|5739|202.06|re. slyly regular theodolites breach slyly even dinos. fluffily regular asymptotes haggle slyly. fluffily bold courts affix furiously. regular requests | +96|8|4942|571.30|e carefully. bold packages sleep against the furiously express requests. express foxes above the dependencies use quickly according to the slyly expres| +96|9|9985|672.29|ecial instructions-- blithely silent theodolites play. even, silent accounts sleep. blithely silent requests haggle final, f| +96|10|7250|587.08|efully ironic foxes. regular, final pinto beans boost above the express a| +97|8|6371|129.77|fluffily unusual accounts. slyly regular theodolites integrate furiou| +97|9|2390|458.34| carefully unusual pinto beans; even deposits detect furiously| +97|10|2618|239.34|al theodolites are daringly requests. warhorses sleep blithely requests. special accounts cajole slyly deposits. a| +97|1|4580|761.41| beans. carefully final deposits alongside of the carefully final requests haggle idly blithely ironic accounts. foxes cajole slyly against the ironic, special packages. furiously brave excuses boo| +98|9|9486|908.21|usly final deposits mold furiously above the even deposits. carefully ironic packages across the quickly regular dolphins are slyly according to the slyly even| +98|10|8550|657.16| sleep carefully. bravely bold somas may sleep pendin| +98|1|3443|139.00|gular pinto beans maintain quickly fluffily regular deposits. express requests sleep. even requests after the regu| +98|2|3759|811.55|iously. final, express packages are across the ironic dependencies. slyly thin ideas according to the even Tiresias detect furiou| +99|10|8487|438.38|lphins affix ironic packages. blithely ironic requests nag fluffily after the slyly ironic foxes. bold dependencies boost furiously. special, | +99|1|7567|496.93|es? permanently even excuses haggle quickly across the dependencies.| +99|2|7970|365.83|ending accounts cajole furiously. requests promise care| +99|3|2789|843.88|ending accounts. furiously sly packages above the carefully unusual dolphins sleep after the thinly even deposits. requests wake abo| +100|1|7885|490.61| accounts nag slyly against the bold excuses. pearls according to the fluffily ironic accounts haggle fluffily along the quickly final platelets| +100|2|2070|196.73| dolphins. bold deposits along the even theodolites sleep furiously about the final pinto beans. furiously unusual courts cajole about the carefully bold asymptotes. accounts integrate slyly entic| +100|3|4994|929.57| deposits. accounts are slyly regular ideas. slyly special pinto beans upo| +100|4|9688|22.00|uctions according to the carefully ironic deposits haggle carefully express ideas? packages across the quickly final requests c| +101|2|5589|305.40|blithely above the fluffily pending ideas. quickly quick accounts nod ruthlessly above the carefully pending packages. slyly s| +101|4|8263|218.71|fluffily final requests. carefully even packages wake quickly about the quickly ironic foxes. fluffily even requests hang quickly about the pending, final requests. sp| +101|6|6324|786.53|olites sleep quickly. slyly ironic theodolites affix. furiously bold accounts integrate among the pinto beans. final ideas hang slyly along the quickly regular packages. instructions cajole.| +101|8|55|612.09|beans against the carefully express ideas wake quickly along the quickly unusual requests. blithely regular accounts cajole fluffily. enticingly pending theodolites haggle furiously fluffily pendi| +102|3|1384|876.75|s wake quickly. carefully express deposits wake. silent, regular requests sleep slyly after the furiously ironic deposits. slyly unusual accounts cajole| +102|5|9137|332.71|telets are final, special deposits. silently ironic deposits wake. pending, eve| +102|7|9156|618.00| the unusual, ironic pinto beans. theodolites above the foxes sleep slyly car| +102|9|6942|231.02|tions haggle against the furiously ironic deposits. quickly final asymptotes haggle carefully. regular sentiments might cajole silent courts. blithely bold frays | +103|4|5913|905.88|e across the theodolites. carefully pending escapades haggle after the ironic theodolites. furiously pending ac| +103|6|7742|414.42|bout the bold, regular deposits; blithely even accounts are regular, even platelets-- carefully express accounts nag slyly pen| +103|8|5164|361.48|furiously thin deposits haggle blithely. blithely regular deposits above the carefully regular accounts are slyly carefully regular packages. silent, unusual| +103|10|429|605.20| theodolites cajole quickly above the asymptotes-- slyly special packages can haggle carefully blithely final instructions. unusual, regular ideas| +104|5|2368|946.39|packages. final packages wake enticingly. furiously regular asymptotes are always about the carefully regular deposits. slyly regular platelets cajole carefully. final pinto beans must pro| +104|7|6110|15.57|ending requests. carefully regular deposits use blithely. bold, ironic deposits wake slyly carefully specia| +104|9|6269|213.89| ideas against the final accounts wake slyly regular notornis. final deposits haggle a| +104|1|3369|729.38|ong the foxes. foxes sleep quickly? carefully regular accounts sleep. special foxes boost quickl| +105|6|4602|27.75|lar pearls cajole never carefully even depths. blithely regular ideas are quickly. unusual asymptotes nod carefully carefully regula| +105|8|269|158.62| unusual courts eat pending excuses. ironic, ironic requests use. bravely | +105|10|8855|810.86|. slyly special depths sleep. bold packages engage furiously bold packages. fluff| +105|2|8519|904.17|ding to the furiously careful ideas. dogged theodolites wake fluffily among the slyly bold ideas. blithely brave warthogs above the slyly even theodolit| +106|7|8649|732.15| slyly ironic instructions are. bold, final accounts cajole slyly ironic pinto beans. fluffily ironic accounts around the quickly special requests use blith| +106|9|3144|779.68| final deposits along the slyly express theodolites cajole blithely after the ironic pinto beans. furiousl| +106|1|1411|310.40|al accounts impress. even instructions engage furiously final foxes. silently final deposits wake qui| +106|3|2297|281.98|inal packages. pending foxes sleep bold hockey players. courts across the blithely regular packages sleep fl| +107|8|7249|168.03|he fluffily even packages. slyly regular dependencies nag fluffily above the final, unusual foxes. final, pending foxes affix. furiously final deposits cajole quickly blithely| +107|10|4029|91.31|integrate. requests maintain quickly. carefully regular ideas about the instructions sle| +107|2|4667|372.94|uctions sleep doggedly final requests. express, final theodolites cajole fluffily furiously silent deposits. blithely regular requests cajole quickly regular instruction| +107|4|7912|474.77|fluffily across the final, bold accounts. quickly regular deposits grow carefully deposits. regular requests haggle blithely. slyly special platelets boost furiously care| +108|9|4149|558.85| of the quickly pending theodolites. fluffily unusual frays wake accounts. carefully even foxes wake slyly. carefully special pinto beans | +108|1|4898|241.47|lthily according to the fluffy deposits. furiously silent ideas according to the furiously special theodolites wake furiously a| +108|3|5534|626.89|instructions. blithely regular instructions according to the permanent foxes cajole blithely slyly fluffy foxes. slyly regular asymptotes cajole foxes. slyly unusual deposits | +108|5|3142|922.27|slyly express accounts are fluffily along the blithely unusual packages. pinto beans mold furiously. furiously bold instructions are blithely deposits. quickly special accounts detect t| +109|10|524|275.19|st the permanently final requests. carefully pending pinto beans haggle quickly slyly ironic dolphins. blithely bold deposits wake blithely. even requests cajole foxes. iro| +109|2|4470|992.21|ake furiously packages. blithely even foxes haggle furious| +109|4|8176|936.60|d the express accounts. even theodolites wake quickly up the furiously bold foxes. furiously regular packages use regular, bold| +109|6|7524|374.49|sual requests. final pinto beans cajole furiously. dependencies integrate slyly even excuses. blithely special requests wake around the slyly final pinto beans. furiously pending requests wake furi| +110|1|2782|566.46|ly sly deposits. regular deposits are; theodolites haggle furiously bold foxes. final pinto beans wake blithely furiously ironic depths. unusual, regular platelets cajole. final, ironic| +110|3|8914|183.47|yly even foxes. carefully regular requests doubt. pending, regular requests across the blithely final theodolites hag| +110|5|1160|932.08|ake blithely. furiously ironic accounts sleep fluffily| +110|7|4927|196.63|theodolites. instructions haggle even packages. waters impress furiously quickly express courts. furiously ironic depths nod quickly? ironic, ironic requests sle| +111|2|1890|321.97|st foxes. daring depths use above the furiously special ideas. ironic foxes among the carefully final theodolites are alongside of the regular depths. e| +111|5|9587|978.65|express instructions against the furiously final grouches haggle across the blithely ironic theodolites. slyly special dependencies in place of the carefully pending | +111|8|8069|745.33|thely bold requests. unusual packages sleep. quickly pending ideas nag furiously to the carefully final p| +111|1|2175|496.08|s around the daringly final pinto beans use furiously against the pi| +112|3|8509|111.42|unts. carefully ironic instructions are final, bold foxes. bold excuses run according to the unusual packages. theodolites cajole carefully according to the fluffily pending deposits? sly| +112|6|7652|783.52| carefully among the furiously final packages. regular instructions nag. slyly pending ideas hang fluffily blithely ironic instructions. ironic deposits haggle except the quickl| +112|9|4835|913.75|ach slyly special dependencies. furiously ironic pinto beans sleep slyly pen| +112|2|1211|815.88|l requests integrate furiously. quickly quiet packages are carefully regular accounts. regular packages eat quickly express, ironic sheaves. quickly dogged accounts o| +113|4|9981|396.26|ithely express pains lose bravely fluffily pending foxes. blithely ironic sauternes cajole q| +113|7|3804|860.68|ully about the carefully even accounts: thinly even foxes are carefully. | +113|10|522|981.41| warthogs use quickly alongside of the furiously unusual requests. final deposits are blithely acro| +113|3|4692|141.48| blithely unusual gifts snooze against the quickly ironic packages. regular packages across the carefully regular packages bo| +114|5|7146|447.24|ideas will nag regular accounts! carefully final requests cajole furiously quickly final tithes. furiously express instructions a| +114|8|3062|555.12|ts. furiously regular requests run carefully thin decoys. ironic platelets sleep alongside of the slyly silent deposits. reg| +114|1|4519|382.87|nts. ironically express dolphins dazzle blithely. special instructions wake carefully along the ideas. quickly special dolphins sleep. furiously pendi| +114|4|2113|570.79|o beans sleep among the ironic excuses. furiously even sheaves are. never regular instructions nod.| +115|6|1817|82.84|uffily final accounts integrate furiously along the carefully busy excuses. slyly even asymptotes doubt quickly. fluffily thin theodoli| +115|9|983|867.45|kly. requests nag after the blithely bold packages. express requests cajole theodolites. blithely express requests sleep after the furiously regular accounts. fluffily r| +115|2|7781|861.93|lyly ironic pinto beans affix alongside of the furiously even ideas: quickly bold warhorses sle| +115|5|3002|81.52|efully after the quickly regular deposits. daringly pending ideas sleep even ideas. silent, re| +116|7|6580|705.50|iously. slyly regular requests detect slyly. carefully bold packages sleep furiously carefu| +116|10|4975|413.86|tions. regular excuses detect. ideas haggle slyly about the slyly ironic courts. ironic foxes solve. ideas affix fluffily after the special, even dependencies. final platelets according| +116|3|8679|866.56|aphs cajole blithely regular accounts. even packages doubt; bold instructions boost quickly. fluffi| +116|6|5632|37.30|ccounts about the special packages nag across the carefu| +117|8|5906|706.51|into beans sleep carefully blithely bold packages. even, bold instructions use across the carefully e| +117|1|1986|322.08|eposits. special pinto beans use fluffily across the furiously regular pinto beans. furiously regular epitaphs nag fluffily packages. special accounts a| +117|4|2577|761.86|riously. doggedly unusual ideas boost blithely blithely regula| +117|7|4762|552.88| run furiously ironic accounts. slyly ironic deposits haggle slyly fluffy requests. flu| +118|9|694|744.73|refully slow requests. requests against the special pac| +118|2|6326|325.61| packages. express, final frays affix quickly above the final asymptotes. carefully regular requests doubt quickly f| +118|5|7806|283.27| accounts affix carefully. regular, regular packages among the brave, pendin| +118|8|4951|976.55|s orbits. even asymptotes above the instructions wake fluffily according to the sly, final excuses. express deposits across the blithely ironic depend| +119|10|2307|473.64| blithely unusual dolphins boost busy, express ideas. regular requests use carefully furiously ironic deposits. carefully regular packages would sle| +119|3|1452|676.92|ular instructions was slyly. furiously bold gifts boost f| +119|6|4955|488.93|ias are along the express requests. fluffily pending ideas nag idly against the fluffily bold instructions? foxes cajole quickly. slyly special deposits haggle slyly e| +119|9|583|782.47|yly pending requests-- carefully special instructions haggle carefully even instructions. blithely regular theodolites detect blithely final ideas. blithely ironic deposits among the sl| +120|1|4976|201.21|inal, regular pinto beans haggle carefully! ironic ideas unwind among the slyly regular theodolites. regular platelets kindle blith| +120|4|7744|365.79|l, special escapades! ideas sleep slyly instructions. carefully bold requests are. even accounts cajole. final accounts use slyly | +120|7|5329|249.61|s cajole blithely. carefully bold requests believe blithely? brave accounts above the pending, dog| +120|10|3102|566.34|ctions; realms beside the blithely final theodolites unwind blithely packages. regular dolphins sleep carefully-- carefully express accounts wake quickly. pending depths use never courts.| +121|2|9741|147.45|ly according to the carefully regular asymptotes. silent excuses cajole carefully against the never silent instructions. furio| +121|6|4246|850.42|usly final instructions. theodolites are according to the permanently ironic accounts. carefully pending accounts haggle about the pending instructio| +121|10|7670|449.39|carefully daring packages. express packages use carefully about the quickly unusual packages. special ideas along | +121|4|8709|655.22| detect carefully along the carefully even pinto beans. gifts haggle: ideas sleep ar| +122|3|1019|451.29| blithely regular accounts. blithely final pains sleep besides the blithely final warhorses. furiously unusual requests haggle furiously| +122|7|2490|637.28|efully special excuses grow slyly unusual packages. carefully quiet as| +122|1|4957|650.84|quests. quickly bold requests nag across the furiously ironic accounts. ironically express instructions detect slyly carefully ironic requests. even, un| +122|5|2083|739.25|counts. unusual requests alongside of the regular requests are carefully stealthy instructions? regular sauternes cajole. final theodolites breach carefully at the blithely final idea| +123|4|9881|107.03|fully bold deposits detect slyly pending instructions. slyly special ideas detect blithely. slyly fluffy instructions hinder| +123|8|5638|818.19|thely even pinto beans. furiously regular asymptotes affix furiously. regular, ironic tithes integrate quickly. blithely regular requests breach finally. decoys alon| +123|2|2692|217.01|he ironic accounts nag fluffily after the bold, pending theodolites. blithely final ideas sleep carefully according to the blithely ironic foxes. regular requests are. furiousl| +123|6|5311|149.65|eposits cajole according to the carefully pending packages. furiously final epitaphs solve alongside of the even requests| +124|5|7102|901.98|ily accounts. furiously busy theodolites above the deposits thrash above the blithely final foxes. express instructions nod slyly furiously busy packages. special asymp| +124|9|3969|908.64|l epitaphs. packages cajole among the furiously regular requests. closely| +124|3|9295|882.54|s along the accounts poach quickly ironic deposits. even, final excuses thrash carefully about the express, special pains. carefully careful accounts breach slyly| +124|7|9416|822.78|ously. theodolites affix around the slyly bold packages. even, ironic packages are carefully pains. furiously unusual requests sleep blith| +125|6|2263|358.45|e. ironic, regular requests cajole fluffily along the even ideas. final ideas wake blithely. blithely bold | +125|10|8276|668.65|nd the carefully express requests. slyly regular requests haggle. blithely unusual platelets solve fluffily fluffily regular| +125|4|2600|455.70|ounts. thinly special accounts cajole carefully. even, special accounts after| +125|8|5546|806.66| to the unusual courts are deposits! final, final pinto beans solve slyly. ironic accounts boost fluffily. furiously pending d| +126|7|2647|221.89|lyly final pinto beans across the regular, even courts use slyly slyly pending braids! unusual requests along the furious| +126|1|2373|194.38|fter the ideas. blithely daring sheaves print furiously among the blithely final packages. iron| +126|5|1532|451.61|refully alongside of the quickly bold excuses. enticing, bold | +126|9|5458|929.43|leep to the furiously special accounts. furiously final courts | +127|8|7658|712.33|al pinto beans! slyly ironic excuses boost after the packages. express foxes integrate carefully. pending, regular theodolites | +127|2|1467|237.98|regular accounts! quickly ironic packages haggle according to the accounts. carefully ironic | +127|6|8281|3.14|ts above the furiously pending asymptotes cajole after the deposits. slyly ironi| +127|10|8894|73.42|fter the sometimes special courts sleep about the slyly unusual reque| +128|9|6982|425.29|ironic asymptotes. fluffily ironic packages use. ironic, regular ideas are in place of the quickly silent deposits. final, bold gifts across the ironic, regular pac| +128|3|7602|224.49|xcuses. blithely unusual theodolites use slyly carefully even warthogs. slyly even dugouts haggle slyly final, express pinto beans. furiously bold packages thrash requests? slyly unusual packages | +128|7|3766|947.16|arefully regular packages boost regularly. accounts are according to the blithely even dependencies. slyly silent accounts doubt slyl| +128|1|7023|875.78| furiously quickly regular pinto beans. always special requests are. quickly regular deposits are furiously. slyly unusual theodolites haggle evenly; furiously special deposits wa| +129|10|5721|129.69|ully express requests above the ironic, final requests cajole slyly along the quickly special packages. sl| +129|4|7242|200.26|es across the furious escapades wake quickly slyly e| +129|8|5299|330.59|final sentiments affix atop the silent foxes. busy pinto beans cajole. slyly final pinto beans haggle against the carefully expres| +129|2|1968|27.22|ealthy, ironic deposits. slyly ironic pinto beans are blithely pinto beans. blithely ironic | +130|1|4928|223.38|ths. slyly even theodolites detect according to the slyly final courts. carefully unusual deposits ar| +130|5|6909|275.58|lly unusual accounts try to boost along the special packages. furiously bold requests x-ray blithely ironic waters. slyly unusual orbi| +130|9|4850|442.81|ully regular deposits snooze. slyly silent foxes detect furiously furiously bold requests. slyly regular accounts breach. blithely bli| +130|3|7387|883.99|aggle furiously. even ideas hinder deposits. even, final ideas are. unusual theodolites after the special, express foxes haggle carefully pending accou| +131|2|3263|211.70|sits sleep quickly regular multipliers. slyly even platelets cajole after the furiously ironic deposits. slyly ironic requests should have to cajole: bl| +131|7|125|861.84|l accounts grow quickly-- slyly ironic requests haggle? quickly express pinto bean| +131|2|5138|572.43|grouches run with the carefully even packages. ironic, even deposits run slyly along the packages. special dependencies among the regular | +131|7|8945|613.09| are carefully along the quickly final theodolites. packages after the quickly pending package| +132|3|3092|687.29|y special decoys against the ideas affix against the sly| +132|8|1904|925.73|the regular foxes wake ironic deposits. ironic, special requests use blithely instructions! final requests hang. blithely regular deposits haggle. ir| +132|3|7441|357.06|ests. furiously unusual requests wake furiously. quickly unusual depos| +132|8|5303|353.06|ep blithely after the sly accounts. slyly express dolphins cajole amon| +133|4|5727|49.17|boost blithely across the ironic, regular instructions. packages use slyly unusual requests. bold accounts above the fu| +133|9|404|478.18|ly ironic requests run instead of the blithely ironic accounts? regular ideas use fluffily: even, express packages sleep abov| +133|4|4568|57.48|dolites. ironic accounts are blithely pinto beans. regular pinto beans haggle beneath| +133|9|2813|277.26|s. pending, final accounts haggle blithely furiously pending deposits! carefully unusual attainments integrate. blithely bo| +134|5|8879|848.14|lites. slyly final foxes after the bold requests cajole carefu| +134|10|9013|102.99|pendencies. furiously express warthogs cajole furiously ironic, regular asymptotes. bold deposits boost among the furiously even theodolites. regular instructions integrate carefully | +134|5|852|927.45| sleep unusual, express packages. unusual sentiments are furio| +134|10|6270|388.28| to the furiously pending deposits nag along the slyly express asymptotes. slyly silent accounts shal| +135|6|6940|465.82|ding foxes cajole. even dugouts haggle busily. fluffily pending packages about the express excuses boost slyly final packages. blithely express ideas cajole about the carefu| +135|1|2443|9.83|atterns. pending, special deposits are furiously. express, regular deposits integrate quickly. unusual gifts cajole blithely stealthily pending deposit| +135|6|7453|698.42|ven accounts. slyly final instructions nag slyly around the regular, unusual packages. slyly sp| +135|1|2771|306.43|old deposits. furiously express instructions boost. pending dolphins use requests. slyly regular packages cajole quickly final ideas. pending, regular ideas nag carefully even, express pla| +136|7|2237|548.19|ond the silent accounts haggle above the blithely regular packages| +136|2|6068|806.19|structions. ironic theodolites haggle according to the final, daring pearls. carefully ironic somas are silently requests. express pa| +136|7|8979|387.57|ans. express pinto beans wake carefully among the slyly ironic foxes: carefully final pinto beans haggle blithely. pending, final deposits promise furiously| +136|2|9617|525.81| across the carefully pending warthogs. close, regular packages are quickly after the never ironic foxes. accounts sleep quickly along the furiously regular re| +137|8|9057|302.26|slyly about the regular instructions. even, ironic theodolites use carefully around the even decoys. unusual, pending dolphin| +137|3|4078|441.11|packages. blithely unusual sentiments should are. furiously regular accounts nag quickly carefully special asymptotes! idly ironic requests dazzle bold requests. carefully expres| +137|8|467|371.85|ly special accounts detect carefully. furiously ironic deposits nag express packages. slyly quiet | +137|3|7850|187.31|atelets sublate fluffily. enticingly unusual packages boost according to the blithely ironic foxes. pending requests mold sly| +138|9|133|576.96|regular, final deposits maintain slyly even requests. regularly furious deposits use above the stealthy requests. ironic deposits are. carefully final frays are carefully. carefu| +138|4|2535|885.35|lar deposits. courts sleep carefully. furiously express ideas boost furiously after the final, regular foxes. furiously bold deposits are. express accounts haggle blithely. | +138|9|7907|119.83|epitaphs? quickly express foxes use pending accounts. special packages cajole blithely among the quickly unusual accounts? boldly ironic packages across the slyly ironic senti| +138|4|967|309.03|pendencies integrate against the unusual pains. carefully unusual theodolites wake quickly across the deposits. blithely regular deposits alongside of the carefully regular deposits| +139|10|2886|285.75|fully ironic requests according to the quickly final idea| +139|5|9255|684.61|ickly furiously regular excuses. boldly express deposits sleep. ideas nag above the silent dependencies. slyly regular packages wake furiously. requests are carefully. quickly final fox| +139|10|1042|972.23|gular, regular theodolites. regular asymptotes haggle carefully according to the permanently even deposits. slyly special account| +139|5|3285|690.00|xpress pains. quickly regular ideas after the special, bold excuses wake furiously final ideas. slyly bold accounts nag packages. ironically regular| +140|1|2379|501.05|of the silent, bold courts. slyly regular dependencies haggle. fluffily special deposits cajole carefully. quickly ironic depos| +140|6|3533|781.45|ayers. carefully ironic pinto beans nod carefully furiously regular pinto beans. slyly ironic requests after the carefully regular packages are about the blithel| +140|1|304|45.84|ing requests. carefully unusual foxes are final requests. slyly regular accounts wake permanently. quickly ironic theodolites hagg| +140|6|7346|429.52| special pinto beans wake carefully unusual warthogs! furi| +141|2|6776|293.63|fluffily unusual courts sleep. close pinto beans haggle quickly after the carefully ir| +141|8|1660|139.18|egular accounts. enticingly bold theodolites eat slyly across the never ironic platelets. theodolites wake bli| +141|4|7628|838.08|sly about the pinto beans. blithely ironic ideas sleep. foxes are quietly among the pinto beans. carefu| +141|10|90|810.68|e doggedly regular ideas. foxes haggle slyly. slyly regular theodolites across the carefu| +142|3|9219|897.49|thlessly special requests sleep blithely about the bold deposits. express, ironic instructions wake. final packages are blithely. deposits are carefully furiously even deposits. furiously regular a| +142|9|13|334.33|are blithely blithely brave requests. slyly regular theodolites are furiously. blithely ironic dependencies haggle blithely. furiously unu| +142|5|3076|860.55|gular requests about the pending packages wake furiously dogged accounts. th| +142|1|3858|854.08|efully special deposits. blithely bold pinto beans haggle. slyly final ideas boost blithely. finally special requests mold along the blithely express packages. entic| +143|4|7326|960.64|the slyly pending requests cajole quickly blithely regular platelets. even requests boost carefully. ironic, final instructions above the regular courts boost a| +143|10|3923|741.01|le quickly furiously silent ideas. carefully regular requests ar| +143|6|7152|772.24|fully furious accounts. final asymptotes cajole regular requests. carefully regular courts are quickly. slyly ironic ideas above the carefully regular requests wake| +143|2|1952|199.37|l accounts are quickly after the unusual packages. regular accounts wake among the quickly even accounts. even, ironic| +144|5|6295|457.37| pinto beans promise across the blithely bold packages. express, regular accounts play around the slyly silent deposits. specia| +144|1|494|849.96|uriously ironic pearls wake idly furiously even pearls. foxes impress slyly busily express requests. carefully slow somas wake quick| +144|7|1799|713.88|yly final requests. packages are. carefully daring accou| +144|3|5427|361.83| foxes integrate carefully. deposits cajole fluffily. pending deposits kindle slyly carefully regular packages. even, thin accounts according to th| +145|6|11|641.67| slyly regular packages are slyly carefully special dolphins. unusual braids use furiously about the final courts. slyly special| +145|2|3838|568.91|ss, final asymptotes are. furiously express accounts run. furiously express dependencies eat carefully blithely ironic theodolites. closely ironic foxes among the silent asymptotes cajole| +145|8|1344|422.60| dependencies. even patterns detect slyly after the ironic deposits. ironically even ideas wake slyly. even packages against the blithely express accounts haggle furiously carefully regular| +145|4|4332|894.57|are carefully above the quickly silent deposits. evenly bold reque| +146|7|2726|231.15|uriously after the fluffy accounts. furiously bold deposits cajole. requests might engage. quick accounts wake carefu| +146|3|4628|123.91|ly across the dependencies. daringly ironic deposits are furiously; requests are. quickly regular accounts hang. carefu| +146|9|5893|858.59|sual instructions believe. fluffily unusual warhorses nag. unusual dependencies sleep. slow sheaves haggle furiously. carefully ironic dependencies cajole slyly against the accounts. | +146|5|9687|882.37|packages? ideas affix slyly even accounts: express requests wake slyly carefully special depths. ironic in| +147|8|7442|939.14|sts against the furiously unusual instructions integrate ironic accounts. slyly final pinto beans sleep blithely carefully final asymptotes. slyly ironic warhorses befor| +147|4|7647|102.19|refully regular orbits about the furiously express asymptotes haggle carefully according to the blithely regular ideas. blithely express excuses around the furiously | +147|10|1596|466.37|ole. slyly final packages do haggle quickly. unusual accounts across the pending pinto beans was furiously according to the furiously brave deposits. pending deposits along the regular request| +147|6|4235|235.91|ar pinto beans. regular instructions sleep carefully after the furiously blithe accounts. slowly pending ideas could nag careful, even accounts. attainments use slyly quickly| +148|9|8007|177.40|final requests-- slyly regular theodolites haggle carefully across the blithely final dependencies. slyly even requests about the carefully even accounts sleep | +148|5|1904|774.56|y even pinto beans. fluffily ironic packages sleep slyly. permanently brave requests boost furiously packages. boldly ironic deposits across the carefully bold pinto b| +148|1|5393|264.09|ses. slyly pending packages haggle fluffily fluffily even instructions. fluffily regular packages are carefully about the furiously even asymptot| +148|7|2957|250.98|. requests boost above the bold, special foxes. blithely regular platelets serve blithely slyly final ideas. carefully special idea| +149|10|959|679.10|y to sleep carefully ironic requests. even, regular dependencies haggle. slyly unusual foxes haggle along the instructions. quickly even accounts nag furiously special accoun| +149|6|7283|201.03|usly bold instructions. regular, final deposits alongside of the furiously ironic platelets are slyly even instructions. carefully bold accounts are. ironic, regular requests nag furious| +149|2|7392|266.53|es detect along the regular instructions. bold ideas boost slyly. quickly unusual accounts doubt. carefully even foxes thrash slyly silent, ironic dolphins: Tiresias must wake | +149|8|4104|312.37|ly express excuses. bold pinto beans boost blithely across the bold, final pinto beans. final deposits haggle carefully from the| +150|1|8091|524.71|sleep furiously furiously bold warthogs. furiously express gifts according to the regularly silent sentiments boost within the f| +150|7|2721|814.79|dependencies. special accounts wake carefully furiously regular accounts. regular accounts haggle along the express instructions. express pinto beans along the express, bold deposits run | +150|3|3172|33.71| about the silent ideas. fluffily final requests impress. slyly final requests wake carefully about the slyly express foxes. slyly regular warthogs sleep fur| +150|9|1640|327.34|slyly even deposits alongside of the furiously even accounts detect boldly quickly regular accounts. final accounts kindle carefu| +151|2|391|281.90|dolites. boldly ironic packages cajole fluffily regular instructions. regular, ironic accounts are blithely. ironic accounts are alongside of th| +151|9|253|840.06| haggle. somas are carefully. slyly regular requests sleep blithely atop the thinly express deposits. stealthily express packages cajole daringly express requests. carefully special requests after t| +151|6|1484|71.68|riously final requests sleep according to the regular deposits? slyly ironic ideas wake furiously. quickly even theodolites use fluffily. regular, unusual courts according to the regular | +151|3|9417|244.06| foxes along the hockey players are slyly about the blithely even packages. unusu| +152|3|1396|164.60|d the instructions. carefully pending accounts haggle fluffily ruthless instruc| +152|10|2653|432.12|carefully pending requests. quickly ironic requests haggle carefully special theodolites. blithely special requests aga| +152|7|3599|77.38| quick excuses according to the pending, ironic requests snooze carefully slyly even foxes: slyly regular instru| +152|4|1283|142.73|olites above the furiously even requests dazzle blithely against the busy, regular pains. furiously blit| +153|4|4695|539.86|atelets. dolphins haggle blithely carefully ironic deposits? express, final accounts wake about the requests. even deposits should use quickly. regular,| +153|1|8464|680.14|cording to the final instructions. carefully fluffy asymptotes haggle carefully | +153|8|2916|685.52|ully express deposits boost daringly packages. furiously ironic accounts sleep slyly ironic instructions. special deposits integrate blithely. | +153|5|6397|285.92|furiously special platelets haggle quickly even, bold pinto beans. blithely close pinto beans boost around the furiously regular packages. quickly express requests cajole.| +154|5|3961|474.19|quickly pending requests nag express dependencies. furiously unusual requests about the regular, pending packages wake according to the ironic packages! theodolites wake about the unusual, regula| +154|2|3231|829.29|ins along the packages use carefully requests. furiously unusual packages kindle fluffily quick| +154|9|7315|960.19|uickly regular dolphins ought to believe among the q| +154|6|1682|160.31|refully except the sly, even requests. careful ideas haggle after the slyly regular foxes: slyly special packages at the slyly regular deposits wake carefully theod| +155|6|2893|222.02|. bold packages are toward the silent pinto beans. quickly fin| +155|3|7077|413.24|lar instructions against the furiously unusual instructions breach furiously for the bold, even platelets. ironic accounts must have to are quickly across the | +155|10|3029|413.02|the carefully ironic asymptotes. even, unusual accounts sleep furiously about the blithely regular ideas. quickly re| +155|7|977|751.45|quickly silent deposits doubt above the unusual instructions. special r| +156|7|4755|453.69|e of the excuses. slyly even theodolites boost about the slyly final foxes? requests after the carefully regular platelets sleep above the furiously pending d| +156|4|7228|994.19|odolites wake quickly slyly final dinos. requests cajole slyly along the instructions. furiously regular deposits cajole slyly blithely ironic instructions. instructions wake. blithely even pinto be| +156|1|3043|252.66|lithely express, silent decoys. bold, special requests along the carefully even accounts| +156|8|3191|9.87|ar instructions-- quickly special deposits wake fluffily about the blithely e| +157|8|5414|369.44|ong the carefully bold ideas boost across the regular, ironic requests. ironic fo| +157|5|763|568.46|cial packages boost along the ideas. packages sleep slyly express packages. ironic, bold requests| +157|2|3718|901.53|, ironic foxes. blithely even foxes wake about the carefully special req| +157|9|3400|288.41|encies integrate carefully even accounts. regular, regular sentiments are against the slyly regular deposits-- even, even ideas use inside the carefull| +158|9|2845|408.72|y. slyly final pinto beans believe fluffily pending, regular deposits. final, unusual ideas according to t| +158|6|8715|452.31|instructions along the ironic, final requests are fluffily regular deposits. regular deposits cajole carefully about the silent instructions| +158|3|4465|837.16| wake carefully in place of the furiously express deposits. slyly regular instructions engage. fluffily f| +158|10|4251|431.90|etly special accounts boost carefully final multipliers. carefu| +159|10|9200|356.66|ccording to the furiously final accounts. carefully fluffy foxes wake idly against the quickly final requests. evenly even pinto beans must have to are against the carefully regular de| +159|7|3585|629.29|g platelets wake furiously slyly bold deposits? slyly regular accounts across the stealthily ironic accounts cajole along the special, ironic pearls. fluffily regular pinto| +159|4|6085|171.40|ross the blithely special deposits are quickly carefully ironic Tiresias. quickly regular deposits was furiously. unusual accounts affix blithely about the regular deposits. asymptotes ab| +159|1|3717|790.87|y blithe dependencies. final accounts haggle furiously. even, special asymptotes| +160|1|2434|525.73|lithely. furiously silent theodolites after the ca| +160|8|8324|999.93|ly final instructions. closely final deposits nag furiously alongside of the furiously dogged theodolites. blithely unusual theodolites are furi| +160|5|6034|733.59| furiously against the final instructions. silent accounts sleep blithely after the boldly final requests. ex| +160|2|6872|872.20|ions are carefully. carefully express foxes nag slyly before the carefully final excuses. accounts after the furiously ironic packages are furio| +161|2|9365|790.03|scapades. packages use. slyly final accounts haggle across the quickly final th| +161|10|8421|394.05|cial ideas. ironic instructions eat blithely slyly special packages. furiously final packages alongside of the furiously final instructions boost carefully against the quickly | +161|8|9679|688.47|ns. blithely express requests sleep slyly foxes. blithely unusual ideas | +161|6|679|893.72| the fluffily final requests. ironic, pending epitaphs affix slyly. qui| +162|3|315|923.04| ideas. carefully final dugouts will have to wake quickly regular asymptotes. express grouches unwind carefully after the regula| +162|1|2604|104.20|usly regular excuses. silent, even sheaves are according to the regular requests. packages grow blithely slyly regular accounts. ca| +162|9|7014|236.07|cording to the stealthily fluffy theodolites. carefully unusual excuses around the regular deposits cajole slyly amo| +162|7|4381|824.36|as across the furiously ironic notornis print blithely alongside of the final, pending deposits. fluffily express deposits slee| +163|4|9241|955.81|cial dolphins. furiously bold foxes could have to use. never sly accounts cajole fluffily about the unusual, special pinto beans. pending, even requests around the quickly special deposits use f| +163|2|3427|499.51|ithely bold packages integrate slyly quiet pinto beans. carefully even deposits boost slyly about the furiously fluffy packages. evenly regular dependencies wa| +163|10|5323|920.75|sly even theodolites against the carefully bold packages wake final pinto beans. furiously pending deposits dazzle furiously. blithely exp| +163|8|9676|573.48|ending accounts haggle blithely ironic, even packages. carefully pending packages wake carefully across the ruthlessly pending accounts! pinto beans wake. slyly final deposits boost slyly. fluffily| +164|5|1295|341.95| bold instructions cajole slyly ironic deposits. quickly ironic foxes are carefully final, bold theodolites. ironic deposi| +164|3|2134|84.02|ns believe. carefully express theodolites impress. carefully fina| +164|1|3245|814.67|brave accounts cajole according to the final platelets. furiously final dolphins across the furi| +164|9|3028|64.89|fully furiously regular requests. furiously bold orbits serve about the regular packages? carefully final deposits p| +165|6|4424|943.82|ular requests. regular accounts cajole against the blithely ironic deposits. blithely even packages cajole. furiously final deposits cajole. thinly pending deposits hagg| +165|4|5534|717.83| quickly regular deposits above the fluffily thin deposits haggle furiously against the quickly final depend| +165|2|3780|730.28| furiously quickly regular foxes. pending requests engage evenly blithel| +165|10|6114|210.84|foxes. foxes haggle. dolphins use carefully according to the fluffily regular packages. blithely special accounts according to the slyly final frets breach blithely after the care| +166|7|6527|309.00|lly. dependencies haggle carefully at the slyly special packages. regular, final packages| +166|5|6508|714.49|y express deposits cajole furiously above the carefully even theod| +166|3|9364|581.52|pinto beans. pinto beans cajole furiously carefully special requests-- quickly | +166|1|6713|631.58| sleep carefully. quickly even deposits run carefully fluffily ironic orbits. ironic deposits wake furiously. close sheaves along the special packages sleep carefully special instr| +167|8|4933|666.70|ular deposits among the even dolphins are quickly express accounts. final, ironic theodolites cajole closely. th| +167|6|5789|524.27| are furiously final, even dugouts. ironic, regular packages nag fu| +167|4|4756|336.75|es are carefully along the carefully express tithes. furiously even deposits cajole slyly slyly regular deposits. bold excuses about the carefully ironic requests sleep blithely instructions| +167|2|6748|704.97|t the silent ideas are blithely carefully even packages; blithely| +168|9|347|394.83|hely blithely final theodolites. blithely final deposits among the quickly even ideas haggle about the blithely bold d| +168|7|1281|771.90|, pending packages. ironic pinto beans use carefully. fluffily bold deposits| +168|5|9089|508.37|ests are always. regular ideas sleep fluffily; special, express instructions cajole slowly. pending platelets boost furiously against the bold, even instructions. bold instructi| +168|3|7519|963.43|requests above the quickly regular deposits use carefully aft| +169|10|6914|619.53|uickly along the dependencies. furiously pending notornis cajole at the carefully special attainments. carefully ironic packages impress slyly care| +169|8|6589|947.03|gside of the quickly regular asymptotes. quickly even theodolites against the theodolites promise express requests. ironic accounts wake careful| +169|6|6731|713.35| the quickly special excuses wake blithely alongside of the carefully silent accounts. regular dolphin| +169|4|7691|476.19|slyly alongside of the warthogs. fluffily even instructions poach under the slyly pending packages. blithely silent deposits use across the fur| +170|1|7516|581.65| pinto beans. unusual ideas was fluffily. excuses cajole carefully final dependencies. platelets nag quickly according to the furiously ironic requests. carefully regular dependenci| +170|9|838|667.16|orges do sleep furiously. fluffily furious requests among the final requests sleep after the slyly bold ideas? regular pinto beans might ha| +170|7|6498|251.19| fluffily regular accounts integrate. blithely even packages cajole fluffily. furiously ironic excuses haggle by the finally final requ| +170|5|6593|202.07|ep blithely final packages. quickly bold pains cajole carefully across the somet| +171|2|8217|859.60|ress deposits. carefully special requests are furiously final requests. accounts cajole carefully blith| +171|1|2311|864.96|s are along the blithely final deposits. regular asymptotes nag slyly against the requests. accounts cajole carefully carefully | +171|10|8561|22.69|y close ideas are quickly silently regular packages. even, silent requests wake against the slyly special dependencies; regular accounts sleep doggedly furiously final pinto beans. slyly unusual pac| +171|9|7589|935.29|s above the theodolites wake slyly along the carefully unusual dependencies. carefully express theodolites a| +172|3|9799|184.96|ts. slyly even asymptotes nag blithely regular accounts. final platelets cajole furiously slyly bold packages. ironic accounts sleep slyly. pendi| +172|2|8333|920.74|ronic foxes. quickly unusual accounts cajole blithely. blithely bold deposits cajole. blithely close pinto beans cajole requests. quickly express excuses around the quickly even deposits nag agai| +172|1|3589|437.86|posits should have to boost furiously near the unusual ideas. final packages cajole blithely. carefully final deposits boost carefully. carefully special attainments boost quickly af| +172|10|1661|687.13|y among the slyly even requests. ideas according to the slyly pending dinos print quickly slyly ironic foxes. pending, even excuses dazzle car| +173|4|2536|353.84|ons-- final, silent dependencies sleep across the special, special excuses. furiously even accounts must have to mold after the ironic accounts. reque| +173|3|8307|70.22|alongside of the furiously even packages. furiously final requests snooze blithely alongside of the carefull| +173|2|6050|683.78|e after the slyly regular ideas. unusual pinto beans cajole even asymptotes-- silent, stealthy requests after the even accounts haggle blithely regular instructions. slyly ev| +173|1|6162|877.84|es. slyly bold requests after the blithely regular dependencies cajole slyly even ideas. unusual deposits integrate about the final somas. | +174|5|2103|681.95|sual, express requests wake furiously ruthless, final accounts. carefully ironic somas dazzle furiously. unusual asymptotes sleep-- patterns about the furiousl| +174|4|6795|143.48|regular theodolites. special accounts integrate across the carefully ironic Tiresias. blithely even platelets detect. foxes about t| +174|3|111|135.46| express packages-- quickly unusual courts lose carefully requests. bold accounts solve about the theodolites; pinto beans use. ironic foxes| +174|2|8404|126.20|nding accounts mold furiously. slyly ironic foxes boost express sheaves. daringly final packages along the stealthy dependencies are blithely ironic requests. furiously pending pin| +175|6|5515|487.68|ages sleep against the Tiresias. slyly pending packages print slyly above the evenly ironic dolphins. furiously ironic packages use f| +175|5|7522|784.93| affix. quickly final theodolites haggle furiously after the slowly even pinto beans. furiously final packages use slyly. slyly regular reque| +175|4|8501|706.61|int above the instructions. furiously regular requests integrate blithely according to the instructions. slyly pending foxes are asymptotes. slyly ruthless accounts wake. r| +175|3|9456|978.56| regular packages. carefully ironic packages use. blithely ironic accounts among the pending, | +176|7|7180|179.09|riously final requests. accounts doubt blithely regular somas. slyly even platelets are. theodolites across | +176|6|3589|157.38|inal excuses. express deposits haggle carefully even deposits. carefully unusual requests haggle along the fluffily bold deposits. even, final requests affix. furi| +176|5|5407|947.51|ending accounts eat carefully instructions. carefully pending packages detect slyly express accounts. foxes wake fluffily across th| +176|4|1783|861.63|g the carefully special platelets. dogged, ironic asymptotes wake requests. regular excus| +177|8|1239|44.75|requests use furiously regular, final requests. regular requests on the pending, ironic deposits use slyly among the excuses. carefully regular sheaves are.| +177|7|4349|63.36|osits sleep among the fluffily unusual instructions. ironic dolphins cajole. furiously bold deposits sleep carefully. even, unusual accounts| +177|6|9872|252.42|sual platelets. bold foxes affix furiously. pending, pending accounts lose furiously. pending platelets along the unusual, even foxes wake regular, even theo| +177|5|4727|859.82|es are. slyly ironic packages haggle around the slyly bold deposits. bold foxes haggle blithely. f| +178|9|4231|558.56|deposits. patterns use against the furiously unusual accounts. accounts wake carefully above the careful| +178|8|1919|362.26| ironic dependencies. blithely regular packages detect fluffily special theodolites. regular instructions poach-- ironic deposits along the final requests | +178|7|6836|864.93|y. ideas integrate regular pinto beans. special foxes wake above the slyly ironic asymptotes. quickly ironic ideas sleep. silent dependencies against the slyly bold packa| +178|6|6922|475.18| regular patterns. fluffily express accounts about the furiously bold deposits cajole slyly about the furiously silent foxe| +179|10|6956|444.38|g the furiously careful excuses haggle quickly thinly special Tiresias. furiously express foxes after the quickly regular deposits sleep ironic packages| +179|9|1954|372.75|even dependencies print carefully. deposits boost blithely about the ironic, ironic accounts. express, regular deposits are. bli| +179|8|2710|277.15|d the frets. pending packages doze quickly across the furiously regular deposits. pending, even deposits impress ironic ideas. quickly regular r| +179|7|4776|8.39|sly special pinto beans. pinto beans cajole. carefully unusual ideas around the silent accounts are blithely carefully ev| +180|1|2467|440.25| instructions affix. regular packages cajole quickly. carefully express asymptotes use furiously around the pendin| +180|10|1108|934.59|hinly after the regular, unusual asymptotes! carefully regular theodolites sublate. regular, ironic deposits against the regular pinto beans nag ca| +180|9|724|426.16|e, regular accounts. furiously final ideas are furiously above the bold, silent asymptotes. sly instructions are carefully quickly final sentiments. furiously ironic foxes cajole bold, exp| +180|8|5899|864.83|hin the carefully furious pinto beans. furiously ironic pinto beans use slyly above the even instructio| +181|2|2416|844.44|ully. theodolites throughout the blithely unusual pinto bea| +181|2|3242|886.53| express ideas nag carefully brave accounts. slyly express deposits would affix. final, special requests against the slyl| +181|2|215|938.29| accounts boost furiously furiously blithe theodolites. slyly bold requests unwind special, unusual requests. furious ideas boost quickly pending | +181|2|1122|657.25|lyly fluffily pending foxes. fluffily ironic pains haggle. thinly regular requests against the deposits affix after the never ev| +182|3|9699|535.27|ound the furiously regular foxes. pending requests dazzle along | +182|3|960|519.36|arefully pending dependencies are always slyly unusual pin| +182|3|6243|741.46|accounts are slyly. furiously ironic requests haggle. express, special instructions against the ironic theodolites use s| +182|3|6146|365.00|s. blithely express theodolites sleep blithely alongside of the requests?| +183|4|30|875.44|slyly. furiously regular instructions cajole slyly about the pending, final theodolites. blithely final deposits cajole fluffily alo| +183|4|4482|424.86|es. depths affix fluffily. bold instructions haggle. ruthless instructions must have to boost| +183|4|8707|884.26|posits wake. blithely pending requests nag furiously alongside of the p| +183|4|333|678.16|ost final, final theodolites. slyly bold foxes dazzle carefully furiously regular accounts. regular, sly instructions about the furiously regular excuses nag blithely abou| +184|5|7069|449.45|nal ideas. blithely final ideas haggle against the pinto beans. qu| +184|5|9193|576.88|uickly quick dependencies could detect furiously. final packages p| +184|5|6400|551.90|ss dependencies. quickly even pinto beans are. express accounts a| +184|5|831|186.84|kages cajole carefully furiously ironic instructions. deposits use bl| +185|6|1475|538.58|unts hinder slyly. quickly express ideas sleep carefully | +185|6|6244|213.04|ly unusual decoys are furiously quickly regular packages. bold, ironic foxes cajole fluffily around| +185|6|7245|426.74|sleep blithely alongside of the regular excuses. even, regular| +185|6|8014|510.23|lithely even ideas. regular platelets wake carefully ironic, special instructions! final pearls above the fluffily quiet ideas use furiously about the | +186|7|1095|252.84|. carefully regular pinto beans according to the blithely close asymptotes haggle carefully special requests. packages cajole up the furi| +186|7|1945|18.75|nic foxes boost carefully careful packages: express, fluffy dolphins nag quickly ironic packages. slyly bold requests nag amon| +186|7|8838|729.42|ing asymptotes. enticingly regular theodolites mai| +186|7|7898|812.37|ctions sleep silently carefully bold platelets. furiously ironic dependencies boost. regular de| +187|8|8656|238.66|tes use along the even foxes? final foxes haggle pinto beans. slyly ironic theodolites are according to the deposits. furiously pending reques| +187|8|4945|316.64|eposits boost quickly bold requests. furiously regular ideas boost boldly. special, express dependencies are fluffily slyly reg| +187|8|3183|362.75|t the bold platelets. fluffily express platelets cajole fluffily along the always bold requests. blith| +187|8|7440|989.71|e slyly against the slyly regular pinto beans. requests haggle carefully around the asymptotes. regular, regular asymptotes use furiously some| +188|9|4835|771.95|pains are fluffily about the fluffily pending asymptot| +188|9|2620|331.70|elets nag slyly regular pinto beans. slyly even dugouts above the blithely unusual theodolites su| +188|9|730|713.62|nag against the final accounts. blithely pending attainments lose. silent requests wake quickly. careful| +188|9|5430|920.20|uriously. special, regular instructions sleep along the accounts. quickly even foxes across the regular theodolites hang u| +189|10|1305|392.50|packages. regular, unusual accounts lose furiously fluffily regular platelets. requests sleep carefully dependenc| +189|10|8777|573.22|beans cajole slyly ironic requests. requests are quickly unusual, even packages. ironic frays haggle. blithely pending requests nod slyly. express, silent requests against the slyly unusual | +189|10|6369|946.07|ts hinder slyly regular, unusual foxes. final sentiments use above the slyly r| +189|10|2505|593.23| the deposits. special deposits sleep-- furiously regular sauternes solve furiously across the furiously regular pack| +190|1|535|621.53|unts must have to haggle; slyly ironic accounts affix slyly alongside of the carefully even accounts. furious deposits haggle quietly among the packages. blithely | +190|1|5845|608.91| haggle along the carefully unusual pinto beans. quickly final accounts sleep a| +190|1|4579|396.60|inal, final foxes. regular, even deposits wake blithely! silent, regular packages integrate according to the slyly regular deposits. ironic, ironic notornis ha| +190|1|2861|458.00|s cajole slyly across the daring, final pinto beans. carefully quiet requests affix along the a| +191|2|8310|521.06|the slowly regular deposits. special accounts along the quickly unusual| +191|3|1693|464.46|y. slyly unusual waters across the special pinto beans nag blithely according to the busy deposits. carefully regular accounts are against the regular accounts; perman| +191|4|597|126.96|ly final accounts should have to boost above the doggedly express pinto beans. blithely regular packages cajole furiously bold requests. fluf| +191|5|9673|119.41|press deposits kindle theodolites! slyly final dependencies against the blithely final packages sleep slyly regular requests. theodolites cajole furiously quickly bold a| +192|3|606|198.69|inal platelets integrate regular accounts. accounts wake ironic, silent accounts. slyly unusual accounts kindle carefully-| +192|4|2656|916.16|uickly. slyly bold ideas affix special, close theodolites. ironic, pending requests use carefully. blithely regular | +192|5|1811|359.59|ly carefully special asymptotes. furiously pending instructions haggle blithely bravely pending requests. carefully f| +192|6|8305|861.23|s against the carefully regular foxes haggle fluffily across the pending accounts. blithely final packages sleep after the furiously ironic theodolites. quickly bold r| +193|4|6184|335.98| quickly bold deposits cajole furiously ruthless courts. carefully| +193|5|4762|606.19|ns sleep against the furiously regular asymptotes. carefully even asymptotes across the daringly final packages sleep fluf| +193|6|385|571.71|ons. slyly ironic deposits wake furiously ironic, unus| +193|7|9791|478.52|quests. carefully even requests use regular excuses. pending accounts are. furiously even pinto beans haggle furi| +194|5|4289|662.17|ic Tiresias serve along the ironic, express accounts. quickly final requests are slyly among the carefully special requests. accounts boost.| +194|6|377|430.21|efully instead of the special ideas. fluffily unusual asymptotes cajole blithely after the regular ideas. final accounts along the silent ex| +194|7|5294|913.46|indle fluffily despite the carefully silent instructions. furiously regular hockey players cajole slyly unusual accounts. furiously regular realms cajole furiously according to the e| +194|8|7890|79.40|ctions sleep. carefully unusual theodolites should wake furiously across the deposits-- furiously bold excuses boost furiously carefully slow accounts. boldly final accounts grow. regular excuse| +195|6|9985|20.39|efully among the fluffily even accounts! requests are slyly ag| +195|7|2947|271.39|yly regular requests cajole carefully. carefully fina| +195|8|319|102.58|ts. ironic foxes wake carefully slyly special pinto beans. blithely silent excuses hinder blithely quietly regular accounts. quickly careful foxes maintain slyly above the slyly express fo| +195|9|2803|992.27|xes according to the regular foxes wake furiously final theodolites. furiously regular packages sleep slyly express theodolites. slyly thin instructions sleep r| +196|7|3843|859.90|l platelets use blithely alongside of the enticingly final deposits. fluffily final requests boost furiously ag| +196|8|2515|966.01|final theodolites. fluffily even deposits are against the| +196|9|4778|37.61|fully final requests cajole fluffily across the furiously ironic accounts. qui| +196|10|1068|928.25| cajole about the blithely regular ideas. final ideas hin| +197|8|9678|753.88|ously. slyly stealthy requests use alongside of the express, unusual packages. final deposits wake. carefully unusual theodolites cajole slyly about the regular foxes. slyly iron| +197|9|2631|279.05|e blithely. quickly final deposits wake fluffily excuses. even, unusual deposits x-ray among the final accounts. even ideas above the blithely ironic requests sleep furiously slyly final inst| +197|10|7598|845.51|lets according to the regular deposits wake furiously about the carefully daring theodolites. blithely express dolphins poach after th| +197|1|8950|897.33|ideas. requests wake above the blithely unusual deposits. slyly regular | +198|9|6878|587.41|y even accounts poach carefully about the asymptotes. deposits haggle slyly. finally unusual requests run silently regular, bold packages: instructions after the | +198|10|6493|673.99|y express excuses use blithely among the pending accounts. stealthy ide| +198|1|8410|166.93|kages. blithely final theodolites dazzle fluffily. accounts boost furiously. furiously unu| +198|2|6190|697.10|beans nag fluffily about the asymptotes. slyly bold escapades haggle quickly. fluffily special requests haggle above the ironic,| +199|10|9343|79.70|ending accounts nag across the instructions. carefully express packages over the blithely even pac| +199|1|8199|46.52|oost slyly. ironic platelets sleep blithely about the slyly silent foxes. furiously even pl| +199|2|2742|890.63| the special deposits? carefully final deposits about the carefully regular sauternes | +199|3|7167|884.56|onic platelets use carefully along the slowly stealthy ideas. slyly dogged instructions are quickly above the slyly u| +200|1|3120|776.41|ntly final packages kindle furiously blithely ironic accounts. carefully final packages according to the carefully | +200|2|5392|242.52|y unusual ideas. ruthlessly express asymptotes cajole. regular theodolites are. carefully silent deposits poach carefully across the fluffily even theodolites. carefully express realms hag| +200|3|9408|307.79|oxes! fluffily regular requests use against the unusual, slow ideas. ironic accounts doze b| +200|4|331|466.07| slyly even requests. fluffily final packages boost carefully express instructions. slyly regular forges are blithely unusual, regular | diff --git a/zetasql/examples/tpch/catalog/region.tbl b/zetasql/examples/tpch/catalog/region.tbl new file mode 100644 index 000000000..c5ebb63b6 --- /dev/null +++ b/zetasql/examples/tpch/catalog/region.tbl @@ -0,0 +1,5 @@ +0|AFRICA|lar deposits. blithely final packages cajole. regular waters are final requests. regular accounts are according to | +1|AMERICA|hs use ironic, even requests. s| +2|ASIA|ges. thinly even pinto beans ca| +3|EUROPE|ly final courts cajole furiously final excuse| +4|MIDDLE EAST|uickly special accounts cajole carefully blithely close requests. carefully final asymptotes haggle furiousl| diff --git a/zetasql/examples/tpch/catalog/supplier.tbl b/zetasql/examples/tpch/catalog/supplier.tbl new file mode 100644 index 000000000..d9c0e9f7e --- /dev/null +++ b/zetasql/examples/tpch/catalog/supplier.tbl @@ -0,0 +1,10 @@ +1|Supplier#000000001| N kD4on9OM Ipw3,gf0JBoQDd7tgrzrddZ|17|27-918-335-1736|5755.94|each slyly above the careful| +2|Supplier#000000002|89eJ5ksX3ImxJQBvxObC,|5|15-679-861-2259|4032.68| slyly bold instructions. idle dependen| +3|Supplier#000000003|q1,G3Pj6OjIuUYfUoH18BFTKP5aU9bEV3|1|11-383-516-1199|4192.40|blithely silent requests after the express dependencies are sl| +4|Supplier#000000004|Bk7ah4CK8SYQTepEmvMkkgMwg|15|25-843-787-7479|4641.08|riously even requests above the exp| +5|Supplier#000000005|Gcdm2rJRzl5qlTVzc|11|21-151-690-3663|-283.84|. slyly regular pinto bea| +6|Supplier#000000006|tQxuVm7s7CnK|14|24-696-997-4969|1365.79|final accounts. regular dolphins use against the furiously ironic decoys. | +7|Supplier#000000007|s,4TicNGB4uO6PaSqNBUq|23|33-990-965-2201|6820.35|s unwind silently furiously regular courts. final requests are deposits. requests wake quietly blit| +8|Supplier#000000008|9Sq4bBH2FQEmaFOocY45sRTxo6yuoG|17|27-498-742-3860|7627.85|al pinto beans. asymptotes haggl| +9|Supplier#000000009|1KhUgZegwM3ua7dsYmekYBsK|10|20-403-398-8662|5302.37|s. unusual, even requests along the furiously regular pac| +10|Supplier#000000010|Saygah3gYWMp72i PY|24|34-852-489-8585|3891.91|ing waters. regular requests ar| diff --git a/zetasql/examples/tpch/catalog/tpch_catalog.cc b/zetasql/examples/tpch/catalog/tpch_catalog.cc new file mode 100644 index 000000000..9aee32852 --- /dev/null +++ b/zetasql/examples/tpch/catalog/tpch_catalog.cc @@ -0,0 +1,324 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/examples/tpch/catalog/tpch_catalog.h" + +#include +#include +#include +#include + +#include "zetasql/common/simple_evaluator_table_iterator.h" +#include "zetasql/examples/tpch/catalog/customer.tbl.h" +#include "zetasql/examples/tpch/catalog/lineitem.tbl.h" +#include "zetasql/examples/tpch/catalog/nation.tbl.h" +#include "zetasql/examples/tpch/catalog/orders.tbl.h" +#include "zetasql/examples/tpch/catalog/part.tbl.h" +#include "zetasql/examples/tpch/catalog/partsupp.tbl.h" +#include "zetasql/examples/tpch/catalog/region.tbl.h" +#include "zetasql/examples/tpch/catalog/supplier.tbl.h" +#include "zetasql/public/analyzer_options.h" +#include "zetasql/public/analyzer_output.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/evaluator_table_iterator.h" +#include "zetasql/public/functions/date_time_util.h" +#include "zetasql/public/simple_catalog.h" +#include "zetasql/public/simple_catalog_util.h" +#include "zetasql/public/type.h" +#include "zetasql/public/types/type.h" +#include "zetasql/public/value.h" +#include "absl/base/const_init.h" +#include "absl/base/macros.h" +#include "absl/container/flat_hash_set.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/numbers.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "riegeli/base/maker.h" +#include "riegeli/bytes/string_reader.h" +#include "riegeli/csv/csv_reader.h" +#include "zetasql/base/ret_check.h" +#include "zetasql/base/status_macros.h" +#include "zetasql/base/clock.h" + +namespace zetasql { + +// These are create statements for the TPCH tables. +// I found copies of these in various places with varying NOT NULL constraints. +// I'm not sure which version is official, but these constraints aren't +// supported or used here anyway so they are just commented out. +static const char* kCreateRegion = + R"(CREATE TABLE Region ( + R_REGIONKEY UINT64, -- NOT NULL + R_NAME STRING, + R_COMMENT STRING, +))"; + +static const char* kCreateNation = + R"(CREATE TABLE Nation ( + N_NATIONKEY UINT64, -- NOT NULL + N_NAME STRING, + N_REGIONKEY UINT64, -- NOT NULL + N_COMMENT STRING, +))"; + +static const char* kCreateCustomer = + R"(CREATE TABLE Customer ( + C_CUSTKEY UINT64, -- NOT NULL + C_NAME STRING, + C_ADDRESS STRING, + C_NATIONKEY UINT64, -- NOT NULL + C_PHONE STRING, + C_ACCTBAL DOUBLE, + C_MKTSEGMENT STRING, + C_COMMENT STRING, +))"; + +static const char* kCreateSupplier = + R"(CREATE TABLE Supplier ( + S_SUPPKEY UINT64, -- NOT NULL + S_NAME STRING, + S_ADDRESS STRING, + S_NATIONKEY UINT64, + S_PHONE STRING, + S_ACCTBAL DOUBLE, + S_COMMENT STRING, +))"; + +static const char* kCreateOrders = + R"(CREATE TABLE Orders ( + O_ORDERKEY UINT64, -- NOT NULL + O_CUSTKEY UINT64, -- NOT NULL + O_ORDERSTATUS STRING, + O_TOTALPRICE DOUBLE, + O_ORDERDATE DATE, + O_ORDERPRIORITY STRING, + O_CLERK STRING, + O_SHIPPRIORITY INT64, + O_COMMENT STRING, +))"; + +static const char* kCreateLineItem = + R"(CREATE TABLE LineItem ( + L_ORDERKEY UINT64, -- NOT NULL + L_PARTKEY UINT64, -- NOT NULL + L_SUPPKEY UINT64, -- NOT NULL + L_LINENUMBER UINT64, -- NOT NULL + L_QUANTITY DOUBLE, + L_EXTENDEDPRICE DOUBLE, + L_DISCOUNT DOUBLE, + L_TAX DOUBLE, + L_RETURNFLAG STRING, + L_LINESTATUS STRING, + L_SHIPDATE DATE, + L_COMMITDATE DATE, + L_RECEIPTDATE DATE, + L_SHIPINSTRUCT STRING, + L_SHIPMODE STRING, + L_COMMENT STRING +))"; + +static const char* kCreatePart = + R"(CREATE TABLE Part ( + P_PARTKEY UINT64, -- NOT NULL + P_NAME STRING, + P_MFGR STRING, + P_BRAND STRING, + P_TYPE STRING, + P_SIZE INT64, + P_CONTAINER STRING, + P_RETAILPRICE DOUBLE, + P_COMMENT STRING, +))"; + +static const char* kCreatePartSupp = + R"(CREATE TABLE PartSupp ( + PS_PARTKEY UINT64, -- NOT NULL + PS_SUPPKEY UINT64, -- NOT NULL + PS_AVAILQTY INT64, + PS_SUPPLYCOST DOUBLE, + PS_COMMENT STRING, +))"; + +// This stores the data parsed from CSV and pre-processed for one TPCH table. +// It's computed lazily and then stored once so it can be reused each time +// we need to make an iterator to scan the table. +struct ParsedTpchTableData { + std::vector>> column_values; + int num_rows = 0; +}; +// This stores a ParsedTpchTableData or an error. +// The `optional` is filled in once loading this tables has been attempted. +typedef std::optional< + absl::StatusOr>> + ParsedTpchTableDataHolder; + +// Load the data for one TPCH table and return it in a ParsedTpchTableData. +static ParsedTpchTableDataHolder::value_type MakeParsedTpchTableData( + const SimpleTable& table, absl::string_view contents) { + riegeli::CsvReader csv_reader( + riegeli::Maker(contents), + riegeli::CsvReaderBase::Options().set_field_separator('|')); + + const int num_columns = table.NumColumns(); + + // Values are in column_major order. + std::vector>> column_values(num_columns); + for (int i = 0; i < num_columns; ++i) { + column_values[i] = std::make_shared>(); + } + + int num_rows = 0; + std::vector record; + while (csv_reader.ReadRecord(record)) { + ++num_rows; + + // The TPCH files have a '|' separator at the end of the line, so they + // look like they have one extra column. + ZETASQL_RET_CHECK_EQ(record.size(), num_columns + 1); + + for (int i = 0; i < num_columns; ++i) { + const Type* type = table.GetColumn(i)->GetType(); + const std::string& field = record[i]; + + if (type->IsString()) { + column_values[i]->push_back(Value::String(field)); + } else if (type->IsInt64()) { + int64_t value; + ZETASQL_RET_CHECK(absl::SimpleAtoi(field, &value)) << field; + column_values[i]->push_back(Value::Int64(value)); + } else if (type->IsUint64()) { + uint64_t value; + ZETASQL_RET_CHECK(absl::SimpleAtoi(field, &value)) << field; + column_values[i]->push_back(Value::Uint64(value)); + } else if (type->IsDouble()) { + double value; + ZETASQL_RET_CHECK(absl::SimpleAtod(field, &value)) << field; + column_values[i]->push_back(Value::Double(value)); + } else if (type->IsDate()) { + int32_t date; + ZETASQL_RET_CHECK_OK(zetasql::functions::ConvertStringToDate(field, &date)) + << field; + column_values[i]->push_back(Value::Date(date)); + } else { + ZETASQL_RET_CHECK_FAIL() << "Unhandled type: " << type->DebugString(); + } + } + } + if (!csv_reader.Close()) return csv_reader.status(); + + auto data = std::make_unique(); + data->num_rows = num_rows; + + data->column_values.reserve(num_columns); + for (auto& values : column_values) { + ZETASQL_RET_CHECK_EQ(values->size(), num_rows); + + // Copying the outer vector was necessary to make the inner vector a + // vector of const. + data->column_values.push_back(values); + } + + return data; +} + +// Make the callback that makes an iterator for a particular TPCH table. +// All work happens lazily and at most once, storing the state in `data_holder`. +// +// This has enough functionality for the TPCH tables here. It could be +// generalized to support more use cases. +static SimpleTable::EvaluatorTableIteratorFactory +MakeIteratorFactoryFromCsvFile(const absl::string_view contents, + const SimpleTable& table, + ParsedTpchTableDataHolder* data_holder) { + auto factory = [contents, &table, + data_holder](absl::Span column_idxs) + -> absl::StatusOr> { + // Initialize the ParsedTpchTableData if it hasn't been done for this table + // yet, using a Mutex to avoid race conditions. + { + static absl::Mutex mutex(absl::kConstInit); + absl::MutexLock lock(&mutex); + if (!*data_holder) { + *data_holder = MakeParsedTpchTableData(table, contents); + } + } + + // We need a new columns vector in case it's a new Table, but we can always + // reuse the values vectors. + std::vector columns; + columns.reserve(table.NumColumns()); + for (int i = 0; i < table.NumColumns(); ++i) { + columns.push_back(table.GetColumn(i)); + } + + ZETASQL_RETURN_IF_ERROR((*data_holder)->status()); + const ParsedTpchTableData* data = (*data_holder)->value().get(); + + // Make the iterator to return the table contents. + std::unique_ptr iter( + new SimpleEvaluatorTableIterator( + columns, data->column_values, data->num_rows, + /*end_status=*/absl::OkStatus(), /*filter_column_idxs=*/ + absl::flat_hash_set(column_idxs.begin(), column_idxs.end()), + /*cancel_cb=*/[]() {}, + /*set_deadline_cb=*/[](absl::Time t) {}, zetasql_base::Clock::RealClock())); + return iter; + }; + return factory; +} + +absl::StatusOr> MakeTpchCatalog() { + auto catalog = std::make_unique(""); + + AnalyzerOptions analyzer_options; + analyzer_options.mutable_language()->AddSupportedStatementKind( + RESOLVED_CREATE_TABLE_STMT); + + std::unique_ptr analyzer_output; + + // These are pairs with {create_statement, file_contents}. + static const char* kTableDefinitions[][2] = { + {kCreateRegion, embedded_resources::kTpchData_region}, + {kCreateLineItem, embedded_resources::kTpchData_lineitem}, + {kCreateNation, embedded_resources::kTpchData_nation}, + {kCreateCustomer, embedded_resources::kTpchData_customer}, + {kCreateSupplier, embedded_resources::kTpchData_supplier}, + {kCreateOrders, embedded_resources::kTpchData_orders}, + {kCreatePart, embedded_resources::kTpchData_part}, + {kCreatePartSupp, embedded_resources::kTpchData_partsupp}, + }; + static const int kNumTables = ABSL_ARRAYSIZE(kTableDefinitions); + static ParsedTpchTableDataHolder* const parsed_datas = + new ParsedTpchTableDataHolder[kNumTables]; + + for (int i = 0; i < kNumTables; ++i) { + SimpleTable* table; + ZETASQL_RETURN_IF_ERROR(AddTableFromCreateTable( + kTableDefinitions[i][0], analyzer_options, + /*allow_non_temp=*/true, analyzer_output, table, *catalog)); + + table->SetEvaluatorTableIteratorFactory(MakeIteratorFactoryFromCsvFile( + kTableDefinitions[i][1], *table, &parsed_datas[i])); + } + + return catalog; +} + +} // namespace zetasql diff --git a/zetasql/examples/tpch/catalog/tpch_catalog.h b/zetasql/examples/tpch/catalog/tpch_catalog.h new file mode 100644 index 000000000..b354d1162 --- /dev/null +++ b/zetasql/examples/tpch/catalog/tpch_catalog.h @@ -0,0 +1,35 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_EXAMPLES_TPCH_CATALOG_TPCH_CATALOG_H_ +#define ZETASQL_EXAMPLES_TPCH_CATALOG_TPCH_CATALOG_H_ + +#include + +#include "zetasql/public/simple_catalog.h" +#include "absl/status/statusor.h" + +namespace zetasql { + +// Make a SimpleCatalog containing the eight tables from TPC-H. +// The tables include a callback to return data. +// A new catalog is made each time, but data is shared across all instances. +// This uses 1MB of source data linked in as a cc_embed_data. +absl::StatusOr> MakeTpchCatalog(); + +} // namespace zetasql + +#endif // ZETASQL_EXAMPLES_TPCH_CATALOG_TPCH_CATALOG_H_ diff --git a/zetasql/examples/tpch/catalog/tpch_catalog_test.cc b/zetasql/examples/tpch/catalog/tpch_catalog_test.cc new file mode 100644 index 000000000..ae88c25d5 --- /dev/null +++ b/zetasql/examples/tpch/catalog/tpch_catalog_test.cc @@ -0,0 +1,104 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/examples/tpch/catalog/tpch_catalog.h" + +#include +#include + +#include "zetasql/base/testing/status_matchers.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/evaluator_table_iterator.h" +#include "zetasql/public/simple_catalog.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace zetasql { + +static void CheckRowCount(SimpleCatalog& catalog, const char* table_name, + int expected_num_rows) { + const Table* table; + ZETASQL_ASSERT_OK(catalog.GetTable(table_name, &table)); + + ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr iterator, + table->CreateEvaluatorTableIterator(/*column_idxs=*/{})); + + int64_t num_rows = 0; + while (iterator->NextRow()) { + ++num_rows; + } + ZETASQL_ASSERT_OK(iterator->Status()); + EXPECT_EQ(num_rows, expected_num_rows); +} + +TEST(TpchCatalog, Make) { + // Run the test twice to make sure that when we make a new catalog, the + // cached values from the previous catalog still work. + for (int iter = 0; iter < 2; ++iter) { + auto make_result = MakeTpchCatalog(); + ZETASQL_EXPECT_OK(make_result); + + SimpleCatalog& catalog = *make_result.value(); + EXPECT_EQ(catalog.tables().size(), 8); + + // Test a sample of one expected table and column. + const Table* table; + ZETASQL_EXPECT_OK(catalog.GetTable("Orders", &table)); + EXPECT_EQ(table->NumColumns(), 9); + EXPECT_EQ(table->GetColumn(3)->Name(), "O_TOTALPRICE"); + EXPECT_EQ(table->GetColumn(3)->GetType()->DebugString(), "DOUBLE"); + + // Check reading content from all the tables works, and we get the + // right row counts. Case insenitive table name lookup works. + CheckRowCount(catalog, "customer", 150); + CheckRowCount(catalog, "lineITEM", 6005); + CheckRowCount(catalog, "nation", 25); + CheckRowCount(catalog, "orders", 1500); + CheckRowCount(catalog, "part", 200); + CheckRowCount(catalog, "partsupp", 800); + CheckRowCount(catalog, "region", 5); + CheckRowCount(catalog, "supplier", 10); + + // Check the first row of Orders, where we can check the value of one column + // of each supported type. + ZETASQL_ASSERT_OK_AND_ASSIGN( + std::unique_ptr iterator, + table->CreateEvaluatorTableIterator(/*column_idxs=*/{})); + ASSERT_TRUE(iterator->NextRow()); + + EXPECT_EQ(iterator->GetColumnName(1), "O_CUSTKEY"); + EXPECT_EQ(iterator->GetColumnType(1)->DebugString(), "UINT64"); + EXPECT_EQ(iterator->GetValue(1).DebugString(), "37"); + + EXPECT_EQ(iterator->GetColumnName(3), "O_TOTALPRICE"); + EXPECT_EQ(iterator->GetColumnType(3)->DebugString(), "DOUBLE"); + EXPECT_EQ(iterator->GetValue(3).DebugString(), "131251.81"); + + EXPECT_EQ(iterator->GetColumnName(4), "O_ORDERDATE"); + EXPECT_EQ(iterator->GetColumnType(4)->DebugString(), "DATE"); + EXPECT_EQ(iterator->GetValue(4).DebugString(), "1996-01-02"); + + EXPECT_EQ(iterator->GetColumnName(5), "O_ORDERPRIORITY"); + EXPECT_EQ(iterator->GetColumnType(5)->DebugString(), "STRING"); + EXPECT_EQ(iterator->GetValue(5).DebugString(), "\"5-LOW\""); + + EXPECT_EQ(iterator->GetColumnName(7), "O_SHIPPRIORITY"); + EXPECT_EQ(iterator->GetColumnType(7)->DebugString(), "INT64"); + EXPECT_EQ(iterator->GetValue(7).DebugString(), "0"); + } +} + +} // namespace zetasql diff --git a/zetasql/examples/tpch/concat_queries.sh b/zetasql/examples/tpch/concat_queries.sh new file mode 100644 index 000000000..f42897fa6 --- /dev/null +++ b/zetasql/examples/tpch/concat_queries.sh @@ -0,0 +1,27 @@ +# +# Copyright 2019 Google LLC +# +# Licensed 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 +# +# 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. +# + +for f in $(seq 1 22); do + echo "# TPCH $f" + cat "queries/$f.sql" + echo +done > all_queries.sql + +for f in $(seq 1 22); do + echo "# TPCH $f" + cat "pipe_queries/$f.sql" + echo +done > all_pipe_queries.sql diff --git a/zetasql/examples/tpch/describe.sql b/zetasql/examples/tpch/describe.sql new file mode 100644 index 000000000..512f4b7e3 --- /dev/null +++ b/zetasql/examples/tpch/describe.sql @@ -0,0 +1,8 @@ +DESCRIBE Customer; +DESCRIBE LineItem; +DESCRIBE Nation; +DESCRIBE Orders; +DESCRIBE Part; +DESCRIBE PartSupp; +DESCRIBE Region; +DESCRIBE Supplier; diff --git a/zetasql/examples/tpch/describe.txt b/zetasql/examples/tpch/describe.txt new file mode 100644 index 000000000..3002844c8 --- /dev/null +++ b/zetasql/examples/tpch/describe.txt @@ -0,0 +1,85 @@ +Table: Customer +Columns: + C_CUSTKEY UINT64 + C_NAME STRING + C_ADDRESS STRING + C_NATIONKEY UINT64 + C_PHONE STRING + C_ACCTBAL DOUBLE + C_MKTSEGMENT STRING + C_COMMENT STRING + +Table: LineItem +Columns: + L_ORDERKEY UINT64 + L_PARTKEY UINT64 + L_SUPPKEY UINT64 + L_LINENUMBER UINT64 + L_QUANTITY DOUBLE + L_EXTENDEDPRICE DOUBLE + L_DISCOUNT DOUBLE + L_TAX DOUBLE + L_RETURNFLAG STRING + L_LINESTATUS STRING + L_SHIPDATE DATE + L_COMMITDATE DATE + L_RECEIPTDATE DATE + L_SHIPINSTRUCT STRING + L_SHIPMODE STRING + L_COMMENT STRING + +Table: Nation +Columns: + N_NATIONKEY UINT64 + N_NAME STRING + N_REGIONKEY UINT64 + N_COMMENT STRING + +Table: Orders +Columns: + O_ORDERKEY UINT64 + O_CUSTKEY UINT64 + O_ORDERSTATUS STRING + O_TOTALPRICE DOUBLE + O_ORDERDATE DATE + O_ORDERPRIORITY STRING + O_CLERK STRING + O_SHIPPRIORITY INT64 + O_COMMENT STRING + +Table: Part +Columns: + P_PARTKEY UINT64 + P_NAME STRING + P_MFGR STRING + P_BRAND STRING + P_TYPE STRING + P_SIZE INT64 + P_CONTAINER STRING + P_RETAILPRICE DOUBLE + P_COMMENT STRING + +Table: PartSupp +Columns: + PS_PARTKEY UINT64 + PS_SUPPKEY UINT64 + PS_AVAILQTY INT64 + PS_SUPPLYCOST DOUBLE + PS_COMMENT STRING + +Table: Region +Columns: + R_REGIONKEY UINT64 + R_NAME STRING + R_COMMENT STRING + +Table: Supplier +Columns: + S_SUPPKEY UINT64 + S_NAME STRING + S_ADDRESS STRING + S_NATIONKEY UINT64 + S_PHONE STRING + S_ACCTBAL DOUBLE + S_COMMENT STRING + diff --git a/zetasql/examples/tpch/pipe_queries/1.sql b/zetasql/examples/tpch/pipe_queries/1.sql new file mode 100644 index 000000000..d073b9512 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/1.sql @@ -0,0 +1,17 @@ +# A simple aggregate query with grouping and ordering collapses +# to just an AGGREGATE call using GROUP AND ORDER BY shorthand. +FROM lineitem +|> WHERE l_shipdate <= date_sub(date '1998-12-01', INTERVAL 74 day) +|> AGGREGATE + sum(l_quantity) AS sum_qty, + sum(l_extendedprice) AS sum_base_price, + sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + avg(l_quantity) AS avg_qty, + avg(l_extendedprice) AS avg_price, + avg(l_discount) AS avg_disc, + COUNT(*) AS count_order + GROUP + AND ORDER BY + l_returnflag, + l_linestatus; diff --git a/zetasql/examples/tpch/pipe_queries/10.sql b/zetasql/examples/tpch/pipe_queries/10.sql new file mode 100644 index 000000000..d8e8b70d3 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/10.sql @@ -0,0 +1,35 @@ +# This is written with SELECT after ORDER BY and LIMIT. +# The other order would work too since SELECT preserves order. +FROM + customer, + orders, + lineitem, + nation +|> WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate >= date '1994-02-01' + AND o_orderdate < date_add(date '1994-02-01', INTERVAL 3 month) + AND l_returnflag = 'R' + AND c_nationkey = n_nationkey +|> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS revenue, + GROUP BY + c_custkey, + c_name, + c_acctbal, + c_phone, + n_name, + c_address, + c_comment +|> ORDER BY revenue DESC +|> LIMIT 20 +|> SELECT + c_custkey, + c_name, + revenue, + c_acctbal, + n_name, + c_address, + c_phone, + c_comment; diff --git a/zetasql/examples/tpch/pipe_queries/11.sql b/zetasql/examples/tpch/pipe_queries/11.sql new file mode 100644 index 000000000..b97d04a49 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/11.sql @@ -0,0 +1,24 @@ +# HAVING is replaced by just a WHERE after the aggregate. +# It can use the `value` column rather than repeating the aggregate expression. +FROM + partsupp, + supplier, + nation +|> WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +|> AGGREGATE sum(ps_supplycost * ps_availqty) AS value + GROUP BY ps_partkey +|> WHERE + value > ( + FROM + partsupp, + supplier, + nation + |> WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' + |> AGGREGATE sum(ps_supplycost * ps_availqty) * 0.0001000000) +|> ORDER BY value DESC; diff --git a/zetasql/examples/tpch/pipe_queries/12.sql b/zetasql/examples/tpch/pipe_queries/12.sql new file mode 100644 index 000000000..c921b9cb0 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/12.sql @@ -0,0 +1,28 @@ +FROM + orders, + lineitem +|> WHERE + o_orderkey = l_orderkey + AND l_shipmode IN ('MAIL', 'AIR') + AND l_commitdate < l_receiptdate + AND l_shipdate < l_commitdate + AND l_receiptdate >= date '1997-01-01' + AND l_receiptdate < date_add(date '1997-01-01', INTERVAL 1 year) +|> AGGREGATE + sum( + CASE + WHEN + o_orderpriority = '1-URGENT' + OR o_orderpriority = '2-HIGH' + THEN 1 + ELSE 0 + END) AS high_line_count, + sum( + CASE + WHEN + o_orderpriority <> '1-URGENT' + AND o_orderpriority <> '2-HIGH' + THEN 1 + ELSE 0 + END) AS low_line_count + GROUP AND ORDER BY l_shipmode; diff --git a/zetasql/examples/tpch/pipe_queries/13.sql b/zetasql/examples/tpch/pipe_queries/13.sql new file mode 100644 index 000000000..b05858af9 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/13.sql @@ -0,0 +1,12 @@ +# This is much shorter because it can apply AGGREGATE twice for +# two-level aggregation without needing a subquery. +# This uses pipe JOIN. Using JOIN inside FROM would also work. +FROM customer +|> LEFT OUTER JOIN orders ON c_custkey = o_custkey AND o_comment NOT LIKE '%unusual%packages%' +|> AGGREGATE COUNT(o_orderkey) c_count + GROUP BY c_custkey +|> AGGREGATE COUNT(*) AS custdist + GROUP BY c_count +|> ORDER BY + custdist DESC, + c_count DESC; diff --git a/zetasql/examples/tpch/pipe_queries/14.sql b/zetasql/examples/tpch/pipe_queries/14.sql new file mode 100644 index 000000000..30c2d59fe --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/14.sql @@ -0,0 +1,16 @@ +FROM + lineitem, + part +|> WHERE + l_partkey = p_partkey + AND l_shipdate >= date '1994-03-01' + AND l_shipdate < date_add(date '1994-03-01', INTERVAL 1 month) +|> AGGREGATE + 100.00 + * sum( + CASE + WHEN p_type LIKE 'PROMO%' + THEN l_extendedprice * (1 - l_discount) + ELSE 0 + END) + / sum(l_extendedprice * (1 - l_discount)) AS promo_revenue; diff --git a/zetasql/examples/tpch/pipe_queries/15.sql b/zetasql/examples/tpch/pipe_queries/15.sql new file mode 100644 index 000000000..45ad83723 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/15.sql @@ -0,0 +1,29 @@ +# This uses a WITH clause, with the subquery in pipe syntax. +# It aliases the grouping column like the original query, although +# this is unnecessary. +WITH + revenue AS ( + FROM lineitem + |> WHERE + l_shipdate >= date '1997-05-01' + AND l_shipdate < date_add(date '1997-05-01', INTERVAL 3 month) + |> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS total_revenue + GROUP BY l_suppkey AS supplier_no + ) +FROM + supplier, + revenue +|> WHERE + s_suppkey = supplier_no + AND total_revenue = ( + SELECT max(total_revenue) + FROM revenue + ) +|> SELECT + s_suppkey, + s_name, + s_address, + s_phone, + total_revenue +|> ORDER BY s_suppkey; diff --git a/zetasql/examples/tpch/pipe_queries/16.sql b/zetasql/examples/tpch/pipe_queries/16.sql new file mode 100644 index 000000000..1562930a6 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/16.sql @@ -0,0 +1,24 @@ +FROM + partsupp, + part +|> WHERE + p_partkey = ps_partkey + AND p_brand <> 'Brand#13' + AND p_type NOT LIKE 'LARGE BURNISHED%' + AND p_size IN (39, 47, 37, 5, 20, 11, 25, 27) + AND ps_suppkey NOT IN ( + SELECT s_suppkey + FROM supplier + WHERE s_comment LIKE '%Customer%Complaints%' + ) +|> AGGREGATE + COUNT(DISTINCT ps_suppkey) AS supplier_cnt + GROUP BY + p_brand, + p_type, + p_size +|> ORDER BY + supplier_cnt DESC, + p_brand, + p_type, + p_size; diff --git a/zetasql/examples/tpch/pipe_queries/17.sql b/zetasql/examples/tpch/pipe_queries/17.sql new file mode 100644 index 000000000..fc4f4f0e5 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/17.sql @@ -0,0 +1,12 @@ +FROM + lineitem, + part +|> WHERE + p_partkey = l_partkey + AND p_brand = 'Brand#33' + AND p_container = 'LG DRUM' + AND l_quantity < ( + FROM lineitem + |> WHERE l_partkey = p_partkey + |> AGGREGATE 0.2 * avg(l_quantity)) +|> AGGREGATE sum(l_extendedprice) / 7.0 AS avg_yearly; diff --git a/zetasql/examples/tpch/pipe_queries/18.sql b/zetasql/examples/tpch/pipe_queries/18.sql new file mode 100644 index 000000000..25bab4eaf --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/18.sql @@ -0,0 +1,25 @@ +FROM + customer, + orders, + lineitem, + ( + FROM lineitem + |> AGGREGATE sum(l_quantity) AS sum_quantity + GROUP BY l_orderkey AS selected_l_orderkey + |> WHERE sum_quantity > 230 + ) +|> WHERE + o_orderkey = selected_l_orderkey + AND c_custkey = o_custkey + AND o_orderkey = l_orderkey +|> AGGREGATE sum(l_quantity) + GROUP BY + c_name, + c_custkey, + o_orderkey, + o_orderdate, + o_totalprice +|> ORDER BY + o_totalprice DESC, + o_orderdate +|> LIMIT 100; diff --git a/zetasql/examples/tpch/pipe_queries/19.sql b/zetasql/examples/tpch/pipe_queries/19.sql new file mode 100644 index 000000000..6ed6aac13 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/19.sql @@ -0,0 +1,36 @@ +FROM + lineitem, + part +|> WHERE + # Added this because optimizer is needed to pull this out of the OR. + p_partkey = l_partkey + AND ( + ( + p_partkey = l_partkey + AND p_brand = 'Brand#53' + # and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') + AND l_quantity >= 5 + AND l_quantity <= 5 + 10 + AND p_size BETWEEN 1 AND 5 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#41' + # and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') + AND l_quantity >= 15 + AND l_quantity <= 15 + 10 + AND p_size BETWEEN 1 AND 10 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#21' + # and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') + AND l_quantity >= 29 + AND l_quantity <= 29 + 10 + AND p_size BETWEEN 1 AND 15 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON')) +|> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS revenue; diff --git a/zetasql/examples/tpch/pipe_queries/2.sql b/zetasql/examples/tpch/pipe_queries/2.sql new file mode 100644 index 000000000..fd77c1e43 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/2.sql @@ -0,0 +1,43 @@ +FROM + part, + supplier, + partsupp, + nation, + region +|> WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND p_size = 19 + AND p_type LIKE '%COPPER' + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + AND ps_supplycost = ( + FROM + partsupp, + supplier, + nation, + region + |> WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + |> AGGREGATE + min(ps_supplycost)) +|> SELECT + s_acctbal, + s_name, + n_name, + p_partkey, + p_mfgr, + s_address, + s_phone, + s_comment +|> ORDER BY + s_acctbal DESC, + n_name, + s_name, + p_partkey +|> LIMIT 100; diff --git a/zetasql/examples/tpch/pipe_queries/20.sql b/zetasql/examples/tpch/pipe_queries/20.sql new file mode 100644 index 000000000..68fcefe34 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/20.sql @@ -0,0 +1,26 @@ +# All layers of expression subqueries are rewritten to pipe syntax here, +# although this is not required. +FROM + supplier, + nation +|> WHERE + s_suppkey IN ( + FROM + partsupp, + part + |> WHERE p_name LIKE 'tan%' + |> WHERE + ps_partkey = p_partkey + AND ps_availqty > ( + FROM lineitem + |> WHERE + l_partkey = ps_partkey + AND l_suppkey = ps_suppkey + AND l_shipdate >= date '1996-01-01' + AND l_shipdate < date_add(date '1996-01-01', INTERVAL 1 year) + |> AGGREGATE 0.5 * sum(l_quantity)) + |> SELECT ps_suppkey) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +|> SELECT s_name, s_address +|> ORDER BY s_name; diff --git a/zetasql/examples/tpch/pipe_queries/21.sql b/zetasql/examples/tpch/pipe_queries/21.sql new file mode 100644 index 000000000..5b91d9fe6 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/21.sql @@ -0,0 +1,30 @@ +# EXISTS subqueries omit the placeholder SELECTs. +FROM + supplier, + lineitem l1, + orders, + nation +|> WHERE + s_suppkey = l1.l_suppkey + AND o_orderkey = l1.l_orderkey + AND o_orderstatus = 'F' + AND l1.l_receiptdate > l1.l_commitdate + AND EXISTS( + FROM lineitem l2 + |> WHERE + l2.l_orderkey = l1.l_orderkey + AND l2.l_suppkey <> l1.l_suppkey) + AND NOT EXISTS( + FROM lineitem l3 + |> WHERE + l3.l_orderkey = l1.l_orderkey + AND l3.l_suppkey <> l1.l_suppkey + AND l3.l_receiptdate > l3.l_commitdate) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +|> AGGREGATE COUNT(*) AS numwait + GROUP BY s_name +|> ORDER BY + numwait DESC, + s_name +|> LIMIT 100; diff --git a/zetasql/examples/tpch/pipe_queries/22.sql b/zetasql/examples/tpch/pipe_queries/22.sql new file mode 100644 index 000000000..eb76818ea --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/22.sql @@ -0,0 +1,21 @@ +# A subquery isn't needed to compute an expression column so it +# can be referenced multiple times. +# It can just be computed directly in GROUP BY and given an alias. +FROM customer +|> WHERE + substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + AND c_acctbal > ( + SELECT avg(c_acctbal) + FROM customer + WHERE + c_acctbal > 0.00 + AND substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + ) + AND NOT EXISTS( + FROM orders + |> WHERE o_custkey = c_custkey + ) +|> AGGREGATE + COUNT(*) AS numcust, + sum(c_acctbal) AS totacctbal + GROUP AND ORDER BY substr(c_phone, 1, 2) AS cntrycode; diff --git a/zetasql/examples/tpch/pipe_queries/3.sql b/zetasql/examples/tpch/pipe_queries/3.sql new file mode 100644 index 000000000..8c5e0dc2a --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/3.sql @@ -0,0 +1,25 @@ +FROM + customer, + orders, + lineitem +|> WHERE + c_mktsegment = 'FURNITURE' + AND c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate < date '1995-03-29' + AND l_shipdate > date '1995-03-29' +|> AGGREGATE + sum(l_extendedprice * (1 - l_discount)) AS revenue, + GROUP BY + l_orderkey, + o_orderdate, + o_shippriority +|> SELECT + l_orderkey, + revenue, + o_orderdate, + o_shippriority +|> ORDER BY + revenue DESC, + o_orderdate +|> LIMIT 10; diff --git a/zetasql/examples/tpch/pipe_queries/4.sql b/zetasql/examples/tpch/pipe_queries/4.sql new file mode 100644 index 000000000..03e200351 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/4.sql @@ -0,0 +1,16 @@ +# The simple outer aggregate involved repeating columns across +# SELECT, GROUP BY and ORDERY. In pipe syntax, all of that is +# just once with AGGREGATE, using GROUP AND ORDER BY. +# Also, the EXISTS subquery is written without a placeholder SELECT list. +FROM + orders +|> WHERE + o_orderdate >= date '1997-06-01' + AND o_orderdate < date_add(date '1997-06-01', INTERVAL 3 month) + AND EXISTS( + FROM lineitem + |> WHERE + l_orderkey = o_orderkey + AND l_commitdate < l_receiptdate) +|> AGGREGATE COUNT(*) AS order_count + GROUP AND ORDER BY o_orderpriority; diff --git a/zetasql/examples/tpch/pipe_queries/5.sql b/zetasql/examples/tpch/pipe_queries/5.sql new file mode 100644 index 000000000..c9ffc4658 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/5.sql @@ -0,0 +1,19 @@ +FROM + customer, + orders, + lineitem, + supplier, + nation, + region +|> WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND l_suppkey = s_suppkey + AND c_nationkey = s_nationkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'AFRICA' + AND o_orderdate >= date '1994-01-01' + AND o_orderdate < date_add(date '1994-01-01', INTERVAL 1 year) +|> AGGREGATE sum(l_extendedprice * (1 - l_discount)) AS revenue DESC + GROUP BY n_name; diff --git a/zetasql/examples/tpch/pipe_queries/6.sql b/zetasql/examples/tpch/pipe_queries/6.sql new file mode 100644 index 000000000..d86306d5f --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/6.sql @@ -0,0 +1,9 @@ +FROM + lineitem +|> WHERE + l_shipdate >= date '1994-01-01' + AND l_shipdate < date_add(date '1994-01-01', INTERVAL 1 year) + AND l_discount BETWEEN 0.08 - 0.01 AND 0.08 + 0.01 + AND l_quantity < 25 +|> AGGREGATE + sum(l_extendedprice * l_discount) AS revenue; diff --git a/zetasql/examples/tpch/pipe_queries/7.sql b/zetasql/examples/tpch/pipe_queries/7.sql new file mode 100644 index 000000000..fb87d78b9 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/7.sql @@ -0,0 +1,33 @@ +# Removes a subquery in favor of linear flow, with SELECT to compute +# expressions before the AGGREGATE. +# The final SELECT is unnecessary since it just selects the grouping and +# aggregate columns with AGGREGATE already produces. +FROM + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2 +|> WHERE + s_suppkey = l_suppkey + AND o_orderkey = l_orderkey + AND c_custkey = o_custkey + AND s_nationkey = n1.n_nationkey + AND c_nationkey = n2.n_nationkey + AND ( + (n1.n_name = 'SAUDI ARABIA' AND n2.n_name = 'UNITED KINGDOM') + OR (n1.n_name = 'UNITED KINGDOM' AND n2.n_name = 'SAUDI ARABIA')) + AND l_shipdate BETWEEN date '1995-01-01' AND date '1996-12-31' +|> SELECT + n1.n_name AS supp_nation, + n2.n_name AS cust_nation, + EXTRACT(year FROM l_shipdate) AS l_year, + l_extendedprice * (1 - l_discount) AS volume +|> AGGREGATE + sum(volume) AS revenue + GROUP + AND ORDER BY + supp_nation, + cust_nation, + l_year; diff --git a/zetasql/examples/tpch/pipe_queries/8.sql b/zetasql/examples/tpch/pipe_queries/8.sql new file mode 100644 index 000000000..a1c280d08 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/8.sql @@ -0,0 +1,31 @@ +# The subquery is unnecessary. Linear pipe flow works without it. +# Selecting n2.n_name and aliasing it is unnecessary since the original +# columns (and their table aliases) are still present after using EXTEND +# to compute expressions. +FROM + part, + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2, + region +|> WHERE + p_partkey = l_partkey + AND s_suppkey = l_suppkey + AND l_orderkey = o_orderkey + AND o_custkey = c_custkey + AND c_nationkey = n1.n_nationkey + AND n1.n_regionkey = r_regionkey + AND r_name = 'AMERICA' + AND s_nationkey = n2.n_nationkey + AND o_orderdate BETWEEN date '1993-01-01' AND date '1997-12-31' + AND p_type = 'STANDARD POLISHED TIN' +|> EXTEND + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) AS volume +|> AGGREGATE + sum(CASE WHEN n2.n_name = 'PERU' THEN volume ELSE 0 END) / sum(volume) AS mkt_share + GROUP BY + o_year ASC; diff --git a/zetasql/examples/tpch/pipe_queries/9.sql b/zetasql/examples/tpch/pipe_queries/9.sql new file mode 100644 index 000000000..812a77b83 --- /dev/null +++ b/zetasql/examples/tpch/pipe_queries/9.sql @@ -0,0 +1,25 @@ +# Removes unnecessary subquery and internal SELECT and aliasing of +# n_name column by using EXTEND to compute expressions. +FROM + part, + supplier, + lineitem, + partsupp, + orders, + nation +|> WHERE + s_suppkey = l_suppkey + AND ps_suppkey = l_suppkey + AND ps_partkey = l_partkey + AND p_partkey = l_partkey + AND o_orderkey = l_orderkey + AND s_nationkey = n_nationkey + AND p_name LIKE '%tomato%' +|> EXTEND + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount +|> AGGREGATE + sum(amount) AS sum_profit + GROUP BY + n_name AS nation ASC, + o_year DESC; diff --git a/zetasql/examples/tpch/queries/1.sql b/zetasql/examples/tpch/queries/1.sql new file mode 100644 index 000000000..4f3275d06 --- /dev/null +++ b/zetasql/examples/tpch/queries/1.sql @@ -0,0 +1,21 @@ +SELECT + l_returnflag, + l_linestatus, + sum(l_quantity) AS sum_qty, + sum(l_extendedprice) AS sum_base_price, + sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price, + sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, + avg(l_quantity) AS avg_qty, + avg(l_extendedprice) AS avg_price, + avg(l_discount) AS avg_disc, + COUNT(*) AS count_order +FROM + lineitem +WHERE + l_shipdate <= date_sub(date '1998-12-01', INTERVAL 74 day) +GROUP BY + l_returnflag, + l_linestatus +ORDER BY + l_returnflag, + l_linestatus; diff --git a/zetasql/examples/tpch/queries/10.sql b/zetasql/examples/tpch/queries/10.sql new file mode 100644 index 000000000..b2684c99d --- /dev/null +++ b/zetasql/examples/tpch/queries/10.sql @@ -0,0 +1,32 @@ +SELECT + c_custkey, + c_name, + sum(l_extendedprice * (1 - l_discount)) AS revenue, + c_acctbal, + n_name, + c_address, + c_phone, + c_comment +FROM + customer, + orders, + lineitem, + nation +WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate >= date '1994-02-01' + AND o_orderdate < date_add(date '1994-02-01', INTERVAL 3 month) + AND l_returnflag = 'R' + AND c_nationkey = n_nationkey +GROUP BY + c_custkey, + c_name, + c_acctbal, + c_phone, + n_name, + c_address, + c_comment +ORDER BY + revenue DESC +LIMIT 20; diff --git a/zetasql/examples/tpch/queries/11.sql b/zetasql/examples/tpch/queries/11.sql new file mode 100644 index 000000000..4c1a0e4d9 --- /dev/null +++ b/zetasql/examples/tpch/queries/11.sql @@ -0,0 +1,26 @@ +SELECT + ps_partkey, + sum(ps_supplycost * ps_availqty) AS value +FROM + partsupp, + supplier, + nation +WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +GROUP BY ps_partkey +HAVING + sum(ps_supplycost * ps_availqty) + > ( + SELECT sum(ps_supplycost * ps_availqty) * 0.0001000000 + FROM + partsupp, + supplier, + nation + WHERE + ps_suppkey = s_suppkey + AND s_nationkey = n_nationkey + AND n_name = 'PERU' + ) +ORDER BY value DESC; diff --git a/zetasql/examples/tpch/queries/12.sql b/zetasql/examples/tpch/queries/12.sql new file mode 100644 index 000000000..6c0f4eb58 --- /dev/null +++ b/zetasql/examples/tpch/queries/12.sql @@ -0,0 +1,32 @@ +SELECT + l_shipmode, + sum( + CASE + WHEN + o_orderpriority = '1-URGENT' + OR o_orderpriority = '2-HIGH' + THEN 1 + ELSE 0 + END) AS high_line_count, + sum( + CASE + WHEN + o_orderpriority <> '1-URGENT' + AND o_orderpriority <> '2-HIGH' + THEN 1 + ELSE 0 + END) AS low_line_count +FROM + orders, + lineitem +WHERE + o_orderkey = l_orderkey + AND l_shipmode IN ('MAIL', 'AIR') + AND l_commitdate < l_receiptdate + AND l_shipdate < l_commitdate + AND l_receiptdate >= date '1997-01-01' + AND l_receiptdate < date_add(date '1997-01-01', INTERVAL 1 year) +GROUP BY + l_shipmode +ORDER BY + l_shipmode; diff --git a/zetasql/examples/tpch/queries/13.sql b/zetasql/examples/tpch/queries/13.sql new file mode 100644 index 000000000..e0811753e --- /dev/null +++ b/zetasql/examples/tpch/queries/13.sql @@ -0,0 +1,22 @@ +SELECT + c_count, + COUNT(*) AS custdist +FROM + ( + SELECT + c_custkey, + COUNT(o_orderkey) c_count + FROM + customer + LEFT OUTER JOIN orders + ON + c_custkey = o_custkey + AND o_comment NOT LIKE '%unusual%packages%' + GROUP BY + c_custkey + ) AS c_orders +GROUP BY + c_count +ORDER BY + custdist DESC, + c_count DESC; diff --git a/zetasql/examples/tpch/queries/14.sql b/zetasql/examples/tpch/queries/14.sql new file mode 100644 index 000000000..7793bd301 --- /dev/null +++ b/zetasql/examples/tpch/queries/14.sql @@ -0,0 +1,16 @@ +SELECT + 100.00 + * sum( + CASE + WHEN p_type LIKE 'PROMO%' + THEN l_extendedprice * (1 - l_discount) + ELSE 0 + END) + / sum(l_extendedprice * (1 - l_discount)) AS promo_revenue +FROM + lineitem, + part +WHERE + l_partkey = p_partkey + AND l_shipdate >= date '1994-03-01' + AND l_shipdate < date_add(date '1994-03-01', INTERVAL 1 month); diff --git a/zetasql/examples/tpch/queries/15.sql b/zetasql/examples/tpch/queries/15.sql new file mode 100644 index 000000000..1b7b043e8 --- /dev/null +++ b/zetasql/examples/tpch/queries/15.sql @@ -0,0 +1,27 @@ +WITH + revenue AS ( + SELECT + l_suppkey AS supplier_no, + sum(l_extendedprice * (1 - l_discount)) AS total_revenue + FROM lineitem + WHERE + l_shipdate >= date '1997-05-01' + AND l_shipdate < date_add(date '1997-05-01', INTERVAL 3 month) + GROUP BY l_suppkey + ) +SELECT + s_suppkey, + s_name, + s_address, + s_phone, + total_revenue +FROM + supplier, + revenue +WHERE + s_suppkey = supplier_no + AND total_revenue = ( + SELECT max(total_revenue) + FROM revenue + ) +ORDER BY s_suppkey; diff --git a/zetasql/examples/tpch/queries/16.sql b/zetasql/examples/tpch/queries/16.sql new file mode 100644 index 000000000..aff60b04e --- /dev/null +++ b/zetasql/examples/tpch/queries/16.sql @@ -0,0 +1,27 @@ +SELECT + p_brand, + p_type, + p_size, + COUNT(DISTINCT ps_suppkey) AS supplier_cnt +FROM + partsupp, + part +WHERE + p_partkey = ps_partkey + AND p_brand <> 'Brand#13' + AND p_type NOT LIKE 'LARGE BURNISHED%' + AND p_size IN (39, 47, 37, 5, 20, 11, 25, 27) + AND ps_suppkey NOT IN ( + SELECT s_suppkey + FROM supplier + WHERE s_comment LIKE '%Customer%Complaints%' + ) +GROUP BY + p_brand, + p_type, + p_size +ORDER BY + supplier_cnt DESC, + p_brand, + p_type, + p_size; diff --git a/zetasql/examples/tpch/queries/17.sql b/zetasql/examples/tpch/queries/17.sql new file mode 100644 index 000000000..88025f980 --- /dev/null +++ b/zetasql/examples/tpch/queries/17.sql @@ -0,0 +1,17 @@ +SELECT + sum(l_extendedprice) / 7.0 AS avg_yearly +FROM + lineitem, + part +WHERE + p_partkey = l_partkey + AND p_brand = 'Brand#33' + AND p_container = 'LG DRUM' + AND l_quantity < ( + SELECT + 0.2 * avg(l_quantity) + FROM + lineitem + WHERE + l_partkey = p_partkey + ); diff --git a/zetasql/examples/tpch/queries/18.sql b/zetasql/examples/tpch/queries/18.sql new file mode 100644 index 000000000..4410d2391 --- /dev/null +++ b/zetasql/examples/tpch/queries/18.sql @@ -0,0 +1,33 @@ +SELECT + c_name, + c_custkey, + o_orderkey, + o_orderdate, + o_totalprice, + sum(l_quantity) +FROM + customer, + orders, + lineitem, + # Moved IN subquery to a table subquery and a join, since there's no + # optimizer to do it automatically. + ( + SELECT l_orderkey AS selected_l_orderkey + FROM lineitem + GROUP BY l_orderkey + HAVING sum(l_quantity) > 230 + ) +WHERE + o_orderkey = selected_l_orderkey + AND c_custkey = o_custkey + AND o_orderkey = l_orderkey +GROUP BY + c_name, + c_custkey, + o_orderkey, + o_orderdate, + o_totalprice +ORDER BY + o_totalprice DESC, + o_orderdate +LIMIT 100; diff --git a/zetasql/examples/tpch/queries/19.sql b/zetasql/examples/tpch/queries/19.sql new file mode 100644 index 000000000..f28fc3552 --- /dev/null +++ b/zetasql/examples/tpch/queries/19.sql @@ -0,0 +1,36 @@ +SELECT + sum(l_extendedprice * (1 - l_discount)) AS revenue +FROM + lineitem, + part +WHERE + # Added this because optimizer is needed to pull this out of the OR. + p_partkey = l_partkey + AND ( + ( + p_partkey = l_partkey + AND p_brand = 'Brand#53' + # and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') + AND l_quantity >= 5 + AND l_quantity <= 5 + 10 + AND p_size BETWEEN 1 AND 5 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#41' + # and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') + AND l_quantity >= 15 + AND l_quantity <= 15 + 10 + AND p_size BETWEEN 1 AND 10 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON') + OR ( + p_partkey = l_partkey + AND p_brand = 'Brand#21' + # and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') + AND l_quantity >= 29 + AND l_quantity <= 29 + 10 + AND p_size BETWEEN 1 AND 15 + # and l_shipmode in ('AIR', 'AIR REG') + AND l_shipinstruct = 'DELIVER IN PERSON')); diff --git a/zetasql/examples/tpch/queries/2.sql b/zetasql/examples/tpch/queries/2.sql new file mode 100644 index 000000000..2bf777a0c --- /dev/null +++ b/zetasql/examples/tpch/queries/2.sql @@ -0,0 +1,44 @@ +SELECT + s_acctbal, + s_name, + n_name, + p_partkey, + p_mfgr, + s_address, + s_phone, + s_comment +FROM + part, + supplier, + partsupp, + nation, + region +WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND p_size = 19 + AND p_type LIKE '%COPPER' + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + AND ps_supplycost = ( + SELECT + min(ps_supplycost) + FROM + partsupp, + supplier, + nation, + region + WHERE + p_partkey = ps_partkey + AND s_suppkey = ps_suppkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'MIDDLE EAST' + ) +ORDER BY + s_acctbal DESC, + n_name, + s_name, + p_partkey +LIMIT 100; diff --git a/zetasql/examples/tpch/queries/20.sql b/zetasql/examples/tpch/queries/20.sql new file mode 100644 index 000000000..b4dea0cbc --- /dev/null +++ b/zetasql/examples/tpch/queries/20.sql @@ -0,0 +1,32 @@ +SELECT + s_name, + s_address +FROM + supplier, + nation +WHERE + s_suppkey IN ( + SELECT ps_suppkey + FROM + partsupp, + ( + SELECT p_partkey + FROM part + WHERE p_name LIKE 'tan%' + ) AS selected_parts + WHERE + ps_partkey = p_partkey + AND ps_availqty > ( + SELECT 0.5 * sum(l_quantity) + FROM lineitem + WHERE + l_partkey = ps_partkey + AND l_suppkey = ps_suppkey + AND l_shipdate >= date '1996-01-01' + AND l_shipdate < date_add(date '1996-01-01', INTERVAL 1 year) + ) + ) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +ORDER BY + s_name; diff --git a/zetasql/examples/tpch/queries/21.sql b/zetasql/examples/tpch/queries/21.sql new file mode 100644 index 000000000..f53a1c960 --- /dev/null +++ b/zetasql/examples/tpch/queries/21.sql @@ -0,0 +1,36 @@ +SELECT + s_name, + COUNT(*) AS numwait +FROM + supplier, + lineitem l1, + orders, + nation +WHERE + s_suppkey = l1.l_suppkey + AND o_orderkey = l1.l_orderkey + AND o_orderstatus = 'F' + AND l1.l_receiptdate > l1.l_commitdate + AND EXISTS( + SELECT * + FROM lineitem l2 + WHERE + l2.l_orderkey = l1.l_orderkey + AND l2.l_suppkey <> l1.l_suppkey + ) + AND NOT EXISTS( + SELECT * + FROM lineitem l3 + WHERE + l3.l_orderkey = l1.l_orderkey + AND l3.l_suppkey <> l1.l_suppkey + AND l3.l_receiptdate > l3.l_commitdate + ) + AND s_nationkey = n_nationkey + AND n_name = 'PERU' +GROUP BY + s_name +ORDER BY + numwait DESC, + s_name +LIMIT 100; diff --git a/zetasql/examples/tpch/queries/22.sql b/zetasql/examples/tpch/queries/22.sql new file mode 100644 index 000000000..6392f548b --- /dev/null +++ b/zetasql/examples/tpch/queries/22.sql @@ -0,0 +1,27 @@ +SELECT + cntrycode, + COUNT(*) AS numcust, + sum(c_acctbal) AS totacctbal +FROM + ( + SELECT + substr(c_phone, 1, 2) AS cntrycode, + c_acctbal + FROM customer + WHERE + substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + AND c_acctbal > ( + SELECT avg(c_acctbal) + FROM customer + WHERE + c_acctbal > 0.00 + AND substr(c_phone, 1, 2) IN ('10', '19', '14', '22', '23', '31', '13') + ) + AND NOT EXISTS( + SELECT * + FROM orders + WHERE o_custkey = c_custkey + ) + ) AS custsale +GROUP BY cntrycode +ORDER BY cntrycode; diff --git a/zetasql/examples/tpch/queries/3.sql b/zetasql/examples/tpch/queries/3.sql new file mode 100644 index 000000000..fd721fea7 --- /dev/null +++ b/zetasql/examples/tpch/queries/3.sql @@ -0,0 +1,23 @@ +SELECT + l_orderkey, + sum(l_extendedprice * (1 - l_discount)) AS revenue, + o_orderdate, + o_shippriority +FROM + customer, + orders, + lineitem +WHERE + c_mktsegment = 'FURNITURE' + AND c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND o_orderdate < date '1995-03-29' + AND l_shipdate > date '1995-03-29' +GROUP BY + l_orderkey, + o_orderdate, + o_shippriority +ORDER BY + revenue DESC, + o_orderdate +LIMIT 10; diff --git a/zetasql/examples/tpch/queries/4.sql b/zetasql/examples/tpch/queries/4.sql new file mode 100644 index 000000000..463ec6bc9 --- /dev/null +++ b/zetasql/examples/tpch/queries/4.sql @@ -0,0 +1,21 @@ +SELECT + o_orderpriority, + COUNT(*) AS order_count +FROM + orders +WHERE + o_orderdate >= date '1997-06-01' + AND o_orderdate < date_add(date '1997-06-01', INTERVAL 3 month) + AND EXISTS( + SELECT + * + FROM + lineitem + WHERE + l_orderkey = o_orderkey + AND l_commitdate < l_receiptdate + ) +GROUP BY + o_orderpriority +ORDER BY + o_orderpriority; diff --git a/zetasql/examples/tpch/queries/5.sql b/zetasql/examples/tpch/queries/5.sql new file mode 100644 index 000000000..90020b02f --- /dev/null +++ b/zetasql/examples/tpch/queries/5.sql @@ -0,0 +1,24 @@ +SELECT + n_name, + sum(l_extendedprice * (1 - l_discount)) AS revenue +FROM + customer, + orders, + lineitem, + supplier, + nation, + region +WHERE + c_custkey = o_custkey + AND l_orderkey = o_orderkey + AND l_suppkey = s_suppkey + AND c_nationkey = s_nationkey + AND s_nationkey = n_nationkey + AND n_regionkey = r_regionkey + AND r_name = 'AFRICA' + AND o_orderdate >= date '1994-01-01' + AND o_orderdate < date_add(date '1994-01-01', INTERVAL 1 year) +GROUP BY + n_name +ORDER BY + revenue DESC; diff --git a/zetasql/examples/tpch/queries/6.sql b/zetasql/examples/tpch/queries/6.sql new file mode 100644 index 000000000..d8a7375c1 --- /dev/null +++ b/zetasql/examples/tpch/queries/6.sql @@ -0,0 +1,9 @@ +SELECT + sum(l_extendedprice * l_discount) AS revenue +FROM + lineitem +WHERE + l_shipdate >= date '1994-01-01' + AND l_shipdate < date_add(date '1994-01-01', INTERVAL 1 year) + AND l_discount BETWEEN 0.08 - 0.01 AND 0.08 + 0.01 + AND l_quantity < 25; diff --git a/zetasql/examples/tpch/queries/7.sql b/zetasql/examples/tpch/queries/7.sql new file mode 100644 index 000000000..ce5428410 --- /dev/null +++ b/zetasql/examples/tpch/queries/7.sql @@ -0,0 +1,38 @@ +SELECT + supp_nation, + cust_nation, + l_year, + sum(volume) AS revenue +FROM + ( + SELECT + n1.n_name AS supp_nation, + n2.n_name AS cust_nation, + EXTRACT(year FROM l_shipdate) AS l_year, + l_extendedprice * (1 - l_discount) AS volume + FROM + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2 + WHERE + s_suppkey = l_suppkey + AND o_orderkey = l_orderkey + AND c_custkey = o_custkey + AND s_nationkey = n1.n_nationkey + AND c_nationkey = n2.n_nationkey + AND ( + (n1.n_name = 'SAUDI ARABIA' AND n2.n_name = 'UNITED KINGDOM') + OR (n1.n_name = 'UNITED KINGDOM' AND n2.n_name = 'SAUDI ARABIA')) + AND l_shipdate BETWEEN date '1995-01-01' AND date '1996-12-31' + ) AS shipping +GROUP BY + supp_nation, + cust_nation, + l_year +ORDER BY + supp_nation, + cust_nation, + l_year; diff --git a/zetasql/examples/tpch/queries/8.sql b/zetasql/examples/tpch/queries/8.sql new file mode 100644 index 000000000..0d861605f --- /dev/null +++ b/zetasql/examples/tpch/queries/8.sql @@ -0,0 +1,34 @@ +SELECT + o_year, + sum(CASE WHEN nation = 'PERU' THEN volume ELSE 0 END) / sum(volume) AS mkt_share +FROM + ( + SELECT + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) AS volume, + n2.n_name AS nation + FROM + part, + supplier, + lineitem, + orders, + customer, + nation n1, + nation n2, + region + WHERE + p_partkey = l_partkey + AND s_suppkey = l_suppkey + AND l_orderkey = o_orderkey + AND o_custkey = c_custkey + AND c_nationkey = n1.n_nationkey + AND n1.n_regionkey = r_regionkey + AND r_name = 'AMERICA' + AND s_nationkey = n2.n_nationkey + AND o_orderdate BETWEEN date '1993-01-01' AND date '1997-12-31' + AND p_type = 'STANDARD POLISHED TIN' + ) AS all_nations +GROUP BY + o_year +ORDER BY + o_year; diff --git a/zetasql/examples/tpch/queries/9.sql b/zetasql/examples/tpch/queries/9.sql new file mode 100644 index 000000000..260df2ecb --- /dev/null +++ b/zetasql/examples/tpch/queries/9.sql @@ -0,0 +1,32 @@ +SELECT + nation, + o_year, + sum(amount) AS sum_profit +FROM + ( + SELECT + n_name AS nation, + EXTRACT(year FROM o_orderdate) AS o_year, + l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity AS amount + FROM + part, + supplier, + lineitem, + partsupp, + orders, + nation + WHERE + s_suppkey = l_suppkey + AND ps_suppkey = l_suppkey + AND ps_partkey = l_partkey + AND p_partkey = l_partkey + AND o_orderkey = l_orderkey + AND s_nationkey = n_nationkey + AND p_name LIKE '%tomato%' + ) AS profit +GROUP BY + nation, + o_year +ORDER BY + nation, + o_year DESC; diff --git a/zetasql/local_service/local_service.cc b/zetasql/local_service/local_service.cc index 18529afd7..97d4bf393 100644 --- a/zetasql/local_service/local_service.cc +++ b/zetasql/local_service/local_service.cc @@ -1519,9 +1519,9 @@ absl::Status ZetaSqlLocalServiceImpl::Parse(const ParseRequest& request, ? std::make_unique(request.options()) : std::make_unique(); - ParserOptions parser_options = - ParserOptions(/*id_string_pool=*/nullptr, - /*arena=*/nullptr, language_options.get()); + ParserOptions parser_options = ParserOptions( + /*id_string_pool=*/nullptr, /*arena=*/nullptr, + language_options.get() ? *language_options.get() : LanguageOptions()); if (request.allow_script()) { return ParseScriptImpl(request, response, parser_options); diff --git a/zetasql/local_service/local_service_test.cc b/zetasql/local_service/local_service_test.cc index f8f9b0e2c..d4b1f8d98 100644 --- a/zetasql/local_service/local_service_test.cc +++ b/zetasql/local_service/local_service_test.cc @@ -785,7 +785,6 @@ TEST_F(ZetaSqlLocalServiceImplTest, Parse) { } is_nested: false is_pivot_input: false - is_graph_subquery: false } } })pb", diff --git a/zetasql/parser/BUILD b/zetasql/parser/BUILD index 433cf157a..f30ce7885 100644 --- a/zetasql/parser/BUILD +++ b/zetasql/parser/BUILD @@ -19,6 +19,7 @@ load("@com_google_protobuf//:protobuf.bzl", "py_proto_library") load("//bazel:bison.bzl", "genyacc") load("//bazel:flex.bzl", "genlex") load("//bazel:textmapper.bzl", "tm_syntax") + # Placeholder: load py_proto_library # Placeholder: load py_binary # Placeholder: load py_library @@ -211,8 +212,8 @@ cc_library( ":flex_istream", ":flex_tokenizer", ":keywords", + ":lookahead_transformer", ":parse_tree", - ":token_disambiguator", "//bazel:flex", "//zetasql/base", "//zetasql/base:arena", @@ -297,9 +298,9 @@ cc_library( ) cc_library( - name = "token_disambiguator", - srcs = ["token_disambiguator.cc"], - hdrs = ["token_disambiguator.h"], + name = "lookahead_transformer", + srcs = ["lookahead_transformer.cc"], + hdrs = ["lookahead_transformer.h"], deps = [ ":bison_parser_mode", ":bison_token_codes", @@ -326,15 +327,14 @@ cc_library( ) cc_test( - name = "flex_tokenizer_test", + name = "lookahead_transformer_test", srcs = [ - "flex_tokenizer_test.cc", + "lookahead_transformer_test.cc", ], deps = [ ":bison_parser_mode", ":bison_token_codes", - ":flex_tokenizer", - ":token_disambiguator", + ":lookahead_transformer", "//zetasql/base:arena", "//zetasql/base:check", "//zetasql/base:ret_check", @@ -468,7 +468,9 @@ cc_library( "//zetasql/base:arena", "//zetasql/base:check", "//zetasql/base:status", + "//zetasql/common:errors", "//zetasql/parser/macros:macro_catalog", + "//zetasql/public:error_helpers", "//zetasql/public:language_options", "//zetasql/public:options_cc_proto", "//zetasql/public/proto:logging_cc_proto", @@ -537,16 +539,18 @@ cc_test( deps = [ ":parse_tree", ":parser", - "//zetasql/base:source_location", + "//zetasql/base:check", "//zetasql/base:status", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/common:errors", "//zetasql/common:status_payload_utils", "//zetasql/proto:internal_error_location_cc_proto", + "//zetasql/public:error_helpers", "//zetasql/public:error_location_cc_proto", "//zetasql/public:parse_location", "//zetasql/testdata:test_schema_cc_proto", + "@com_google_absl//absl/log", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", diff --git a/zetasql/parser/ast_enums.proto b/zetasql/parser/ast_enums.proto index 2ec1b364d..657a5f7f1 100644 --- a/zetasql/parser/ast_enums.proto +++ b/zetasql/parser/ast_enums.proto @@ -36,6 +36,7 @@ enum SchemaObjectKind { kInvalidSchemaObjectKind = 1; kAggregateFunction = 2; kApproxView = 17; + kConnection = 19; kConstant = 3; kDatabase = 4; kExternalTable = 5; @@ -382,3 +383,13 @@ message ASTSpannerInterleaveClauseEnums { IN_PARENT = 2; } } + +message ASTRowPatternOperationEnums { + enum OperationType { + OPERATION_TYPE_UNSPECIFIED = 0; + CONCAT = 1; + ALTERNATE = 2; + PERMUTE = 3; + EXCLUDE = 4; // {- ... -} + } +} diff --git a/zetasql/parser/bison_parser.cc b/zetasql/parser/bison_parser.cc index f5ece4366..5eeab2a7a 100644 --- a/zetasql/parser/bison_parser.cc +++ b/zetasql/parser/bison_parser.cc @@ -32,8 +32,8 @@ #include "zetasql/parser/ast_node.h" #include "zetasql/parser/bison_parser.bison.h" #include "zetasql/parser/keywords.h" +#include "zetasql/parser/lookahead_transformer.h" #include "zetasql/parser/macros/macro_catalog.h" -#include "zetasql/parser/token_disambiguator.h" #include "zetasql/public/id_string.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" @@ -254,6 +254,10 @@ static absl::StatusOr GenerateImprovedBisonSyntaxError( // precedence parsing. expectations_set.erase("$end"); + // Avoid suggesting pipe characters since the syntax might not be enabled, + // and to avoid adding this on every error that looks for a ")" after a query. + expectations_set.erase("|>"); + // Avoid suggesting FROM every place a query might show up. // This is usually a bad suggestion anyway. expectations_set.erase("keyword FROM"); @@ -288,11 +292,11 @@ static absl::StatusOr GenerateImprovedBisonSyntaxError( // start the tokenizer in kTokenizer mode because we don't need to get a bogus // token at the start to indicate the statement type. That token interferes // with errors at offset 0. - ZETASQL_ASSIGN_OR_RETURN( - auto tokenizer, - DisambiguatorLexer::Create(BisonParserMode::kTokenizerPreserveComments, - error_location.filename(), input, start_offset, - language_options, macro_catalog, arena)); + ZETASQL_ASSIGN_OR_RETURN(auto tokenizer, + LookaheadTransformer::Create( + BisonParserMode::kTokenizerPreserveComments, + error_location.filename(), input, start_offset, + language_options, macro_catalog, arena)); ParseLocationRange token_location; int token = -1; while (token != 0) { @@ -404,7 +408,7 @@ static absl::Status ParseWithBison( ASTStatementProperties* ast_statement_properties, int* statement_end_byte_offset, bool& format_error, int64_t& out_num_lexical_tokens) { - ZETASQL_ASSIGN_OR_RETURN(auto tokenizer, DisambiguatorLexer::Create( + ZETASQL_ASSIGN_OR_RETURN(auto tokenizer, LookaheadTransformer::Create( mode, filename, input, start_byte_offset, language_options, macro_catalog, arena)); diff --git a/zetasql/parser/bison_parser.y b/zetasql/parser/bison_parser.y index 10192b576..88e994373 100644 --- a/zetasql/parser/bison_parser.y +++ b/zetasql/parser/bison_parser.y @@ -44,21 +44,6 @@ #ifndef YYDEBUG #define YYDEBUG 0 #endif - -// Define the handling of our custom ParseLocationRange. -# define YYLLOC_DEFAULT(Cur, Rhs, N) \ -do \ - if (N) \ - { \ - (Cur).set_start(YYRHSLOC(Rhs, 1).start()); \ - (Cur).set_end(YYRHSLOC(Rhs, N).end()); \ - } \ - else \ - { \ - (Cur).set_start(YYRHSLOC(Rhs, 0).end()); \ - (Cur).set_end(YYRHSLOC(Rhs, 0).end()); \ - } \ -while (0) } %defines @@ -86,8 +71,8 @@ while (0) // Parameters for the parser. The tokenizer gets passed through into the lexer // as well, so it is declared with "%lex-param" too. -%lex-param {zetasql::parser::DisambiguatorLexer* tokenizer} -%parse-param {zetasql::parser::DisambiguatorLexer* tokenizer} +%lex-param {zetasql::parser::LookaheadTransformer* tokenizer} +%parse-param {zetasql::parser::LookaheadTransformer* tokenizer} %parse-param {zetasql::parser::BisonParser* parser} %parse-param {zetasql::ASTNode** ast_node_result} %parse-param {zetasql::parser::ASTStatementProperties* @@ -96,156 +81,6 @@ while (0) %parse-param {zetasql::ParseLocationPoint* error_location} %parse-param {int* statement_end_byte_offset} -// AMBIGUOUS CASES -// =============== -// -// AMBIGUOUS CASE 1: SAFE_CAST(...) -// -------------------------------- -// The SAFE_CAST keyword is non-reserved and can be used as an identifier. This -// causes one shift/reduce conflict between keyword_as_identifier and the rule -// that starts with "SAFE_CAST" "(". It is resolved in favor of the SAFE_CAST( -// rule, which is the desired behavior. -// -// -// AMBIGUOUS CASE 2: CREATE TABLE FUNCTION -// --------------------------------------- -// ZetaSQL now supports statements of type CREATE TABLE FUNCTION to -// generate new table-valued functions with user-defined names. It also -// supports statements of type CREATE TABLE to generate tables. In the -// latter case, the table name can be any identifier, including FUNCTION, so -// the parser encounters a shift/reduce conflict when the CREATE TABLE FUNCTION -// tokens are pushed onto the stack. By default, the parser chooses to shift, -// favoring creating a new table-valued function. The user may workaround this -// limitation by surrounding the FUNCTION token in backticks. -// This case is responsible for 3 shift/reduce conflicts: -// 1. The separate parser rules for CREATE EXTERNAL TABLE and CREATE EXTERNAL -// TABLE FUNCTION encounter a shift/reduce conflict. -// 2. The separate parser rules for CREATE TABLE AS and CREATE TABLE FUNCTION -// encounter a shift/reduce confict. -// 3. The separate next_statement_kind rules for CREATE TABLE AS and CREATE -// TABLE FUNCTION encounter a shift/reduce confict. -// -// -// AMBIGUOUS CASE 3: CREATE TABLE CONSTRAINTS -// ------------------------------------------ -// The CREATE TABLE rules for the PRIMARY KEY and FOREIGN KEY constraints have -// 2 shift/reduce conflicts, one for each constraint. PRIMARY and FOREIGN can -// be used as keywords for constraint definitions and as identifiers for column -// names. Bison can either shift the PRIMARY or FOREIGN keywords and use them -// for constraint definitions, or it can reduce them as identifiers and use -// them for column definitions. By default Bison shifts them. If the next token -// is KEY, Bison proceeds to reduce table_constraint_definition; otherwise, it -// reduces PRIMARY or FOREIGN as identifier and proceeds to reduce -// table_column_definition. Note that this grammar reports a syntax error when -// using PRIMARY KEY or FOREIGN KEY as column definition name and type pairs. -// -// AMBIGUOUS CASE 4: REPLACE_FIELDS(...) -// -------------------------------- -// The REPLACE_FIELDS keyword is non-reserved and can be used as an identifier. -// This causes a shift/reduce conflict between keyword_as_identifier and the -// rule that starts with "REPLACE_FIELDS" "(". It is resolved in favor of the -// REPLACE_FIELDS( rule, which is the desired behavior. -// -// AMBIGUOUS CASE 5: Procedure parameter list in CREATE PROCEDURE -// ------------------------------------------------------------- -// With rule procedure_parameter being: -// [] -// Optional can be non-reserved word OUT or INOUT, which can also be -// used as . This causes 4 shift/reduce conflicts: -// ( OUT -// ( INOUT -// , OUT -// , INOUT -// By default, Bison chooses to "shift" and always treat OUT/INOUT as . -// In order to use OUT/INOUT as identifier, it needs to be escaped with -// backticks. -// -// AMBIGUOUS CASE 6: CREATE TABLE GENERATED -// ------------------------------------------------------------- -// The GENERATED keyword is non-reserved, so when a generated column is defined -// with " [] GENERATED AS ()", we have a shift/reduce conflict, not -// knowing whether the word GENERATED is an identifier from or the -// keyword GENERATED because is missing. By default, Bison chooses -// "shift", treating GENERATED as a keyword. To use it as an identifier, it -// needs to be escaped with backticks. -// -// AMBIGUOUS CASE 7: DESCRIPTOR(...) -// -------------------------------- -// The DESCRIPTOR keyword is non-reserved and can be used as an identifier. This -// causes one shift/reduce conflict between keyword_as_identifier and the rule -// that starts with "DESCRIPTOR" "(". It is resolved in favor of DESCRIPTOR( -// rule, which is the desired behavior. -// -// AMBIGUOUS CASE 8: ANALYZE OPTIONS(...) -// -------------------------------- -// The OPTIONS keyword is non-reserved and can be used as an identifier. -// This causes a shift/reduce conflict between keyword_as_identifier and the -// rule that starts with "ANALYZE" "OPTIONS" "(". It is resolved in favor of -// the OPTIONS( rule, which is the desired behavior. -// -// AMBIGUOUS CASE 9: SELECT * FROM T QUALIFY -// -------------------------------- -// The QUALIFY keyword is non-reserved and can be used as an identifier. -// This causes a shift/reduce conflict between keyword_as_identifier and the -// rule that starts with "QUALIFY". It is resolved in favor of the QUALIFY rule, -// which is the desired behavior. Currently this is only used to report -// error messages to user when QUALIFY clause is used without -// WHERE/GROUP BY/HAVING. -// -// AMBIGUOUS CASE 10: ALTER COLUMN -// -------------------------------- -// Spanner DDL compatibility extensions provide support for Spanner flavor of -// ALTER COLUMN action, which expects full column definition instead of -// sub-action. Column type identifier in this definition causes 2 shift/reduce -// conflicts with -// ALTER COLUMN... DROP DEFAULT -// ALTER COLUMN... DROP NOT NULL actions -// In both cases when encountering DROP, bison might either choose to shift -// (e.g. interpret DROP as keyword and proceed with one of the 2 rules above), -// or reduce DROP as type identifier in Spanner-specific rule. Bison chooses to -// shift, which is a desired behavior. -// -// AMBIGUOUS CASE 11: SEQUENCE CLAMPED -// ---------------------------------- -// MyFunction(SEQUENCE clamped) -// Resolve to a function call passing a SEQUENCE input argument type. -// -// MyFunction(sequence clamped between x and y) -// Resolve to a function call passing a column 'sequence' modified -// with "clamped between x and y". -// -// Bison favors reducing the 2nd form to an error, so we add a lexer rule to -// force SEQUENCE followed by clamped to resolve to an identifier. -// So bison still thinks there is a conflict but the lexer -// will _never_ produce: -// ... KW_SEQUENCE KW_CLAMPED ... -// it instead produces -// ... IDENTIFIER KW_CLAMPED -// Which will resolve toward the second form -// (sequence clamped between x and y) correctly, and the first form ( -// sequence clamped) will result in an error. -// -// In other contexts, CLAMPED will also act as an identifier via the -// keyword_as_identifier rule. -// -// If the user wants to reference a sequence called 'clamped', they must -// identifier quote it (SEQUENCE `clamped`); -// -// Total expected shift/reduce conflicts as described above: -// 1: SAFE CAST -// 3: CREATE TABLE FUNCTION -// 2: CREATE TABLE CONSTRAINTS -// 1: REPLACE FIELDS -// 4: CREATE PROCEDURE -// 1: CREATE TABLE GENERATED -// 1: CREATE EXTERNAL TABLE FUNCTION -// 1: DESCRIPTOR -// 1: ANALYZE -// 5: QUALIFY -// 2: ALTER COLUMN -// 1: SUM(SEQUENCE CLAMPED BETWEEN x and y) -%expect 23 - %union { bool boolean; int64_t int64_val; @@ -303,6 +138,7 @@ while (0) zetasql::ASTQuery* query; zetasql::ASTExpression* expression; zetasql::ASTExpressionSubquery* expression_subquery; + zetasql::ASTPathExpression* path_expression; zetasql::ASTFunctionCall* function_call; zetasql::ASTAlias* alias; zetasql::ASTIdentifier* identifier; @@ -312,17 +148,25 @@ while (0) zetasql::parser_internal::SeparatedIdentifierTmpNode* slashed_identifier; zetasql::ASTPivotClause* pivot_clause; zetasql::ASTUnpivotClause* unpivot_clause; + zetasql::ASTMatchRecognizeClause* match_recognize_clause; + zetasql::ASTRowPatternExpression* row_pattern_expression; zetasql::ASTSetOperationType* set_operation_type; zetasql::ASTSetOperationAllOrDistinct* set_operation_all_or_distinct; zetasql::ASTBytesLiteral* bytes_literal; zetasql::ASTBytesLiteralComponent* bytes_literal_component; zetasql::ASTStringLiteral* string_literal; zetasql::ASTStringLiteralComponent* string_literal_component; + zetasql::ASTPipeOperator* pipe_operator; + zetasql::ASTSampleClause* sample_clause; struct { zetasql::ASTPivotClause* pivot_clause; zetasql::ASTUnpivotClause* unpivot_clause; zetasql::ASTAlias* alias; } pivot_or_unpivot_clause_and_alias; + struct { + zetasql::ASTMatchRecognizeClause* match_recognize_clause; + zetasql::ASTSampleClause* sample_clause; + } match_recognize_and_sample_clauses; struct { zetasql::ASTNode* where; zetasql::ASTNode* group_by; @@ -370,11 +214,18 @@ while (0) } query_or_replica_source_info; struct { zetasql::ASTNode* hint; + bool and_order_by; } group_by_preamble; zetasql::ASTStructBracedConstructor* struct_braced_constructor; zetasql::ASTBracedConstructor* braced_constructor; zetasql::ASTBracedConstructorField* braced_constructor_field; zetasql::ASTBracedConstructorFieldValue* braced_constructor_field_value; + + struct { + zetasql::ASTNode* partition_by; + zetasql::ASTNode* options_list; + zetasql::ASTNode* spanner_index_innerleaving_clause; + } create_index_statement_suffix; } // YYEOF is a special token used to indicate the end of the input. It's alias // defaults to "end of file", but "end of input" is more appropriate for us. @@ -449,6 +300,7 @@ while (0) %token KW_SHIFT_RIGHT ">>" %token KW_NAMED_ARGUMENT_ASSIGNMENT "=>" %token KW_LAMBDA_ARROW "->" +%token KW_PIPE "|>" // These are not used in the grammar. They are here for parity with the JavaCC // tokenizer. @@ -544,6 +396,7 @@ using namespace zetasql::parser_internal; %token KW_EXTRACT "EXTRACT" %token KW_FALSE "FALSE" %token KW_FOLLOWING "FOLLOWING" +%token KW_FOR "FOR" %token KW_FROM "FROM" %token KW_FULL "FULL" %token KW_GROUP "GROUP" @@ -563,6 +416,7 @@ using namespace zetasql::parser_internal; %token KW_LIKE "LIKE" %token KW_LIMIT "LIMIT" %token KW_LOOKUP "LOOKUP" +%token KW_MATCH_RECOGNIZE_RESERVED %token KW_MERGE "MERGE" %token KW_NATURAL "NATURAL" %token KW_NEW "NEW" @@ -607,7 +461,6 @@ using namespace zetasql::parser_internal; %token KW_ESCAPE "ESCAPE" %token KW_EXCLUDE "EXCLUDE" %token KW_FETCH "FETCH" -%token KW_FOR "FOR" %token KW_GROUPS "GROUPS" %token KW_LATERAL "LATERAL" %token KW_OF "OF" @@ -638,12 +491,12 @@ using namespace zetasql::parser_internal; %token LB_END_OF_STATEMENT_LEVEL_HINT // Represents a "." token used in a path expression, as opposed to the "." in // floating point literals. This token is only used as a lookback by the -// disambiguator. +// lookahead_transformer. %token LB_DOT_IN_PATH_EXPRESSION // Used to lookback to know whether the previous token was the paren before a // nested DML statement. %token LB_OPEN_NESTED_DML -// Two tokens that help the disambiguator locate type templates. +// Two tokens that help the lookahead_transformer locate type templates. %token LB_OPEN_TYPE_TEMPLATE %token LB_CLOSE_TYPE_TEMPLATE // Used as a lookback override in SELECT WITH OPTIONS. See below @@ -707,27 +560,28 @@ using namespace zetasql::parser_internal; // A special token to indicate that an integer or floating point literal is // immediately followed by an identifier without space, for example 123abc. // -// This token is only used by the disambiguator under the `kTokenizer` and -// `kTokenizerPreserveComments` mode to prevent the callers of GetParseTokens() -// to blindly inserting whitespaces between "123" and "abc". For example, -// when formatting "SELECT 123abc", which is invalid, the formatted SQL should -// not become "SELECT 123 abc", which is valid. +// This token is only used by the lookahead_transformer under the `kTokenizer` +// and `kTokenizerPreserveComments` mode to prevent the callers of +// GetParseTokens() to blindly inserting whitespaces between "123" and "abc". +// For example, when formatting "SELECT 123abc", which is invalid, the formatted +// SQL should not become "SELECT 123 abc", which is valid. %token INVALID_LITERAL_PRECEDING_IDENTIFIER_NO_SPACE // The following two tokens will be converted into INTEGER_LITERAL by the -// disambiguator, and the parser should not use them directly. +// lookahead_transformer, and the parser should not use them directly. %token DECIMAL_INTEGER_LITERAL %token HEX_INTEGER_LITERAL // Represents an exponent part without a sign used in a float literal, for // example the "e10" in "1.23e10". It will gets fused into a floating point -// literal or becomes an identifier by the disambiguator, and the parser should -// not use it directly. +// literal or becomes an identifier by the lookahead_transformer, and the parser +// should not use it directly. %token EXP_IN_FLOAT_NO_SIGN // Represents the exponent part "E" used in a float literal, for example the "e" // in "1.23e+10". It will gets fused into a floating point literal or becomes an -// identifier by the disambiguator, and the parser should not use it directly. +// identifier by the lookahead_transformer, and the parser should not use it +// directly. %token STANDALONE_EXPONENT_SIGN "e" // Non-reserved keywords. These can also be used as identifiers. @@ -830,10 +684,12 @@ using namespace zetasql::parser_internal; %token KW_MACRO "MACRO" %token KW_MAP "MAP" %token KW_MATCH "MATCH" +%token KW_MATCH_RECOGNIZE_NONRESERVED %token KW_MATCHED "MATCHED" %token KW_MATERIALIZED "MATERIALIZED" %token KW_MAX "MAX" %token KW_MAXVALUE "MAXVALUE" +%token KW_MEASURES "MEASURES" %token KW_MESSAGE "MESSAGE" %token KW_METADATA "METADATA" %token KW_MIN "MIN" @@ -848,6 +704,7 @@ using namespace zetasql::parser_internal; %token KW_OUTPUT "OUTPUT" %token KW_OVERWRITE "OVERWRITE" %token KW_PARTITIONS "PARTITIONS" +%token KW_PATTERN "PATTERN" %token KW_PERCENT "PERCENT" %token KW_PIVOT "PIVOT" %token KW_POLICIES "POLICIES" @@ -893,6 +750,7 @@ using namespace zetasql::parser_internal; %token KW_SQL "SQL" %token KW_STABLE "STABLE" %token KW_START "START" +%token KW_STATIC_DESCRIBE "STATIC_DESCRIBE" %token KW_STORED "STORED" %token KW_STORING "STORING" %token KW_SYSTEM "SYSTEM" @@ -993,6 +851,7 @@ using namespace zetasql::parser_internal; %type commit_statement %type connection_clause %type create_constant_statement +%type create_connection_statement %type create_database_statement %type create_function_statement %type create_procedure_statement @@ -1076,9 +935,12 @@ using namespace zetasql::parser_internal; %type grant_statement %type grantee_list %type grantee_list_with_parens_prefix +%type opt_and_order %type group_by_clause_prefix %type group_by_all %type group_by_preamble +%type opt_selection_item_order +%type opt_grouping_item_order %type grouping_item %type grouping_set %type grouping_set_list @@ -1124,9 +986,9 @@ using namespace zetasql::parser_internal; %type path_expression_list_with_parens %type opt_path_expression_list_with_parens %type set_statement -%type index_order_by +%type index_order_by_and_options %type index_all_columns -%type index_order_by_prefix +%type index_order_by_and_options_prefix %type index_storing_list %type index_unnest_expression_list %type in_list_two_or_more_prefix @@ -1175,6 +1037,7 @@ using namespace zetasql::parser_internal; %type next_statement %type next_script_statement %type null_literal +%type null_order %type opt_null_order %type numeric_literal %type on_clause @@ -1224,6 +1087,7 @@ using namespace zetasql::parser_internal; %type opt_function_returns %type group_by_clause %type opt_group_by_clause +%type opt_group_by_clause_with_opt_comma %type generic_entity_body %type opt_generic_entity_body %type having_clause @@ -1277,7 +1141,15 @@ using namespace zetasql::parser_internal; %type unpivot_clause %type pivot_expression %type pivot_expression_list -%type opt_sample_clause +%type match_recognize_clause +// Use a combined struct to make sure they always are applied together, to avoid +// forgetting one of them. +%type opt_match_recognize_and_sample_clauses +%type opt_match_recognize_clause +%type row_pattern_expr +%type row_pattern_concatenation +%type row_pattern_factor +%type opt_sample_clause %type opt_sample_clause_suffix %type opt_select_as_clause %type opt_table_element_list @@ -1299,7 +1171,11 @@ using namespace zetasql::parser_internal; %type options_list_prefix %type order_by_clause_prefix %type order_by_clause +%type order_by_clause_with_opt_comma %type ordering_expression +%type column_ordering_and_options_expr +%type all_column_column_options +%type opt_with_column_options %type named_parameter_expression %type parameter_expression %type system_variable_expression @@ -1307,7 +1183,7 @@ using namespace zetasql::parser_internal; %type parenthesized_anysomeall_list_in_rhs %type partition_by_clause_prefix %type partition_by_clause_prefix_no_hint -%type path_expression +%type path_expression %type dashed_identifier %type slashed_identifier %type slashed_path_expression @@ -1352,17 +1228,19 @@ using namespace zetasql::parser_internal; %type row_access_policy_alter_action %type row_access_policy_alter_action_list %type run_batch_statement -%type sample_clause +%type sample_clause %type sample_size %type sample_size_value %type select %type select_clause %type select_column %type select_column_expr +%type select_column_expr_with_as_alias %type select_column_dot_star %type select_column_star %type select_list %type select_list_prefix +%type select_list_prefix_with_as_aliases %type with_expression_variable %type with_expression_variable_prefix %type with_expression @@ -1524,6 +1402,46 @@ using namespace zetasql::parser_internal; %type opt_minvalue %type opt_cycle +%type bad_keyword_after_from_query +%type bad_keyword_after_from_query_allows_parens +%type query_without_pipe_operators +%type pipe_operator +%type pipe_where_clause +%type pipe_select_clause +%type pipe_limit_offset_clause +%type pipe_order_by_clause +%type pipe_extend_clause +%type pipe_selection_item +%type pipe_selection_item_list +%type pipe_selection_item_list_no_comma +%type pipe_selection_item_with_order +%type pipe_selection_item_list_no_comma_with_order +%type pipe_selection_item_list_with_order +%type pipe_selection_item_list_with_order_or_empty +%type pipe_rename_item +%type pipe_rename_item_list +%type pipe_rename +%type pipe_aggregate_clause +%type pipe_group_by +%type pipe_set_operation_clause +%type pipe_join +%type pipe_call +%type pipe_window_clause +%type pipe_distinct +%type pipe_tablesample +%type pipe_as +%type pipe_static_describe +%type pipe_assert_base +%type pipe_assert +%type identifier_in_pipe_drop +%type identifier_list_in_pipe_drop +%type pipe_drop +%type pipe_set_item +%type pipe_set_item_list +%type pipe_set +%type pipe_pivot +%type pipe_unpivot + %type additive_operator %type comparative_operator %type options_assignment_operator @@ -1566,7 +1484,7 @@ using namespace zetasql::parser_internal; // Spanner-specific non-terminals %type opt_spanner_null_filtered -%type opt_spanner_index_interleave_clause +%type spanner_index_interleave_clause %type opt_spanner_interleave_in_parent_clause %type opt_spanner_generated_or_default %type opt_spanner_not_null_attribute @@ -1575,105 +1493,257 @@ using namespace zetasql::parser_internal; %type spanner_generated_or_default %type spanner_set_on_delete_action %type spanner_primary_key -// End of Spanner-specific non-terminals - -%start start_mode -%% - -start_mode: - MODE_STATEMENT sql_statement { *ast_node_result = $2; } - | MODE_SCRIPT script { *ast_node_result = $2; } - | MODE_NEXT_STATEMENT next_statement { *ast_node_result = $2; } - | MODE_NEXT_SCRIPT_STATEMENT next_script_statement { *ast_node_result = $2; } - | MODE_NEXT_STATEMENT_KIND next_statement_kind - { ast_statement_properties->node_kind = $2; } - | MODE_EXPRESSION expression { *ast_node_result = $2; } - | MODE_TYPE type { *ast_node_result = $2; } - ; - - -opt_semicolon: ";" | %empty ; - -sql_statement: - unterminated_sql_statement opt_semicolon - { - $$ = $1; - } - ; - -next_script_statement: - unterminated_statement ";" - { - // The semicolon marks the end of the statement. - SetForceTerminate(tokenizer, statement_end_byte_offset); - $$ = $1; - } - | unterminated_statement - { - // There's no semicolon. That means we have to be at EOF. - *statement_end_byte_offset = -1; - $$ = $1; - } - ; -next_statement: - unterminated_sql_statement ";" - { - // The semicolon marks the end of the statement. - SetForceTerminate(tokenizer, statement_end_byte_offset); - $$ = $1; - } - | unterminated_sql_statement - { - // There's no semicolon. That means we have to be at EOF. - *statement_end_byte_offset = -1; - $$ = $1; - } - ; +%type opt_create_index_statement_suffix +// End of Spanner-specific non-terminals -// This rule exists to run an action before parsing a statement irrespective -// of whether or not the statement is a sql statement or a script statement. -// This shape is recommended in the Bison manual. -// See https://www.gnu.org/software/bison/manual/bison.html#Midrule-Conflicts +// AMBIGUOUS CASES +// =============== // -// We override the lookback of BEGIN here, and not locally in begin_end_block -// to avoid shift/reduce conflicts with the BEGIN TRANSACTION statement. The -// alternative lookback in this case meerly asserts that BEGIN is a keyword -// at the beginning of a statement, which is true both for being end blocks -// and for BEGIN TRANSACTION. -pre_statement: - %empty - { OVERRIDE_NEXT_TOKEN_LOOKBACK(KW_BEGIN, LB_BEGIN_AT_STATEMENT_START); } - -unterminated_statement: - pre_statement unterminated_sql_statement[stmt] - { - @$ = @stmt; - $$ = $stmt; - } - | pre_statement unterminated_script_statement[stmt] - { - @$ = @stmt; - $$ = $stmt; - } - ; - -statement_level_hint: - hint - { - OVERRIDE_CURRENT_TOKEN_LOOKBACK(@hint, LB_END_OF_STATEMENT_LEVEL_HINT); - $$ = $hint; - } - ; - -unterminated_sql_statement: - sql_statement_body - | statement_level_hint[hint] sql_statement_body - { - $$ = MAKE_NODE(ASTHintedStatement, @$, {$hint, $sql_statement_body}); - } - | "DEFINE" "MACRO"[kw_macro] - { +// AMBIGUOUS CASE 1: SAFE_CAST(...) +// -------------------------------- +// The SAFE_CAST keyword is non-reserved and can be used as an identifier. This +// causes one shift/reduce conflict between keyword_as_identifier and the rule +// that starts with "SAFE_CAST" "(". It is resolved in favor of the SAFE_CAST( +// rule, which is the desired behavior. +// +// +// AMBIGUOUS CASE 2: CREATE TABLE FUNCTION +// --------------------------------------- +// ZetaSQL now supports statements of type CREATE TABLE FUNCTION to +// generate new table-valued functions with user-defined names. It also +// supports statements of type CREATE TABLE to generate tables. In the +// latter case, the table name can be any identifier, including FUNCTION, so +// the parser encounters a shift/reduce conflict when the CREATE TABLE FUNCTION +// tokens are pushed onto the stack. By default, the parser chooses to shift, +// favoring creating a new table-valued function. The user may workaround this +// limitation by surrounding the FUNCTION token in backticks. +// This case is responsible for 3 shift/reduce conflicts: +// 1. The separate parser rules for CREATE EXTERNAL TABLE and CREATE EXTERNAL +// TABLE FUNCTION encounter a shift/reduce conflict. +// 2. The separate parser rules for CREATE TABLE AS and CREATE TABLE FUNCTION +// encounter a shift/reduce confict. +// 3. The separate next_statement_kind rules for CREATE TABLE AS and CREATE +// TABLE FUNCTION encounter a shift/reduce confict. +// +// +// AMBIGUOUS CASE 3: CREATE TABLE CONSTRAINTS +// ------------------------------------------ +// The CREATE TABLE rules for the PRIMARY KEY and FOREIGN KEY constraints have +// 2 shift/reduce conflicts, one for each constraint. PRIMARY and FOREIGN can +// be used as keywords for constraint definitions and as identifiers for column +// names. Bison can either shift the PRIMARY or FOREIGN keywords and use them +// for constraint definitions, or it can reduce them as identifiers and use +// them for column definitions. By default Bison shifts them. If the next token +// is KEY, Bison proceeds to reduce table_constraint_definition; otherwise, it +// reduces PRIMARY or FOREIGN as identifier and proceeds to reduce +// table_column_definition. Note that this grammar reports a syntax error when +// using PRIMARY KEY or FOREIGN KEY as column definition name and type pairs. +// +// AMBIGUOUS CASE 4: REPLACE_FIELDS(...) +// -------------------------------- +// The REPLACE_FIELDS keyword is non-reserved and can be used as an identifier. +// This causes a shift/reduce conflict between keyword_as_identifier and the +// rule that starts with "REPLACE_FIELDS" "(". It is resolved in favor of the +// REPLACE_FIELDS( rule, which is the desired behavior. +// +// AMBIGUOUS CASE 5: Procedure parameter list in CREATE PROCEDURE +// ------------------------------------------------------------- +// With rule procedure_parameter being: +// [] +// Optional can be non-reserved word OUT or INOUT, which can also be +// used as . This causes 4 shift/reduce conflicts: +// ( OUT +// ( INOUT +// , OUT +// , INOUT +// By default, Bison chooses to "shift" and always treat OUT/INOUT as . +// In order to use OUT/INOUT as identifier, it needs to be escaped with +// backticks. +// +// AMBIGUOUS CASE 6: CREATE TABLE GENERATED +// ------------------------------------------------------------- +// The GENERATED keyword is non-reserved, so when a generated column is defined +// with " [] GENERATED AS ()", we have a shift/reduce conflict, not +// knowing whether the word GENERATED is an identifier from or the +// keyword GENERATED because is missing. By default, Bison chooses +// "shift", treating GENERATED as a keyword. To use it as an identifier, it +// needs to be escaped with backticks. +// +// AMBIGUOUS CASE 7: DESCRIPTOR(...) +// -------------------------------- +// The DESCRIPTOR keyword is non-reserved and can be used as an identifier. This +// causes one shift/reduce conflict between keyword_as_identifier and the rule +// that starts with "DESCRIPTOR" "(". It is resolved in favor of DESCRIPTOR( +// rule, which is the desired behavior. +// +// AMBIGUOUS CASE 8: ANALYZE OPTIONS(...) +// -------------------------------- +// The OPTIONS keyword is non-reserved and can be used as an identifier. +// This causes a shift/reduce conflict between keyword_as_identifier and the +// rule that starts with "ANALYZE" "OPTIONS" "(". It is resolved in favor of +// the OPTIONS( rule, which is the desired behavior. +// +// AMBIGUOUS CASE 9: SELECT * FROM T QUALIFY +// -------------------------------- +// The QUALIFY keyword is non-reserved and can be used as an identifier. +// This causes a shift/reduce conflict between keyword_as_identifier and the +// rule that starts with "QUALIFY". It is resolved in favor of the QUALIFY rule, +// which is the desired behavior. Currently this is only used to report +// error messages to user when QUALIFY clause is used without +// WHERE/GROUP BY/HAVING. +// +// AMBIGUOUS CASE 10: ALTER COLUMN +// -------------------------------- +// Spanner DDL compatibility extensions provide support for Spanner flavor of +// ALTER COLUMN action, which expects full column definition instead of +// sub-action. Column type identifier in this definition causes 2 shift/reduce +// conflicts with +// ALTER COLUMN... DROP DEFAULT +// ALTER COLUMN... DROP NOT NULL actions +// In both cases when encountering DROP, bison might either choose to shift +// (e.g. interpret DROP as keyword and proceed with one of the 2 rules above), +// or reduce DROP as type identifier in Spanner-specific rule. Bison chooses to +// shift, which is a desired behavior. +// +// AMBIGUOUS CASE 11: SEQUENCE CLAMPED +// ---------------------------------- +// MyFunction(SEQUENCE clamped) +// Resolve to a function call passing a SEQUENCE input argument type. +// +// MyFunction(sequence clamped between x and y) +// Resolve to a function call passing a column 'sequence' modified +// with "clamped between x and y". +// +// Bison favors reducing the 2nd form to an error, so we add a lexer rule to +// force SEQUENCE followed by clamped to resolve to an identifier. +// So bison still thinks there is a conflict but the lexer +// will _never_ produce: +// ... KW_SEQUENCE KW_CLAMPED ... +// it instead produces +// ... IDENTIFIER KW_CLAMPED +// Which will resolve toward the second form +// (sequence clamped between x and y) correctly, and the first form ( +// sequence clamped) will result in an error. +// +// In other contexts, CLAMPED will also act as an identifier via the +// keyword_as_identifier rule. +// +// If the user wants to reference a sequence called 'clamped', they must +// identifier quote it (SEQUENCE `clamped`); +// +// Total expected shift/reduce conflicts as described above: +// 1: SAFE CAST +// 3: CREATE TABLE FUNCTION +// 2: CREATE TABLE CONSTRAINTS +// 1: REPLACE FIELDS +// 4: CREATE PROCEDURE +// 1: CREATE TABLE GENERATED +// 1: CREATE EXTERNAL TABLE FUNCTION +// 1: DESCRIPTOR +// 1: ANALYZE +// 5: QUALIFY +// 2: ALTER COLUMN +// 1: SUM(SEQUENCE CLAMPED BETWEEN x and y) +%expect 23 + +%start start_mode +%% + +start_mode: + MODE_STATEMENT sql_statement { *ast_node_result = $2; } + | MODE_SCRIPT script { *ast_node_result = $2; } + | MODE_NEXT_STATEMENT next_statement { *ast_node_result = $2; } + | MODE_NEXT_SCRIPT_STATEMENT next_script_statement { *ast_node_result = $2; } + | MODE_NEXT_STATEMENT_KIND next_statement_kind + { ast_statement_properties->node_kind = $2; } + | MODE_EXPRESSION expression { *ast_node_result = $2; } + | MODE_TYPE type { *ast_node_result = $2; } + ; + + +opt_semicolon: ";" | %empty ; + +sql_statement: + unterminated_sql_statement opt_semicolon + { + $$ = $1; + } + ; + +next_script_statement: + unterminated_statement ";" + { + // The semicolon marks the end of the statement. + SetForceTerminate(tokenizer, statement_end_byte_offset); + $$ = $1; + } + | unterminated_statement + { + // There's no semicolon. That means we have to be at EOF. + *statement_end_byte_offset = -1; + $$ = $1; + } + ; + +next_statement: + unterminated_sql_statement ";" + { + // The semicolon marks the end of the statement. + SetForceTerminate(tokenizer, statement_end_byte_offset); + $$ = $1; + } + | unterminated_sql_statement + { + // There's no semicolon. That means we have to be at EOF. + *statement_end_byte_offset = -1; + $$ = $1; + } + ; + +// This rule exists to run an action before parsing a statement irrespective +// of whether or not the statement is a sql statement or a script statement. +// This shape is recommended in the Bison manual. +// See https://www.gnu.org/software/bison/manual/bison.html#Midrule-Conflicts +// +// We override the lookback of BEGIN here, and not locally in begin_end_block +// to avoid shift/reduce conflicts with the BEGIN TRANSACTION statement. The +// alternative lookback in this case meerly asserts that BEGIN is a keyword +// at the beginning of a statement, which is true both for being end blocks +// and for BEGIN TRANSACTION. +pre_statement: + %empty + { OVERRIDE_NEXT_TOKEN_LOOKBACK(KW_BEGIN, LB_BEGIN_AT_STATEMENT_START); } + +unterminated_statement: + pre_statement unterminated_sql_statement[stmt] + { + @$ = @stmt; + $$ = $stmt; + } + | pre_statement unterminated_script_statement[stmt] + { + @$ = @stmt; + $$ = $stmt; + } + ; + +statement_level_hint: + hint + { + OVERRIDE_CURRENT_TOKEN_LOOKBACK(@hint, LB_END_OF_STATEMENT_LEVEL_HINT); + $$ = $hint; + } + ; + +unterminated_sql_statement: + sql_statement_body + | statement_level_hint[hint] sql_statement_body + { + $$ = MAKE_NODE(ASTHintedStatement, @$, {$hint, $sql_statement_body}); + } + | "DEFINE" "MACRO"[kw_macro] + { if (!parser->language_options().LanguageFeatureEnabled( zetasql::FEATURE_V_1_4_SQL_MACROS)) { YYERROR_AND_ABORT_AT(@kw_macro, "Macros are not supported"); @@ -1763,6 +1833,7 @@ sql_statement_body: | run_batch_statement | abort_batch_statement | create_constant_statement + | create_connection_statement | create_database_statement | create_function_statement | create_procedure_statement @@ -1804,7 +1875,7 @@ define_macro_statement: // Use a special version of KW_DEFINE which indicates that this macro // definition was "original" (i.e., not expanded from other macros), and // is top-level (i.e., not nested under other statements or blocks like IF). - "DEFINE for macros" "MACRO" + KW_DEFINE_FOR_MACROS "MACRO" { if (!parser->language_options().LanguageFeatureEnabled( zetasql::FEATURE_V_1_4_SQL_MACROS)) { @@ -2151,6 +2222,8 @@ schema_object_kind: { $$ = zetasql::SchemaObjectKind::kAggregateFunction; } | "APPROX" "VIEW" { $$ = zetasql::SchemaObjectKind::kApproxView; } + | "CONNECTION" + { $$ = zetasql::SchemaObjectKind::kConnection; } | "CONSTANT" { $$ = zetasql::SchemaObjectKind::kConstant; } | "DATABASE" @@ -2202,6 +2275,8 @@ alter_statement: // APPROX VIEW and MODEL are currently supported. if ($2 == zetasql::SchemaObjectKind::kApproxView) { node = MAKE_NODE(ASTAlterApproxViewStatement, @$); + } else if ($2 == zetasql::SchemaObjectKind::kConnection) { + node = MAKE_NODE(ASTAlterConnectionStatement, @$); } else if ($2 == zetasql::SchemaObjectKind::kDatabase) { node = MAKE_NODE(ASTAlterDatabaseStatement, @$); } else if ($2 == zetasql::SchemaObjectKind::kSchema) { @@ -3116,22 +3191,56 @@ create_external_table_function_statement: } ; +// This rule encounters a shift/reduce conflict with 'create_index_statement' +// if "PARTITION BY" and "INTERLEAVING IN" are both present. This is because in +// "PARTITION BY", a "," can be used to separate the partition columns, while +// "INTERLEAVING IN" is leading by a ",". +// To avoid this conflict, in the create index suffix, we do not allow partition +// by and interleaving in to be present at the same time. +opt_create_index_statement_suffix: + partition_by_clause_prefix_no_hint opt_options_list + { + $$ = {$partition_by_clause_prefix_no_hint, $opt_options_list, + /*spanner_index_innerleaving_clause=*/nullptr}; + } + | opt_options_list spanner_index_interleave_clause + { + $$ = {/*partition_by=*/nullptr, $opt_options_list, + $spanner_index_interleave_clause}; + } + | options + { + $$ = {/*partition_by=*/nullptr, $options, + /*spanner_index_innerleaving_clause=*/nullptr}; + } + | %empty + { + $$ = {/*partition_by=*/nullptr, /*opt_options_list=*/nullptr, + /*spanner_index_innerleaving_clause=*/nullptr}; + } + ; + create_index_statement: - "CREATE" opt_or_replace opt_unique opt_spanner_null_filtered opt_index_type - "INDEX" opt_if_not_exists path_expression "ON" path_expression opt_as_alias - opt_index_unnest_expression_list index_order_by opt_index_storing_list - opt_options_list opt_spanner_index_interleave_clause + "CREATE" opt_or_replace opt_unique opt_spanner_null_filtered opt_index_type + "INDEX" opt_if_not_exists path_expression on_path_expression opt_as_alias + opt_index_unnest_expression_list index_order_by_and_options + opt_index_storing_list opt_create_index_statement_suffix { auto* create = MAKE_NODE(ASTCreateIndexStatement, @$, - {$8, $10, $11, $12, $13, $14, $15, $16}); - create->set_is_or_replace($2); - create->set_is_unique($3); - create->set_is_if_not_exists($7); - create->set_spanner_is_null_filtered($4); - if ($5 == IndexTypeKeywords::kSearch) { + {$path_expression, $on_path_expression, $opt_as_alias, + $opt_index_unnest_expression_list, $index_order_by_and_options, + $opt_index_storing_list, + $opt_create_index_statement_suffix.partition_by, + $opt_create_index_statement_suffix.options_list, + $opt_create_index_statement_suffix.spanner_index_innerleaving_clause}); + create->set_is_or_replace($opt_or_replace); + create->set_is_unique($opt_unique); + create->set_is_if_not_exists($opt_if_not_exists); + create->set_spanner_is_null_filtered($opt_spanner_null_filtered); + if ($opt_index_type == IndexTypeKeywords::kSearch) { create->set_is_search(true); - } else if ($5 == IndexTypeKeywords::kVector) { + } else if ($opt_index_type == IndexTypeKeywords::kVector) { create->set_is_vector(true); } $$ = create; @@ -3161,6 +3270,17 @@ create_external_schema_statement: } ; +create_connection_statement: + "CREATE" opt_or_replace "CONNECTION" opt_if_not_exists path_expression + opt_options_list + { + auto* create = MAKE_NODE(ASTCreateConnectionStatement, @$, {$path_expression, $opt_options_list}); + create->set_is_or_replace($2); + create->set_is_if_not_exists($4); + $$ = create; + } + ; + undrop_statement: "UNDROP" schema_object_kind opt_if_not_exists path_expression opt_at_system_time opt_options_list @@ -3824,7 +3944,7 @@ opt_cycle: ; identity_column_info: - KW_IDENTITY "(" opt_start_with[start] opt_increment_by[increment] + "IDENTITY" "(" opt_start_with[start] opt_increment_by[increment] opt_maxvalue[max] opt_minvalue[min] opt_cycle[cycle] ")" { auto* identity_column = @@ -4172,7 +4292,7 @@ opt_foreign_key_match: foreign_key_match_mode: "SIMPLE" { $$ = zetasql::ASTForeignKeyReference::SIMPLE; } - | KW_FULL { $$ = zetasql::ASTForeignKeyReference::FULL; } + | "FULL" { $$ = zetasql::ASTForeignKeyReference::FULL; } | "NOT_SPECIAL" "DISTINCT" { $$ = zetasql::ASTForeignKeyReference::NOT_DISTINCT; } @@ -4346,7 +4466,10 @@ query_or_replica_source: ; as_query: - "AS" query { $$ = $2; } + "AS" query + { + $$ = $2; + } ; opt_as_query: @@ -4478,7 +4601,7 @@ privileges: ; opt_privileges_keyword: - KW_PRIVILEGES + "PRIVILEGES" | %empty ; @@ -4505,7 +4628,7 @@ privilege_name: { $$ = $1; } - | KW_SELECT + | "SELECT" { // The SELECT keyword is allowed to be a privilege name. $$ = parser->MakeIdentifier(@1, parser->GetInputText(@1)); @@ -4543,32 +4666,59 @@ module_statement: } ; -index_order_by_prefix: - "(" ordering_expression + +column_ordering_and_options_expr: + expression opt_collate_clause opt_asc_or_desc opt_null_order opt_options_list + { + auto* ordering_expr = + MAKE_NODE(ASTOrderingExpression, @$, { + $expression, + $opt_collate_clause, + $opt_null_order, + $opt_options_list + }); + ordering_expr->set_ordering_spec($3); + $$ = ordering_expr; + } + ; + +index_order_by_and_options_prefix: + "(" column_ordering_and_options_expr { $$ = MAKE_NODE(ASTIndexItemList, @$, {$2}); } - | index_order_by_prefix "," ordering_expression + | index_order_by_and_options_prefix "," column_ordering_and_options_expr { $$ = WithExtraChildren($1, {$3}); } ; +all_column_column_options: + index_order_by_and_options_prefix ")" + +opt_with_column_options: + "WITH" "COLUMN" "OPTIONS" all_column_column_options + { + $$ = $4; + } + | %empty { $$ = nullptr; } + ; + index_all_columns: - "(" "ALL" "COLUMNS" ")" + "(" "ALL" "COLUMNS" opt_with_column_options ")" { - auto* all_columns = MAKE_NODE(ASTIndexAllColumns, @$); + auto* all_columns = MAKE_NODE(ASTIndexAllColumns, @$, {$4}); all_columns->set_image("ALL COLUMNS"); auto* ordering_expr = MAKE_NODE(ASTOrderingExpression, @$, - {all_columns, nullptr, nullptr}); + {all_columns, nullptr, nullptr, nullptr}); ordering_expr->set_ordering_spec( zetasql::ASTOrderingExpression::UNSPECIFIED); $$ = MAKE_NODE(ASTIndexItemList, @$, {ordering_expr}); } -index_order_by: - index_order_by_prefix ")" +index_order_by_and_options: + index_order_by_and_options_prefix ")" { $$ = parser->WithEndLocation($1, @$); } @@ -4728,135 +4878,628 @@ show_target: } ; -opt_like_string_literal: - "LIKE" string_literal +opt_like_string_literal: + "LIKE" string_literal + { + $$ = $2; + } + | %empty { $$ = nullptr; } + ; + +opt_like_path_expression: + "LIKE" maybe_dashed_path_expression + { + $$ = $2; + } + | %empty { $$ = nullptr; } + ; + +opt_clone_table: + "CLONE" clone_data_source + { + $$ = $2; + } + | %empty { $$ = nullptr; } + ; + +opt_copy_table: + "COPY" copy_data_source + { + $$ = $2; + } + | %empty { $$ = nullptr; } + ; + +all_or_distinct: + "ALL" { + $$ = MAKE_NODE(ASTSetOperationAllOrDistinct, @$, {}); + $$->set_value(zetasql::ASTSetOperation::ALL); + } + | "DISTINCT" { + $$ = MAKE_NODE(ASTSetOperationAllOrDistinct, @$, {}); + $$->set_value(zetasql::ASTSetOperation::DISTINCT); + } + ; + +// Returns the token for a set operation as expected by +// ASTSetOperation::op_type(). +query_set_operation_type: + "UNION" + { + $$ = MAKE_NODE(ASTSetOperationType, @$, {}); + $$->set_value(zetasql::ASTSetOperation::UNION); + } + | KW_EXCEPT_IN_SET_OP + { + $$ = MAKE_NODE(ASTSetOperationType, @$, {}); + $$->set_value(zetasql::ASTSetOperation::EXCEPT); + } + | "INTERSECT" + { + $$ = MAKE_NODE(ASTSetOperationType, @$, {}); + $$->set_value(zetasql::ASTSetOperation::INTERSECT); + } + ; + +query_primary_or_set_operation: + query_primary + | query_set_operation + ; + +parenthesized_query: + "(" query ")" + { + // We do not call $query->set_parenthesized(true) because typically the + // calling rule expects parentheses and will already insert one pair + // when unparsing. + $$ = $query; + } + ; + +select_or_from_keyword: + "SELECT" | "FROM" + ; + +// These rules are for generating errors for unexpected clauses after FROM +// queries. The returned string constant is the name for the error message. +bad_keyword_after_from_query: + "WHERE" { $$ = "WHERE"; } + | "SELECT" { $$ = "SELECT"; } + | "GROUP" { $$ = "GROUP BY"; } + ; + +// These produce a different error that says parentheses are also allowed. +bad_keyword_after_from_query_allows_parens: + "ORDER" { $$ = "ORDER BY"; } + | "UNION" { $$ = "UNION"; } + | "INTERSECT" { $$ = "INTERSECT"; } + | KW_EXCEPT_IN_SET_OP { $$ = "EXCEPT"; } + | "LIMIT" { $$ = "LIMIT"; } + ; + +query_without_pipe_operators: + // We don't use an opt_with_clause for the first element because it causes + // shift/reduce conflicts. + with_clause query_primary_or_set_operation[query_primary] + opt_order_by_clause[order_by] + opt_limit_offset_clause[offset] + { + auto* node = MAKE_NODE(ASTQuery, @$, + {$with_clause, $query_primary, $order_by, $offset}); + $$ = node; + } + | with_clause_with_trailing_comma select_or_from_keyword + { + // TODO: Consider pointing the error location at the comma + // instead of at the SELECT. + YYERROR_AND_ABORT_AT(@2, + "Syntax error: Trailing comma after the WITH " + "clause before the main query is not allowed"); + } + | with_clause "|>" + { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT(@2, "Syntax error: Unexpected |"); + } else { + YYERROR_AND_ABORT_AT(@2, + "Syntax error: A pipe operator cannot follow " + "the WITH clause before the main query; The " + "main query usually starts with SELECT or " + "FROM here"); + } + } + | query_primary_or_set_operation[query_primary] + opt_order_by_clause[order_by] + opt_limit_offset_clause[offset] + { + zetasql::ASTQuery* query = $query_primary->GetAsOrNull< + zetasql::ASTQuery>(); + if (query && !query->parenthesized()) { + auto* node = WithExtraChildren(query, + {$order_by, $offset}); + $$ = node; + } else if (query && !$order_by && !$offset + ) { + // This means it is a query originally and there are no other clauses. + // So then wrapping it is semantically useless. + $$ = query; + } else { + auto* node = MAKE_NODE(ASTQuery, @$, + {$query_primary, $order_by, $offset}); + $$ = node; + } + } + // Support FROM queries, which just have a standalone FROM clause and + // no other clauses (other than pipe operators). + // FROM queries also cannot be followed with a LIMIT, ORDER BY, or set + // operations (which would be allowed if this was attached in + // query_primary rather than here). + | opt_with_clause from_clause + { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT(@2, "Syntax error: Unexpected FROM"); + } + zetasql::ASTFromQuery* from_query = MAKE_NODE(ASTFromQuery, @2, {$2}); + $$ = MAKE_NODE(ASTQuery, @$, {$1, from_query}); + } + | opt_with_clause from_clause bad_keyword_after_from_query + { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT(@2, "Syntax error: Unexpected FROM"); + } + const absl::string_view keyword = $3; + YYERROR_AND_ABORT_AT(@3, absl::StrCat( + "Syntax error: ", keyword, " not supported after FROM query; " + "Consider using pipe operator `|> ", + keyword == "GROUP BY" ? "AGGREGATE" : keyword, "`")); + } + | opt_with_clause from_clause bad_keyword_after_from_query_allows_parens + { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT(@2, "Syntax error: Unexpected FROM"); + } + const absl::string_view keyword = $3; + YYERROR_AND_ABORT_AT(@3, absl::StrCat( + "Syntax error: ", keyword, " not supported after FROM query; " + "Consider using pipe operator `|> ", keyword, + "` or parentheses around the FROM query")); + } + ; + +query: + query_without_pipe_operators + | query "|>" pipe_operator + { + // Adjust the location on the operator node to include the pipe symbol. + zetasql::ASTNode* pipe_op = + parser->WithStartLocation($pipe_operator, @2); + + zetasql::ASTQuery* query = $1; + if (query->parenthesized()) { + // When we have a pipe operator following a parenthesized query, rather + // than just adding it on, we created a nested query expression, so + // we get a better representation of how the pipes bind. + // We set is_nested for the Unparser, and unset parenthesized to avoid + // printing double-parentheses. + query->set_is_nested(true); + query->set_parenthesized(false); + $$ = MAKE_NODE(ASTQuery, @$, {query, pipe_op}); + } else { + $$ = WithExtraChildren(query, {pipe_op}); + } + } + ; + +pipe_operator: + pipe_where_clause + | pipe_select_clause + | pipe_extend_clause + | pipe_rename + | pipe_aggregate_clause + | pipe_group_by + | pipe_limit_offset_clause + | pipe_set_operation_clause + | pipe_order_by_clause + | pipe_join + | pipe_call + | pipe_window_clause + | pipe_distinct + | pipe_tablesample + | pipe_as + | pipe_static_describe + | pipe_assert + | pipe_drop + | pipe_set + | pipe_pivot + | pipe_unpivot + ; + +pipe_where_clause: + where_clause + { + $$ = MAKE_NODE(ASTPipeWhere, @$, {$1}); + } + ; + +pipe_select_clause: + select_clause + { + $$ = MAKE_NODE(ASTPipeSelect, @$, {$1}); + } + ; + +pipe_limit_offset_clause: + limit_offset_clause + { + $$ = MAKE_NODE(ASTPipeLimitOffset, @$, {$1}); + } + ; + +pipe_order_by_clause: + order_by_clause_with_opt_comma + { + $$ = MAKE_NODE(ASTPipeOrderBy, @$, {$1}); + } + ; + +pipe_extend_clause: + "EXTEND" pipe_selection_item_list + { + // Pipe EXTEND is represented as an ASTSelect inside an + // ASTPipeExtend. This allows more resolver code sharing. + zetasql::ASTSelect* select = + MAKE_NODE(ASTSelect, @$, {$pipe_selection_item_list}); + $$ = MAKE_NODE(ASTPipeExtend, @$, {select}); + } + ; + +pipe_selection_item: + select_column_expr + | select_column_dot_star + ; + +// This adds optional selection_item_order suffixes on the expression cases. +// Dot-star cases are also supported, without order suffixes. +pipe_selection_item_with_order: + select_column_expr opt_selection_item_order + { + $$ = WithExtraChildren($select_column_expr, {$opt_selection_item_order}); + } + | select_column_dot_star + ; + +// This is a restricted form of ASTSelectList that excludes * and +// other SELECT-list specific syntaxes. +pipe_selection_item_list_no_comma: + pipe_selection_item + { + $$ = MAKE_NODE(ASTSelectList, @$, {$1}); + } + | pipe_selection_item_list_no_comma "," pipe_selection_item + { + $$ = WithExtraChildren($1, {$3}); + } + ; + +pipe_selection_item_list_no_comma_with_order: + pipe_selection_item_with_order + { + $$ = MAKE_NODE(ASTSelectList, @$, {$1}); + } + | pipe_selection_item_list_no_comma_with_order "," + pipe_selection_item_with_order + { + $$ = WithExtraChildren($1, {$3}); + } + ; + +// This is the selection list used for most pipe operators. +// It resolves to an ASTSelectList. +pipe_selection_item_list: + pipe_selection_item_list_no_comma opt_comma + { + $$ = parser->WithEndLocation($1, @$); + } + ; + +// This extends pipe_selection_item_list to support +// ASTGroupingItemOrder suffixes. +pipe_selection_item_list_with_order: + pipe_selection_item_list_no_comma_with_order opt_comma + { + $$ = parser->WithEndLocation($1, @$); + } + ; + +pipe_selection_item_list_with_order_or_empty: + pipe_selection_item_list_with_order + { + $$ = $1; + } + | %empty + { + $$ = MAKE_NODE(ASTSelectList, @$, {}); + } + ; + +pipe_rename_item: + identifier[old] opt_as identifier[new] + { + $$ = MAKE_NODE(ASTPipeRenameItem, @$, {$old, $new}); + } + | identifier "." + { + YYERROR_AND_ABORT_AT( + @1, + "Syntax error: Pipe RENAME can only rename columns by name alone; " + "Renaming columns under table aliases or fields under paths is not " + "supported"); + } + ; + +pipe_rename_item_list: + pipe_rename_item[item] + { + $$ = MAKE_NODE(ASTPipeRename, @$, {$item}); + } + | pipe_rename_item_list[list] "," pipe_rename_item[item] + { + $$ = WithExtraChildren($list, {$item}); + } + ; + +pipe_rename: + "RENAME" pipe_rename_item_list opt_comma + { + $$ = $pipe_rename_item_list; + } + ; + + +// Note that when using opt_comma for trailing commas, and passing through a +// node constructed in another rule (rather than construcing the node locally), +// it may be necessary to call +// $$ = parser->WithEndLocation($1, @$); +// to ensure the node's location range includes the comma. +opt_comma: + "," + | %empty + ; + +pipe_aggregate_clause: + "AGGREGATE" pipe_selection_item_list_with_order_or_empty + opt_group_by_clause_with_opt_comma + { + // Pipe AGGREGATE is represented as an ASTSelect inside an + // ASTPipeAggregate. This allows more resolver code sharing. + zetasql::ASTSelect* select = MAKE_NODE(ASTSelect, @$, {$2, $3}); + $$ = MAKE_NODE(ASTPipeAggregate, @$, {select}); + } + ; + +// |> GROUP BY is an error - likely because of an unwanted pipe character. +pipe_group_by: + "GROUP" + { + YYERROR_AND_ABORT_AT( + @1, + "Syntax error: GROUP BY should be part of a pipe AGGREGATE " + "operator, without a leading pipe character"); + } + ; + +pipe_set_operation_clause: + set_operation_metadata parenthesized_query { - $$ = $2; + $2->set_parenthesized(true); + $$ = MAKE_NODE(ASTPipeSetOperation, @$, {$1, $2}); + } + | pipe_set_operation_clause "," parenthesized_query + { + $3->set_parenthesized(true); + $$ = WithExtraChildren($1, {$3}); } - | %empty { $$ = nullptr; } ; -opt_like_path_expression: - "LIKE" maybe_dashed_path_expression +pipe_join: + opt_natural join_type join_hint "JOIN" opt_hint table_primary + opt_on_or_using_clause { - $$ = $2; + // Pipe JOIN has no LHS, so we use this placeholder in the ASTJoin. + zetasql::ASTPipeJoinLhsPlaceholder* join_lhs = + MAKE_NODE(ASTPipeJoinLhsPlaceholder, @$, {}); + + // JoinRuleAction expects a list of clauses, but this grammar rule only + // accepts one. Wrap it into a list. + zetasql::ASTOnOrUsingClauseList* clause_list = nullptr; + if ($7 != nullptr) { + clause_list = MAKE_NODE(ASTOnOrUsingClauseList, @$, {$7}); + } + + // Our main code for constructing ASTJoin is in JoinRuleAction. + // In other places, it handles complex cases of chains of joins with + // repeated join clauses. Here, we always have just one JOIN, + // so it just constructs a single ASTJoin. + zetasql::parser::ErrorInfo error_info; + auto *join_location = + parser->MakeLocation(NonEmptyRangeLocation(@1, @2, @3, @4)); + zetasql::ASTNode* join = zetasql::parser::JoinRuleAction( + @1, @$, + join_lhs, $1, $2, $3, $5, $6, clause_list, + join_location, parser, &error_info); + if (join == nullptr) { + YYERROR_AND_ABORT_AT(error_info.location, error_info.message); + } + + $$ = MAKE_NODE(ASTPipeJoin, @$, {join}); } - | %empty { $$ = nullptr; } ; -opt_clone_table: - "CLONE" clone_data_source +pipe_call: + "CALL" tvf opt_as_alias { - $$ = $2; + $$ = MAKE_NODE(ASTPipeCall, @$, {WithExtraChildren($2, {$3})}); } - | %empty { $$ = nullptr; } ; -opt_copy_table: - "COPY" copy_data_source +pipe_window_clause: + "WINDOW" pipe_selection_item_list { - $$ = $2; + // Pipe WINDOW is represented as an ASTSelect inside an + // ASTPipeWindow. This allows more resolver code sharing. + zetasql::ASTSelect* select = MAKE_NODE(ASTSelect, @$, {$2}); + $$ = MAKE_NODE(ASTPipeWindow, @$, {select}); } - | %empty { $$ = nullptr; } - ; + ; -all_or_distinct: - "ALL" { - $$ = MAKE_NODE(ASTSetOperationAllOrDistinct, @$, {}); - $$->set_value(zetasql::ASTSetOperation::ALL); +pipe_distinct: + "DISTINCT" + { + $$ = MAKE_NODE(ASTPipeDistinct, @$); } - | "DISTINCT" { - $$ = MAKE_NODE(ASTSetOperationAllOrDistinct, @$, {}); - $$->set_value(zetasql::ASTSetOperation::DISTINCT); + ; + +pipe_tablesample: + sample_clause + { + $$ = MAKE_NODE(ASTPipeTablesample, @$, {$1}); } - ; + ; -// Returns the token for a set operation as expected by -// ASTSetOperation::op_type(). -query_set_operation_type: - "UNION" - { - $$ = MAKE_NODE(ASTSetOperationType, @$, {}); - $$->set_value(zetasql::ASTSetOperation::UNION); - } - | KW_EXCEPT_IN_SET_OP - { - $$ = MAKE_NODE(ASTSetOperationType, @$, {}); - $$->set_value(zetasql::ASTSetOperation::EXCEPT); - } - | "INTERSECT" - { - $$ = MAKE_NODE(ASTSetOperationType, @$, {}); - $$->set_value(zetasql::ASTSetOperation::INTERSECT); - } - ; +pipe_as: + "AS" identifier + { + auto* alias = MAKE_NODE(ASTAlias, @2, {$2}); + $$ = MAKE_NODE(ASTPipeAs, @$, {alias}); + } + ; -query_primary_or_set_operation: - query_primary - | query_set_operation - ; +pipe_static_describe: + "STATIC_DESCRIBE" + { + $$ = MAKE_NODE(ASTPipeStaticDescribe, @$); + } + ; -parenthesized_query: - "(" query ")" +pipe_assert_base: + "ASSERT" expression + { + $$ = MAKE_NODE(ASTPipeAssert, @$, {$2}); + } + | pipe_assert_base "," expression + { + $$ = WithExtraChildren($1, {$3}); + } + ; + +pipe_assert: + pipe_assert_base opt_comma + { + $$ = parser->WithEndLocation($1, @$); + } + ; + +identifier_in_pipe_drop: + identifier + { + $$ = $identifier; + } + | identifier "." + { { - // We do not call $query->set_parenthesized(true) because typically the - // calling rule expects parentheses and will already insert one pair - // when unparsing. - $$ = $query; + YYERROR_AND_ABORT_AT( + @1, + "Syntax error: Pipe DROP can only drop columns by name alone; " + "Dropping columns under table aliases or fields under paths is not " + "supported"); } + } ; -select_or_from_keyword: - "SELECT" | "FROM" +// This is the same as identifier_list, but gives a custom error if the +// items are paths rather than just identifiers. +identifier_list_in_pipe_drop: + identifier_in_pipe_drop[item] + { + $$ = MAKE_NODE(ASTIdentifierList, @$, {$item}); + } + | identifier_list_in_pipe_drop[list] "," identifier_in_pipe_drop[item] + { + $$ = parser->WithEndLocation(WithExtraChildren($list, {$item}), @$); + } ; -query: - // We don't use an opt_with_clause for the first element because it causes - // shift/reduce conflicts. - with_clause query_primary_or_set_operation[query_primary] - opt_order_by_clause[order_by] - opt_limit_offset_clause[offset] +pipe_drop: + "DROP" identifier_list_in_pipe_drop[list] opt_comma + { + $$ = MAKE_NODE(ASTPipeDrop, @$, {$list}); + } + ; + +pipe_set_item: + identifier "=" expression { - $$ = MAKE_NODE(ASTQuery, @$, - {$with_clause, $query_primary, $order_by, $offset}); + $$ = MAKE_NODE(ASTPipeSetItem, @$, {$1, $3}); } - | with_clause_with_trailing_comma select_or_from_keyword + | identifier "." + { { - // TODO: Consider pointing the error location at the comma - // instead of at the SELECT. - YYERROR_AND_ABORT_AT(@2, - "Syntax error: Trailing comma after the WITH " - "clause before the main query is not allowed"); + YYERROR_AND_ABORT_AT( + @1, + "Syntax error: Pipe SET can only update columns by column name " + "alone; Setting columns under table aliases or fields under " + "paths is not supported"); } - | query_primary_or_set_operation[query_primary] - opt_order_by_clause[order_by] - opt_limit_offset_clause[offset] + } + ; + +pipe_set_item_list: + pipe_set_item { - zetasql::ASTQuery* query = $query_primary->GetAsOrNull< - zetasql::ASTQuery>(); - if (query && !query->parenthesized()) { - $$ = WithExtraChildren(query, {$order_by, $offset}); - } else if (query && !$order_by && !$offset) { - // This means it is a query originally and there are no other clauses. - // So then wrapping it is semantically useless. - $$ = query; - } else { - $$ = MAKE_NODE(ASTQuery, @$, {$query_primary, $order_by, $offset}); - } + $$ = MAKE_NODE(ASTPipeSet, @$, {$1}); + } + | pipe_set_item_list "," pipe_set_item + { + $$ = WithExtraChildren($1, {$3}); } - | opt_with_clause "FROM" - { - YYERROR_AND_ABORT_AT(@2, "Syntax error: Unexpected FROM"); - } ; +pipe_set: + "SET" pipe_set_item_list opt_comma + { + $$ = parser->WithLocation($2, @$); + } + ; + +pipe_pivot: + pivot_clause opt_as_alias + { + // The alias is parsed separately from pivot_clause but needs to be + // added into that AST node. + $$ = MAKE_NODE(ASTPipePivot, @$, { WithExtraChildren($1, {$2}) }); + } + ; + +pipe_unpivot: + unpivot_clause opt_as_alias + { + // The alias is parsed separately from unpivot_clause but needs to be + // added into that AST node. + $$ = MAKE_NODE(ASTPipeUnpivot, @$, { WithExtraChildren($1, {$2}) }); + } + ; + opt_corresponding_outer_mode: KW_FULL_IN_SET_OP opt_outer { $$ = MAKE_NODE(ASTSetOperationColumnPropagationMode, @$, {}); $$->set_value(zetasql::ASTSetOperation::FULL); } - | KW_OUTER + | "OUTER" { $$ = MAKE_NODE(ASTSetOperationColumnPropagationMode, @$, {}); $$->set_value(zetasql::ASTSetOperation::FULL); @@ -4873,7 +5516,7 @@ opt_corresponding_outer_mode: ; opt_strict: - KW_STRICT + "STRICT" { $$ = MAKE_NODE(ASTSetOperationColumnPropagationMode, @$, {}); $$->set_value(zetasql::ASTSetOperation::STRICT); @@ -4885,16 +5528,16 @@ opt_strict: ; opt_column_match_suffix: - KW_CORRESPONDING + "CORRESPONDING" { auto* mode = MAKE_NODE(ASTSetOperationColumnMatchMode, @$, {}); mode->set_value(zetasql::ASTSetOperation::CORRESPONDING); $$.column_match_mode = mode; $$.column_list = nullptr; } - | KW_CORRESPONDING KW_BY column_list + | "CORRESPONDING" "BY" column_list { - auto* mode = MAKE_NODE(ASTSetOperationColumnMatchMode, @KW_CORRESPONDING, @KW_BY, {}); + auto* mode = MAKE_NODE(ASTSetOperationColumnMatchMode, @1, @2, {}); mode->set_value(zetasql::ASTSetOperation::CORRESPONDING_BY); $$.column_match_mode = mode; $$.column_list = $column_list->GetAsOrDie(); @@ -4925,6 +5568,26 @@ query_set_operation_prefix: $prefix->mutable_child(0)->AddChild($set_operation_metadata); $$ = WithExtraChildren($prefix, {$query_primary}); } + | query_primary set_operation_metadata "FROM" + { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT(@3, "Syntax error: Unexpected FROM"); + } + YYERROR_AND_ABORT_AT(@3, absl::StrCat( + "Syntax error: Unexpected FROM; " + "FROM queries following a set operation must be parenthesized")); + } + | query_set_operation_prefix set_operation_metadata "FROM" + { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT(@3, "Syntax error: Unexpected FROM"); + } + YYERROR_AND_ABORT_AT(@3, absl::StrCat( + "Syntax error: Unexpected FROM; " + "FROM queries following a set operation must be parenthesized")); + } ; set_operation_metadata: @@ -4957,9 +5620,13 @@ query_primary: | parenthesized_query[query] opt_as_alias_with_required_as[alias] { if ($alias != nullptr) { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { YYERROR_AND_ABORT_AT( @alias, "Syntax error: Alias not allowed on parenthesized " "outer query"); + } + $$ = MAKE_NODE(ASTAliasedQueryExpression, @$, {$query, $alias}); } else { $query->set_parenthesized(true); $$ = $query; @@ -5005,12 +5672,10 @@ pre_select_with: opt_select_with: pre_select_with "WITH"[with] identifier { - @$.set_start(@with.start()); $$ = MAKE_NODE(ASTSelectWith, @$, {$identifier}); } | pre_select_with "WITH"[with] identifier KW_OPTIONS_IN_SELECT_WITH_OPTIONS options_list { - @$.set_start(@with.start()); $$ = MAKE_NODE(ASTSelectWith, @$, {$identifier, $options_list}); } | pre_select_with { $$ = nullptr; } @@ -5207,15 +5872,30 @@ select_column_expr: { $$ = MAKE_NODE(ASTSelectColumn, @$, {$1}); } - | expression "AS" identifier + | select_column_expr_with_as_alias + | expression[e] identifier[alias] + { + auto* alias = MAKE_NODE(ASTAlias, @alias, {$alias}); + $$ = MAKE_NODE(ASTSelectColumn, @$, {$e, alias}); + } + ; + +select_list_prefix_with_as_aliases: + select_column_expr_with_as_alias[c] + { + $$ = MAKE_NODE(ASTSelectList, @$, {$c}); + } + | select_list_prefix_with_as_aliases[list] "," select_column_expr_with_as_alias[c] { - auto* alias = MAKE_NODE(ASTAlias, @2, @3, {$3}); - $$ = MAKE_NODE(ASTSelectColumn, @$, {$1, alias}); + $$ = WithExtraChildren($list, {$c}); } - | expression identifier + ; + +select_column_expr_with_as_alias: + expression[expr] "AS"[as] identifier[alias] { - auto* alias = MAKE_NODE(ASTAlias, @2, {$2}); - $$ = MAKE_NODE(ASTSelectColumn, @$, {$1, alias}); + auto* alias = MAKE_NODE(ASTAlias, @as, @alias, {$alias}); + $$ = MAKE_NODE(ASTSelectColumn, @$, {$expr, alias}); } ; @@ -5286,7 +5966,7 @@ opt_as_or_into_alias: ; opt_as: - KW_AS + "AS" | %empty ; @@ -5387,6 +6067,24 @@ sample_clause: } ; +opt_match_recognize_and_sample_clauses: + %empty + { + $$.match_recognize_clause = nullptr; + $$.sample_clause = nullptr; + } + | match_recognize_clause[match_recognize] opt_sample_clause[sample] + { + $$.match_recognize_clause = $match_recognize; + $$.sample_clause = $sample; + } + | sample_clause[sample] opt_match_recognize_clause[match_recognize] + { + $$.match_recognize_clause = $match_recognize; + $$.sample_clause = $sample; + } + ; + opt_sample_clause: sample_clause | %empty { $$ = nullptr; } @@ -5592,14 +6290,69 @@ opt_pivot_or_unpivot_clause_and_alias: $$.alias = nullptr; $$.pivot_clause = nullptr; $$.unpivot_clause = nullptr; - }; + } + ; + +opt_match_recognize_clause: + match_recognize_clause + | %empty { $$ = nullptr; } + ; + +match_recognize_clause: + KW_MATCH_RECOGNIZE_RESERVED "(" + opt_partition_by_clause[partition_by] + order_by_clause[order_by] + "MEASURES" select_list_prefix_with_as_aliases[measures] + "PATTERN" "(" row_pattern_expr ")" + "DEFINE" with_expression_variable_prefix[definitions] + ")" opt_as_alias[alias] + { + $$ = MAKE_NODE(ASTMatchRecognizeClause, @$, + {$partition_by, $order_by, $measures, $row_pattern_expr, + $definitions, $alias}); + } + ; + +row_pattern_expr: + row_pattern_concatenation +| row_pattern_expr[alt] "|" row_pattern_concatenation[e] + { + $$ = MakeOrCombineRowPatternOperation( + zetasql::ASTRowPatternOperation::ALTERNATE, parser, @$, $alt, $e); + } +; + +row_pattern_concatenation: + row_pattern_factor + | row_pattern_concatenation[sequence] row_pattern_factor[e] + { + $$ = MakeOrCombineRowPatternOperation( + zetasql::ASTRowPatternOperation::CONCAT, parser, @$, $sequence, $e); + } + ; + +row_pattern_factor: + identifier + { + $$ = MAKE_NODE(ASTRowPatternVariable, @$, {$1}); + } + | "(" row_pattern_expr[e] ")" + { + $e->set_parenthesized(true); + // Don't include the location in the parentheses. Semantic error + // messages about this expression should point at the start of the + // expression, not at the opening parentheses. + $$ = $e; + } ; table_subquery: - parenthesized_query[query] opt_pivot_or_unpivot_clause_and_alias[clauses] opt_sample_clause[sample] + parenthesized_query[query] + opt_pivot_or_unpivot_clause_and_alias[pivot_and_alias] + opt_match_recognize_and_sample_clauses[postfix_ops] { zetasql::ASTQuery* query = $query; - if ($clauses.pivot_clause != nullptr) { + if ($pivot_and_alias.pivot_clause != nullptr) { query->set_is_pivot_input(true); } query->set_is_nested(true); @@ -5608,11 +6361,12 @@ table_subquery: // So set parenthesized to false. query->set_parenthesized(false); $$ = MAKE_NODE(ASTTableSubquery, @$, { - $query, $clauses.alias, $clauses.pivot_clause, $clauses.unpivot_clause, $sample}); + $query, $pivot_and_alias.alias, $pivot_and_alias.pivot_clause, + $pivot_and_alias.unpivot_clause, + $postfix_ops.match_recognize_clause, $postfix_ops.sample_clause}); } ; - table_clause: "TABLE" tvf_with_suffixes { @@ -5777,17 +6531,25 @@ tvf: tvf_with_suffixes: // Using the `tvf` production inside these rules causes a reduce conflict. - tvf_prefix_no_args ")" opt_hint opt_pivot_or_unpivot_clause_and_alias - opt_sample_clause + tvf_prefix_no_args[prefix] ")" + opt_hint[hint] + opt_pivot_or_unpivot_clause_and_alias[pivot_and_alias] + opt_match_recognize_and_sample_clauses[postfix_ops] { - $$ = WithExtraChildren(parser->WithEndLocation($1, @$), { - $3, $4.alias, $4.pivot_clause, $4.unpivot_clause, $5}); + $$ = WithExtraChildren(parser->WithEndLocation($prefix, @$), { + $hint, $pivot_and_alias.alias, $pivot_and_alias.pivot_clause, + $pivot_and_alias.unpivot_clause, + $postfix_ops.match_recognize_clause, $postfix_ops.sample_clause}); } - | tvf_prefix ")" opt_hint opt_pivot_or_unpivot_clause_and_alias - opt_sample_clause + | tvf_prefix[prefix] ")" + opt_hint[hint] + opt_pivot_or_unpivot_clause_and_alias[pivot_and_alias] + opt_match_recognize_and_sample_clauses[postfix_ops] { - $$ = WithExtraChildren(parser->WithEndLocation($1, @$), { - $3, $4.alias, $4.pivot_clause, $4.unpivot_clause, $5}); + $$ = WithExtraChildren(parser->WithEndLocation($prefix, @$), { + $hint, $pivot_and_alias.alias, $pivot_and_alias.pivot_clause, + $pivot_and_alias.unpivot_clause, + $postfix_ops.match_recognize_clause, $postfix_ops.sample_clause}); } ; @@ -5825,11 +6587,14 @@ table_path_expression_base: ; table_path_expression: - table_path_expression_base opt_hint - opt_pivot_or_unpivot_clause_and_alias - opt_with_offset_and_alias opt_at_system_time opt_sample_clause - { - if ( $4 != nullptr) { + table_path_expression_base[path] + opt_hint[hint] + opt_pivot_or_unpivot_clause_and_alias[pivot_and_alias] + opt_with_offset_and_alias[offset] + opt_at_system_time[time] + opt_match_recognize_and_sample_clauses[postfix_ops] + { + if ( $offset != nullptr) { // We do not support combining PIVOT or UNPIVOT with WITH OFFSET. // If we did, we would want the WITH OFFSET clause to appear in the // grammar before PIVOT so that it operates on the pivot input. @@ -5842,47 +6607,56 @@ table_path_expression: // put opt_with_offset_and_alias after PIVOT (so the right action // happens if we have a WITH OFFSET without PIVOT) and give an explicit // error if both clauses are present. - if ($3.pivot_clause != nullptr) { + if ($pivot_and_alias.pivot_clause != nullptr) { YYERROR_AND_ABORT_AT(@4, "PIVOT and WITH OFFSET cannot be combined"); } - if ($3.unpivot_clause != nullptr) { + if ($pivot_and_alias.unpivot_clause != nullptr) { YYERROR_AND_ABORT_AT(@4, "UNPIVOT and WITH OFFSET cannot be combined"); } } - if ($5 != nullptr) { - if ($3.pivot_clause != nullptr) { + if ($time != nullptr) { + if ($pivot_and_alias.pivot_clause != nullptr) { YYERROR_AND_ABORT_AT( @5, "Syntax error: PIVOT and FOR SYSTEM TIME AS OF " "may not be combined"); } - if ($3.unpivot_clause != nullptr) { + if ($pivot_and_alias.unpivot_clause != nullptr) { YYERROR_AND_ABORT_AT( @5, "Syntax error: UNPIVOT and FOR SYSTEM TIME AS OF " "may not be combined"); } } - $$ = MAKE_NODE(ASTTablePathExpression, @$, {$1, $2, $3.alias, - $3.pivot_clause, $3.unpivot_clause, $4, $5, $6}); + $$ = MAKE_NODE(ASTTablePathExpression, @$, {$path, $hint, + $pivot_and_alias.alias, $pivot_and_alias.pivot_clause, + $pivot_and_alias.unpivot_clause, $offset, $time, + $postfix_ops.match_recognize_clause, $postfix_ops.sample_clause}); }; table_primary: + // TODO: sample, pivot, match_recognize, etc should be grouped + // both in the grammar and in the AST, to apply to all + // table primary types, and would help dedup the unparser. + // Test coverage to add: pipes, parenthesized join. tvf_with_suffixes | table_path_expression - | "(" join ")" opt_sample_clause + | "(" join ")" + opt_match_recognize_and_sample_clauses[postfix_ops] { zetasql::parser::ErrorInfo error_info; auto node = zetasql::parser::TransformJoinExpression( - $2, parser, &error_info); + $join, parser, &error_info); if (node == nullptr) { YYERROR_AND_ABORT_AT(error_info.location, error_info.message); } - $$ = MAKE_NODE(ASTParenthesizedJoin, @$, {node, $4}); + $$ = MAKE_NODE(ASTParenthesizedJoin, @$, + {node, $postfix_ops.match_recognize_clause, + $postfix_ops.sample_clause}); } | table_subquery ; @@ -5967,9 +6741,9 @@ opt_on_or_using_clause: // Returns the join type id. Returns 0 to indicate "just a join". join_type: "CROSS" { $$ = zetasql::ASTJoin::CROSS; } - | KW_FULL opt_outer { $$ = zetasql::ASTJoin::FULL; } + | "FULL" opt_outer { $$ = zetasql::ASTJoin::FULL; } | "INNER" { $$ = zetasql::ASTJoin::INNER; } - | KW_LEFT opt_outer { $$ = zetasql::ASTJoin::LEFT; } + | "LEFT" opt_outer { $$ = zetasql::ASTJoin::LEFT; } | "RIGHT" opt_outer { $$ = zetasql::ASTJoin::RIGHT; } | %empty { $$ = zetasql::ASTJoin::DEFAULT_JOIN_TYPE; } ; @@ -6079,7 +6853,7 @@ from_clause_contents: YYERROR_AND_ABORT_AT( @1, "Query parameters cannot be used in place of table names"); } - | KW_DOUBLE_AT + | "@@" { YYERROR_AND_ABORT_AT( @1, "System variables cannot be used in place of table names"); @@ -6209,6 +6983,29 @@ grouping_set_list: } ; +// In selection items, NULLS FIRST/LAST is not allowed without ASC/DESC first. +opt_selection_item_order: + asc_or_desc opt_null_order + { + auto* node = + MAKE_NODE(ASTGroupingItemOrder, @$, {$opt_null_order}); + node->set_ordering_spec($1); + $$ = node; + } + | %empty { $$ = nullptr; } + ; + +// In grouping items, NULLS FIRST/LAST is allowed without ASC/DESC first. +opt_grouping_item_order: + opt_selection_item_order + | null_order + { + auto* node = + MAKE_NODE(ASTGroupingItemOrder, @$, {$null_order}); + $$ = node; + } + ; + grouping_item: "(" ")" { @@ -6218,13 +7015,24 @@ grouping_item: // Making AS optional currently causes a conflict because // KW_QUALIFY_NONRESERVED can follow GROUP BY. | expression opt_as_alias_with_required_as + opt_grouping_item_order { if ($2 != nullptr + && !parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES) ) { YYERROR_AND_ABORT_AT( @2, "Syntax error: GROUP BY does not support aliases"); } - $$ = MAKE_NODE(ASTGroupingItem, @$, {$1, $2}); + if ($3 != nullptr + && !parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES) + ) { + YYERROR_AND_ABORT_AT( + @3, absl::StrCat("Syntax error: Unexpected ", + parser->GetFirstTokenOfNode(@3))); + } + $$ = MAKE_NODE(ASTGroupingItem, @$, {$1, $2, $3}); } | rollup_list ")" { @@ -6240,11 +7048,26 @@ grouping_item: } ; +opt_and_order: + "AND" "ORDER" { + if (!parser->language_options().LanguageFeatureEnabled( + zetasql::FEATURE_PIPES)) { + YYERROR_AND_ABORT_AT( + @1, "Syntax error: Unexpected AND"); + } + $$ = true; + } + | + %empty { $$ = false; } + ; + group_by_preamble: "GROUP" opt_hint + opt_and_order "BY" { $$.hint = $opt_hint; + $$.and_order_by = $opt_and_order; } ; @@ -6252,6 +7075,7 @@ group_by_clause_prefix: group_by_preamble[preamble] grouping_item[item] { auto* node = MAKE_NODE(ASTGroupBy, @$, {$preamble.hint, $item}); + node->set_and_order_by($preamble.and_order_by); $$ = node; } | group_by_clause_prefix[prefix] "," grouping_item[item] @@ -6261,10 +7085,11 @@ group_by_clause_prefix: ; group_by_all: - group_by_preamble[preamble] KW_ALL[all] + group_by_preamble[preamble] "ALL"[all] { auto* group_by_all = MAKE_NODE(ASTGroupByAll, @all, {}); auto* node = MAKE_NODE(ASTGroupBy, @$, {$preamble.hint, group_by_all}); + node->set_and_order_by($preamble.and_order_by); $$ = node; } ; @@ -6279,6 +7104,17 @@ opt_group_by_clause: | %empty { $$ = nullptr; } ; +// Note: This version does not support GROUP BY ALL. +// Using `group_by_clause` instead of `group_by_clause_prefix` causes +// a shift/reduce conflict. +opt_group_by_clause_with_opt_comma: + group_by_clause_prefix opt_comma + { + $$ = parser->WithEndLocation($1, @$); + } + | %empty { $$ = nullptr; } + ; + having_clause: "HAVING" expression { @@ -6536,7 +7372,7 @@ opt_asc_or_desc: | %empty { $$ = zetasql::ASTOrderingExpression::UNSPECIFIED; } ; -opt_null_order: +null_order: "NULLS" "FIRST" { auto* null_order = MAKE_NODE(ASTNullOrder, @$, {}); @@ -6549,6 +7385,10 @@ opt_null_order: null_order->set_nulls_first(false); $$ = null_order; } + ; + +opt_null_order: + null_order | %empty { $$ = nullptr; } ; @@ -6580,7 +7420,7 @@ ordering_expression: expression opt_collate_clause opt_asc_or_desc opt_null_order { auto* ordering_expr = - MAKE_NODE(ASTOrderingExpression, @$, {$1, $2, $4}); + MAKE_NODE(ASTOrderingExpression, @$, {$1, $2, $4, nullptr}); ordering_expr->set_ordering_spec($3); $$ = ordering_expr; } @@ -6604,6 +7444,13 @@ order_by_clause: } ; +order_by_clause_with_opt_comma: + order_by_clause_prefix opt_comma + { + $$ = parser->WithEndLocation($1, @$); + } + ; + opt_order_by_clause: order_by_clause { $$ = $1; } | %empty { $$ = nullptr; } @@ -7221,7 +8068,7 @@ unparenthesized_expression_higher_prec_than_and: "Syntax error: Expression in BETWEEN must be " "parenthesized"); } - | expression_higher_prec_than_and is_operator KW_UNKNOWN %prec "IS" + | expression_higher_prec_than_and is_operator "UNKNOWN" %prec "IS" { // The Bison parser allows comparison expressions in the LHS, even // though these operators are at the same precedence level and are not @@ -7494,7 +8341,7 @@ dashed_identifier: out->set_path_parts(std::move(prev)); $$ = out; } - | identifier '-' FLOATING_POINT_LITERAL identifier + | identifier "-" FLOATING_POINT_LITERAL identifier { // a - 1. b if (parser->HasWhitespace(@1, @2) || parser->HasWhitespace(@2, @3)) { @@ -7514,7 +8361,7 @@ dashed_identifier: out->set_path_parts({{id1, "-", id2}, {id3}}); $$ = out; } - | dashed_identifier '-' FLOATING_POINT_LITERAL identifier + | dashed_identifier "-" FLOATING_POINT_LITERAL identifier { // a-b - 1. c if (parser->HasWhitespace(@1, @2) || parser->HasWhitespace(@2, @3)) { @@ -8389,7 +9236,7 @@ function_name_from_keyword: { $$ = parser->MakeIdentifier(@1, parser->GetInputText(@1)); } - | KW_LEFT + | "LEFT" { $$ = parser->MakeIdentifier(@1, parser->GetInputText(@1)); } @@ -8544,18 +9391,18 @@ sequence_arg: ; named_argument: - identifier KW_NAMED_ARGUMENT_ASSIGNMENT expression + identifier "=>" expression { $$ = MAKE_NODE(ASTNamedArgument, @$, {$1, $3}); } - | identifier KW_NAMED_ARGUMENT_ASSIGNMENT lambda_argument + | identifier "=>" lambda_argument { $$ = MAKE_NODE(ASTNamedArgument, @$, {$identifier, $lambda_argument}); } ; lambda_argument: - lambda_argument_list KW_LAMBDA_ARROW expression + lambda_argument_list "->" expression { $$ = MAKE_NODE(ASTLambda, @$, {$1, $3}); } @@ -9158,9 +10005,9 @@ label: ; system_variable_expression: - KW_DOUBLE_AT path_expression %prec DOUBLE_AT_PRECEDENCE + "@@" path_expression %prec DOUBLE_AT_PRECEDENCE { - if (parser->HasWhitespace(@KW_DOUBLE_AT, @path_expression)) { + if (parser->HasWhitespace(@1, @2)) { // TODO: Add a deprecation warning in this case. } $$ = MAKE_NODE(ASTSystemVariableExpr, @$, {$2}); @@ -9264,10 +10111,12 @@ common_keyword_as_identifier: | "MACRO" | "MAP" | "MATCH" + | KW_MATCH_RECOGNIZE_NONRESERVED | "MATCHED" | "MATERIALIZED" | "MAX" | "MAXVALUE" + | "MEASURES" | "MESSAGE" | "METADATA" | "MIN" @@ -9282,6 +10131,7 @@ common_keyword_as_identifier: | "OUTPUT" | "OVERWRITE" | "PARTITIONS" + | "PATTERN" | "PERCENT" | "PIVOT" | "POLICIES" @@ -9333,6 +10183,7 @@ common_keyword_as_identifier: | "SQL" | "STABLE" | "START" + | "STATIC_DESCRIBE" | "STORED" | "STORING" | "STRICT" @@ -9653,7 +10504,6 @@ nested_dml_statement: { OVERRIDE_NEXT_TOKEN_CHAR_LOOKBACK('(', LB_OPEN_NESTED_DML); } "("[open] dml_statement ")" { - @$.set_start(@open.start()); $$ = $dml_statement; } ; @@ -10100,9 +10950,9 @@ drop_statement: ; index_type: - KW_SEARCH + "SEARCH" { $$ = IndexTypeKeywords::kSearch; } - | KW_VECTOR + | "VECTOR" { $$ = IndexTypeKeywords::kVector; }; opt_index_type: @@ -10120,7 +10970,7 @@ unterminated_non_empty_statement_list: } $$ = MAKE_NODE(ASTStatementList, @$, {$stmt}); } - | unterminated_non_empty_statement_list[old_list] ';' + | unterminated_non_empty_statement_list[old_list] ";" unterminated_statement[new_stmt] { if ($new_stmt->Is()) { @@ -10138,7 +10988,7 @@ unterminated_non_empty_top_level_statement_list: { $$ = MAKE_NODE(ASTStatementList, @$, {$stmt}); } - | unterminated_non_empty_top_level_statement_list[old_list] ';' + | unterminated_non_empty_top_level_statement_list[old_list] ";" unterminated_statement[new_stmt] { $$ = parser->WithEndLocation(WithExtraChildren($old_list, {$new_stmt}), @$); @@ -10146,7 +10996,7 @@ unterminated_non_empty_top_level_statement_list: ; opt_execute_into_clause: - KW_INTO identifier_list + "INTO" identifier_list { $$ = MAKE_NODE(ASTExecuteIntoClause, @$, {$2}); } @@ -10157,7 +11007,7 @@ opt_execute_into_clause: ; execute_using_argument: - expression KW_AS identifier + expression "AS" identifier { auto* alias = MAKE_NODE(ASTAlias, @3, @3, {$3}); $$ = MAKE_NODE(ASTExecuteUsingArgument, @$, {$1, alias}); @@ -10182,7 +11032,7 @@ execute_using_argument_list: ; opt_execute_using_clause: - KW_USING execute_using_argument_list + "USING" execute_using_argument_list { $$ = $2; } @@ -10193,7 +11043,7 @@ opt_execute_using_clause: ; execute_immediate: - KW_EXECUTE KW_IMMEDIATE expression opt_execute_into_clause + "EXECUTE" "IMMEDIATE" expression opt_execute_into_clause opt_execute_using_clause { $$ = MAKE_NODE(ASTExecuteImmediateStatement, @$, {$3, $4, $5}); @@ -10206,7 +11056,7 @@ script: $1->set_variable_declarations_allowed(true); $$ = MAKE_NODE(ASTScript, @$, {$1}); } - | unterminated_non_empty_top_level_statement_list ';' + | unterminated_non_empty_top_level_statement_list ";" { $1->set_variable_declarations_allowed(true); $$ = MAKE_NODE(ASTScript, @$, {parser->WithEndLocation($1, @$)}); @@ -10221,7 +11071,7 @@ script: ; statement_list: - unterminated_non_empty_statement_list ';' + unterminated_non_empty_statement_list ";" { $$ = parser->WithEndLocation($1, @$); } @@ -10282,14 +11132,6 @@ if_statement: { $$ = parser->WithEndLocation($1, @$); } - | - if_statement_unclosed error - { - // This rule produces an error for any IF statement not closed with END - // IF. Without it, the error would indicate that the parser expected the - // END keyword without explicitly referencing END IF. - YYERROR_AND_ABORT_AT(@2, "Syntax error: Expected END IF"); - } ; when_then_clauses: @@ -10387,7 +11229,6 @@ loop_statement: { OVERRIDE_NEXT_TOKEN_LOOKBACK(KW_LOOP, LB_OPEN_STATEMENT_BLOCK); } "LOOP"[loop] statement_list "END" "LOOP" { - @$.set_start(@loop.start()); $$ = MAKE_NODE(ASTWhileStatement, @$, {$statement_list}); } ; @@ -10414,7 +11255,6 @@ repeat_statement: zetasql::FEATURE_V_1_3_REPEAT)) { YYERROR_AND_ABORT_AT(@repeat, "REPEAT is not supported"); } - @$.set_start(@repeat.start()); $$ = MAKE_NODE(ASTRepeatStatement, @$, {$statement_list, $until_clause}); } ; @@ -10539,6 +11379,10 @@ next_statement_kind_parenthesized_select: "(" next_statement_kind_parenthesized_select { $$ = $2; } | "SELECT" { $$ = zetasql::ASTQueryStatement::kConcreteNodeKind; } | "WITH" { $$ = zetasql::ASTQueryStatement::kConcreteNodeKind; } + // FROM is always treated as indicating the statement is a query, even + // if the syntax is not enabled. This is okay because a statement + // starting with FROM couldn't be anything else, and it'll be reasonable + // to give errors a statement starting with FROM being an invalid query. | "FROM" { $$ = zetasql::ASTQueryStatement::kConcreteNodeKind; } ; @@ -10570,7 +11414,7 @@ next_statement_kind_without_hint: | next_statement_kind_parenthesized_select | "DEFINE" "TABLE" { $$ = zetasql::ASTDefineTableStatement::kConcreteNodeKind; } - | "DEFINE for macros" "MACRO" + | KW_DEFINE_FOR_MACROS "MACRO" { $$ = zetasql::ASTDefineMacroStatement::kConcreteNodeKind; } | "EXECUTE" "IMMEDIATE" { $$ = zetasql::ASTExecuteImmediateStatement::kConcreteNodeKind; } @@ -10656,6 +11500,8 @@ next_statement_kind_without_hint: { $$ = zetasql::ASTAbortBatchStatement::kConcreteNodeKind; } | "ALTER" "APPROX" "VIEW" { $$ = zetasql::ASTAlterApproxViewStatement::kConcreteNodeKind; } + | "ALTER" "CONNECTION" + { $$ = zetasql::ASTAlterConnectionStatement::kConcreteNodeKind; } | "ALTER" "DATABASE" { $$ = zetasql::ASTAlterDatabaseStatement::kConcreteNodeKind; } | "ALTER" "SCHEMA" @@ -10683,6 +11529,8 @@ next_statement_kind_without_hint: { $$ = zetasql::ASTAlterModelStatement::kConcreteNodeKind; } | "CREATE" "DATABASE" { $$ = zetasql::ASTCreateDatabaseStatement::kConcreteNodeKind; } + | "CREATE" next_statement_kind_create_modifiers "CONNECTION" + { $$ = zetasql::ASTCreateConnectionStatement::kConcreteNodeKind; } | "CREATE" next_statement_kind_create_modifiers opt_aggregate "CONSTANT" { @@ -10813,7 +11661,7 @@ spanner_primary_key: } ; -opt_spanner_index_interleave_clause: +spanner_index_interleave_clause: "," "INTERLEAVE" "IN" maybe_dashed_path_expression { if (!parser->language_options().LanguageFeatureEnabled( @@ -10825,8 +11673,6 @@ opt_spanner_index_interleave_clause: clause->set_type(zetasql::ASTSpannerInterleaveClause::IN); $$ = clause; } - | %empty { $$ = nullptr; } - ; opt_spanner_interleave_in_parent_clause: "," "INTERLEAVE" "IN" "PARENT" maybe_dashed_path_expression diff --git a/zetasql/parser/flex_tokenizer.cc b/zetasql/parser/flex_tokenizer.cc index b7bd4a158..2444e84ad 100644 --- a/zetasql/parser/flex_tokenizer.cc +++ b/zetasql/parser/flex_tokenizer.cc @@ -49,12 +49,10 @@ absl::StatusOr ZetaSqlFlexTokenizer::GetNextToken( ZetaSqlFlexTokenizer::ZetaSqlFlexTokenizer(absl::string_view filename, absl::string_view input, - bool preserve_comments, int start_offset) : filename_(filename), start_offset_(start_offset), - input_stream_(std::make_unique(input)), - preserve_comments_(preserve_comments) { + input_stream_(std::make_unique(input)) { // Seek the stringstream to the start_offset, and then instruct flex to read // from the stream. (Flex has the ability to read multiple consecutive // streams, but we only ever feed it one.) diff --git a/zetasql/parser/flex_tokenizer.h b/zetasql/parser/flex_tokenizer.h index a6b1f8272..2ddc9a950 100644 --- a/zetasql/parser/flex_tokenizer.h +++ b/zetasql/parser/flex_tokenizer.h @@ -34,7 +34,7 @@ namespace zetasql { namespace parser { -// Flex-based tokenizer for the ZetaSQL Bison parser. +// Flex-based tokenizer for ZetaSQL. class ZetaSqlFlexTokenizer final : public ZetaSqlFlexTokenizerBase { public: // Type aliases to improve readability of API. @@ -43,7 +43,7 @@ class ZetaSqlFlexTokenizer final : public ZetaSqlFlexTokenizerBase { // Constructs a wrapper around a flex generated tokenizer. // `filename`, `input` and `language_options` must outlive this object. ZetaSqlFlexTokenizer(absl::string_view filename, absl::string_view input, - bool preserve_comments, int start_offset); + int start_offset); ZetaSqlFlexTokenizer(const ZetaSqlFlexTokenizer&) = delete; ZetaSqlFlexTokenizer& operator=(const ZetaSqlFlexTokenizer&) = delete; @@ -70,10 +70,6 @@ class ZetaSqlFlexTokenizer final : public ZetaSqlFlexTokenizerBase { // sentinel. std::unique_ptr input_stream_; - // If set, comments are preserved. Used only in raw tokenization for the - // formatter. - bool preserve_comments_; - // The Flex-generated tokenizer does not work with absl::StatusOr, so it // stores the error in this field. GetNextToken() grabs the status from here // when returning the result. diff --git a/zetasql/parser/flex_tokenizer.l b/zetasql/parser/flex_tokenizer.l index eee264027..fe7f864c1 100644 --- a/zetasql/parser/flex_tokenizer.l +++ b/zetasql/parser/flex_tokenizer.l @@ -340,10 +340,12 @@ loop return Tokens::KW_LOOP; macro return Tokens::KW_MACRO; map return Tokens::KW_MAP; match return Tokens::KW_MATCH; +match_recognize return Tokens::KW_MATCH_RECOGNIZE_NONRESERVED; matched return Tokens::KW_MATCHED; materialized return Tokens::KW_MATERIALIZED; max return Tokens::KW_MAX; maxvalue return Tokens::KW_MAXVALUE; +measures return Tokens::KW_MEASURES; merge return Tokens::KW_MERGE; message return Tokens::KW_MESSAGE; metadata return Tokens::KW_METADATA; @@ -372,6 +374,7 @@ over return Tokens::KW_OVER; overwrite return Tokens::KW_OVERWRITE; partition return Tokens::KW_PARTITION; partitions return Tokens::KW_PARTITIONS; +pattern return Tokens::KW_PATTERN; percent return Tokens::KW_PERCENT; pivot return Tokens::KW_PIVOT; policies return Tokens::KW_POLICIES; @@ -428,6 +431,7 @@ source return Tokens::KW_SOURCE; sql return Tokens::KW_SQL; stable return Tokens::KW_STABLE; start return Tokens::KW_START; +static_describe return Tokens::KW_STATIC_DESCRIBE; stored return Tokens::KW_STORED; storing return Tokens::KW_STORING; strict return Tokens::KW_STRICT; @@ -536,6 +540,7 @@ zone return Tokens::KW_ZONE; "?" return '?'; "!" return '!'; "%" return '%'; +"|>" return Tokens::KW_PIPE; "@" return '@'; "@@" return Tokens::KW_DOUBLE_AT; "." return '.'; @@ -553,16 +558,13 @@ zone return Tokens::KW_ZONE; in better errors for unexpected end of input. But it doesn't skip trailing comments. */ -<*>{whitespace_no_comments} {} +{whitespace_no_comments} {} -<*>{comment} { - if (preserve_comments_) { - return Tokens::COMMENT; - } -} +{comment} return Tokens::COMMENT; <> { - // The location of YYEOF is always [N, N), where N is the length of the input. + /* The location of YYEOF is always [N, N), where N is the length of the input. + */ location->mutable_start().SetByteOffset( location->mutable_end().GetByteOffset()); yyterminate(); diff --git a/zetasql/parser/gen_parse_tree.py b/zetasql/parser/gen_parse_tree.py index daad3cd59..3eaa5136b 100644 --- a/zetasql/parser/gen_parse_tree.py +++ b/zetasql/parser/gen_parse_tree.py @@ -39,7 +39,7 @@ from zetasql.parser.generator_utils import Trim from zetasql.parser.generator_utils import UpperCamelCase -NEXT_NODE_TAG_ID = 477 +NEXT_NODE_TAG_ID = 488 ROOT_NODE_NAME = 'ASTNode' @@ -255,6 +255,10 @@ def EnumScalarType(enum_name, node_name, cpp_default): 'ASTSpannerInterleaveClause', 'NOT_SET') +SCALAR_ROW_PATTERN_OPERATION_TYPE = EnumScalarType( + 'OperationType', 'ASTRowPatternOperation', 'OPERATION_TYPE_UNSPECIFIED' +) + SCALAR_COLUMN_MATCH_MODE = EnumScalarType( 'ColumnMatchMode', 'ASTSetOperation', 'BY_POSITION' ) @@ -800,6 +804,29 @@ def main(argv): tag_id=2) ]) + gen.AddNode( + name='ASTAliasedQueryExpression', + tag_id=475, + parent='ASTQueryExpression', + comment=""" + This is a parenthesized query expression with an alias. + """, + fields=[ + Field( + 'query', + 'ASTQuery', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field( + 'alias', + 'ASTAlias', + tag_id=3, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + gen.AddNode( name='ASTQuery', tag_id=4, @@ -849,10 +876,401 @@ def main(argv): True if this query represents the input to a pivot clause. """, ), + Field( + 'pipe_operator_list', + 'ASTPipeOperator', + tag_id=8, + field_loader=FieldLoaderMethod.REST_AS_REPEATED, + ), ], use_custom_debug_string=True, ) + gen.AddNode( + name='ASTFromQuery', + comment=""" + This represents a FROM query, which has just a FROM clause and + no other clauses. This is enabled by FEATURE_PIPES. + """, + tag_id=414, + parent='ASTQueryExpression', + fields=[ + Field( + 'from_clause', + 'ASTFromClause', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ) + ], + ) + + gen.AddNode( + name='ASTPipeOperator', + tag_id=408, + parent='ASTNode', + is_abstract=True, + comment=""" + This is the superclass of all ASTPipe* operators, representing one + pipe operation in a chain. + """, + ) + + gen.AddNode( + name='ASTPipeExtend', + tag_id=409, + parent='ASTPipeOperator', + comment=""" + Pipe EXTEND is represented with an ASTSelect with only the + SELECT clause present, where the SELECT clause stores the + EXTEND expression list. + Using this representation rather than storing an ASTSelectList + makes sharing resolver code easier. + """, + fields=[ + Field( + 'select', + 'ASTSelect', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ) + ], + ) + + gen.AddNode( + name='ASTPipeRenameItem', + tag_id=482, + parent='ASTPipeOperator', + fields=[ + Field( + 'old_name', + 'ASTIdentifier', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field( + 'new_name', + 'ASTIdentifier', + tag_id=3, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeRename', + tag_id=483, + parent='ASTPipeOperator', + fields=[ + Field( + 'rename_item_list', + 'ASTPipeRenameItem', + tag_id=2, + field_loader=FieldLoaderMethod.REST_AS_REPEATED, + ) + ], + ) + + gen.AddNode( + name='ASTPipeAggregate', + tag_id=412, + parent='ASTPipeOperator', + comment=""" + Pipe AGGREGATE is represented with an ASTSelect with only the + SELECT and (optionally) GROUP BY clause present, where the SELECT + clause stores the AGGREGATE expression list. + Using this representation rather than storing an ASTSelectList and + ASTGroupBy makes sharing resolver code easier. + """, + fields=[ + Field( + 'select', + 'ASTSelect', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeSetOperation', + tag_id=413, + parent='ASTPipeOperator', + comment=""" + Pipe set operations are represented differently from ASTSetOperation + because we have the set operation and metadata always once, and then + one or more (not two or more) input queries. + + The syntax looks like + |> UNION ALL [modifiers] (query1), (query2), ... + and it produces the combination of input_table plus all rhs queries. + """, + fields=[ + Field( + 'metadata', + 'ASTSetOperationMetadata', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field( + 'inputs', + 'ASTQueryExpression', + tag_id=3, + field_loader=FieldLoaderMethod.REST_AS_REPEATED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeJoin', + tag_id=415, + parent='ASTPipeOperator', + comment=""" + Pipe JOIN is represented with an ASTJoin, where the required lhs + is always an ASTPipeJoinLhsPlaceholder. + """, + fields=[ + Field( + 'join', + 'ASTJoin', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeCall', + tag_id=417, + parent='ASTPipeOperator', + fields=[ + Field( + 'tvf', + 'ASTTVF', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ) + ], + ) + + gen.AddNode( + name='ASTPipeWindow', + tag_id=418, + parent='ASTPipeOperator', + comment=""" + Pipe WINDOW is represented with an ASTSelect with only the + SELECT clause present, where the SELECT clause stores the + WINDOW expression list. + Using this representation rather than storing an ASTSelectList + makes sharing resolver code easier. + """, + fields=[ + Field( + 'select', + 'ASTSelect', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeWhere', + tag_id=419, + parent='ASTPipeOperator', + fields=[ + Field( + 'where', + 'ASTWhereClause', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ) + ], + ) + + gen.AddNode( + name='ASTPipeSelect', + tag_id=420, + parent='ASTPipeOperator', + comment=""" + Pipe SELECT is represented with an ASTSelect with only the + SELECT clause present. + Using this representation rather than storing an ASTSelectList + makes sharing resolver code easier. + """, + fields=[ + Field( + 'select', + 'ASTSelect', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeLimitOffset', + tag_id=421, + parent='ASTPipeOperator', + fields=[ + Field( + 'limit_offset', + 'ASTLimitOffset', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeOrderBy', + tag_id=422, + parent='ASTPipeOperator', + fields=[ + Field( + 'order_by', + 'ASTOrderBy', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeDistinct', + tag_id=431, + parent='ASTPipeOperator', + ) + + gen.AddNode( + name='ASTPipeTablesample', + tag_id=435, + parent='ASTPipeOperator', + fields=[ + Field( + 'sample', + 'ASTSampleClause', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeAs', + tag_id=437, + parent='ASTPipeOperator', + fields=[ + Field( + 'alias', + 'ASTAlias', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeStaticDescribe', + tag_id=447, + parent='ASTPipeOperator', + ) + + gen.AddNode( + name='ASTPipeAssert', + tag_id=448, + parent='ASTPipeOperator', + fields=[ + Field( + 'condition', + 'ASTExpression', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field( + 'message_list', + 'ASTExpression', + tag_id=3, + field_loader=FieldLoaderMethod.REST_AS_REPEATED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeDrop', + tag_id=449, + parent='ASTPipeOperator', + fields=[ + Field( + 'column_list', + 'ASTIdentifierList', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeSetItem', + tag_id=450, + parent='ASTNode', + fields=[ + Field( + 'column', + 'ASTIdentifier', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field( + 'expression', + 'ASTExpression', + tag_id=3, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeSet', + tag_id=451, + parent='ASTPipeOperator', + fields=[ + Field( + 'set_item_list', + 'ASTPipeSetItem', + tag_id=2, + field_loader=FieldLoaderMethod.REST_AS_REPEATED, + ), + ], + ) + + gen.AddNode( + name='ASTPipePivot', + tag_id=467, + parent='ASTPipeOperator', + fields=[ + Field( + 'pivot_clause', + 'ASTPivotClause', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTPipeUnpivot', + tag_id=468, + parent='ASTPipeOperator', + fields=[ + Field( + 'unpivot_clause', + 'ASTUnpivotClause', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + gen.AddNode( name='ASTSelect', tag_id=5, @@ -910,6 +1328,13 @@ def main(argv): name='ASTSelectList', tag_id=6, parent='ASTNode', + comment=""" + This is the column list in SELECT, containing expressions with optional + aliases and supporting SELECT-list features like star and dot-star. + + This is also used for selection lists in pipe operators, where + ASTGroupingItemOrder suffixes may be present. + """, fields=[ Field( 'columns', @@ -932,6 +1357,17 @@ def main(argv): field_loader=FieldLoaderMethod.REQUIRED, ), Field('alias', 'ASTAlias', tag_id=3), + Field( + 'grouping_item_order', + 'ASTGroupingItemOrder', + tag_id=4, + comment=""" + This is the ordering suffix {ASC|DESC} [NULLS {FIRST|LAST}]. + It can only be present on ASTSelectColumns parsed with the + `pipe_selection_item_list_with_order` rule, which is + currently only the pipe AGGREGATE operator. + """, + ), ], ) @@ -1220,6 +1656,11 @@ def main(argv): 'for_system_time', 'ASTForSystemTime', tag_id=9), + Field( + 'match_recognize_clause', + 'ASTMatchRecognizeClause', + tag_id=11, + ), Field( 'sample_clause', 'ASTSampleClause', @@ -1230,6 +1671,17 @@ def main(argv): """ ) + gen.AddNode( + name='ASTPipeJoinLhsPlaceholder', + tag_id=416, + parent='ASTTableExpression', + comment=""" + This is a placehodler ASTTableExpression used for the lhs field in + the ASTJoin used to represent ASTPipeJoin. + """, + fields=[], + ) + gen.AddNode( name='ASTFromClause', tag_id=17, @@ -1417,6 +1869,7 @@ def main(argv): Field('collate', 'ASTCollate', tag_id=3), Field('null_order', 'ASTNullOrder', tag_id=4), Field('ordering_spec', SCALAR_ORDERING_SPEC, tag_id=5), + Field('option_list', 'ASTOptionsList', tag_id=6), ], extra_public_defs=""" bool descending() const { return ordering_spec_ == DESC; } @@ -1438,6 +1891,22 @@ def main(argv): ], ) + gen.AddNode( + name='ASTGroupingItemOrder', + tag_id=466, + parent='ASTNode', + use_custom_debug_string=True, + fields=[ + Field('ordering_spec', SCALAR_ORDERING_SPEC, tag_id=2), + Field('null_order', 'ASTNullOrder', tag_id=3), + ], + extra_public_defs=""" + bool descending() const { + return ordering_spec_ == ASTOrderingExpression::DESC; + } + """, + ) + gen.AddNode( name='ASTGroupingItem', tag_id=25, @@ -1475,6 +1944,22 @@ def main(argv): ASTGroupingSet. """, ), + Field( + 'alias', + 'ASTAlias', + tag_id=6, + comment="""Alias can only be present for `expression` cases. + It can be present but is not valid outside pipe AGGREGATE. + """, + ), + Field( + 'grouping_item_order', + 'ASTGroupingItemOrder', + tag_id=7, + comment="""Order can only be present for `expression` cases. + It can be present but is not valid outside pipe AGGREGATE. + """, + ), ], ) @@ -1482,6 +1967,7 @@ def main(argv): name='ASTGroupBy', tag_id=26, parent='ASTNode', + use_custom_debug_string=True, fields=[ Field('hint', 'ASTHint', tag_id=2), Field( @@ -1500,6 +1986,17 @@ def main(argv): tag_id=4, field_loader=FieldLoaderMethod.REST_AS_REPEATED, ), + Field( + 'and_order_by', + SCALAR_BOOL, + tag_id=5, + # Used for backwards compatibility, and so the serializer tests + # don't see different outputs in the strip(pipes) version. + serialize_default_value=False, + comment=""" + True if query had AND ORDER BY on the GROUP BY. + """, + ), ], ) @@ -2868,6 +3365,7 @@ def main(argv): private_comment=""" Required. """), + Field('match_recognize_clause', 'ASTMatchRecognizeClause', tag_id=4), Field( 'sample_clause', 'ASTSampleClause', @@ -3176,6 +3674,10 @@ def main(argv): 'unpivot_clause', 'ASTUnpivotClause', tag_id=5), + Field( + 'match_recognize_clause', + 'ASTMatchRecognizeClause', + tag_id=7), Field( 'sample_clause', 'ASTSampleClause', @@ -4267,6 +4769,82 @@ def main(argv): field_loader=FieldLoaderMethod.REQUIRED), ]) + gen.AddNode( + name='ASTMatchRecognizeClause', + tag_id=484, + parent='ASTNode', + comment=""" + Represents a row pattern recognition clause, i.e., MATCH_RECOGNIZE(). + """, + fields=[ + Field('partition_by', 'ASTPartitionBy', tag_id=2), + Field('order_by', 'ASTOrderBy', tag_id=3), + Field('measures', 'ASTSelectList', tag_id=4), + Field( + 'pattern', + 'ASTRowPatternExpression', + tag_id=5, + field_loader=FieldLoaderMethod.REQUIRED, + ), + # Note: Leaving a placeholder for subset_clause. + Field( + 'pattern_variable_definition_list', + 'ASTSelectList', + tag_id=7, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field('output_alias', 'ASTAlias', tag_id=8), + ], + ) + + gen.AddNode( + name='ASTRowPatternExpression', + tag_id=485, + parent='ASTNode', + is_abstract=True, + comment=""" + Represents a pattern expression for row pattern recognition. + """, + fields=[ + Field('parenthesized', SCALAR_BOOL, tag_id=2), + ], + ) + + gen.AddNode( + name='ASTRowPatternVariable', + tag_id=486, + parent='ASTRowPatternExpression', + fields=[ + Field( + 'name', + 'ASTIdentifier', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + ], + ) + + gen.AddNode( + name='ASTRowPatternOperation', + tag_id=487, + parent='ASTRowPatternExpression', + comment=""" + Represents an operation on a pattern expression. For example, it can + be an alternation (A|B) or a concatenation (A B), or quantification. + Note that alternation is analogous to OR, while concatenation is analogous + to AND. + """, + fields=[ + Field('op_type', SCALAR_ROW_PATTERN_OPERATION_TYPE, tag_id=2), + Field( + 'inputs', + 'ASTRowPatternExpression', + tag_id=3, + field_loader=FieldLoaderMethod.REST_AS_REPEATED, + ), + ], + ) + gen.AddNode( name='ASTQualify', tag_id=135, @@ -4933,6 +5511,7 @@ def main(argv): 'unpivot_clause', 'ASTUnpivotClause', tag_id=7), + Field('match_recognize_clause', 'ASTMatchRecognizeClause', tag_id=9), Field( 'sample', 'ASTSampleClause', @@ -5059,6 +5638,33 @@ def main(argv): field_loader=FieldLoaderMethod.REQUIRED), ]) + gen.AddNode( + name='ASTCreateConnectionStatement', + tag_id=479, + parent='ASTCreateStatement', + comment=""" + This represents a CREATE CONNECTION statement, i.e., + CREATE [OR REPLACE] CONNECTION + [IF NOT EXISTS] OPTIONS (name=value, ...); + """, + fields=[ + Field( + 'name', + 'ASTPathExpression', + tag_id=2, + field_loader=FieldLoaderMethod.REQUIRED, + ), + Field( + 'options_list', + 'ASTOptionsList', + tag_id=3, + ), + ], + extra_public_defs=""" + const ASTPathExpression* GetDdlTarget() const override { return name_; } + """, + ) + gen.AddNode( name='ASTCreateConstantStatement', tag_id=165, @@ -5073,16 +5679,19 @@ def main(argv): 'name', 'ASTPathExpression', tag_id=2, - field_loader=FieldLoaderMethod.REQUIRED), + field_loader=FieldLoaderMethod.REQUIRED, + ), Field( 'expr', 'ASTExpression', tag_id=3, - field_loader=FieldLoaderMethod.REQUIRED), + field_loader=FieldLoaderMethod.REQUIRED, + ), ], extra_public_defs=""" - const ASTPathExpression* GetDdlTarget() const override { return name_; } - """) + const ASTPathExpression* GetDdlTarget() const override { return name_; } + """, + ) gen.AddNode( name='ASTCreateDatabaseStatement', @@ -5276,6 +5885,12 @@ def main(argv): tag_id=171, parent='ASTPrintableLeaf', comment="Represents 'ALL COLUMNS' index key expression.", + fields=[ + Field( + 'column_options', + 'ASTIndexItemList', + tag_id=2), + ] ) gen.AddNode( @@ -5360,16 +5975,21 @@ def main(argv): 'ASTIndexStoringExpressionList', tag_id=7, ), - Field('options_list', 'ASTOptionsList', tag_id=8), - Field('is_unique', SCALAR_BOOL, tag_id=9), - Field('is_search', SCALAR_BOOL, tag_id=10), + Field( + 'optional_partition_by', + 'ASTPartitionBy', + tag_id=8, + ), + Field('options_list', 'ASTOptionsList', tag_id=9), + Field('is_unique', SCALAR_BOOL, tag_id=10), + Field('is_search', SCALAR_BOOL, tag_id=11), Field( 'spanner_interleave_clause', 'ASTSpannerInterleaveClause', - tag_id=11, + tag_id=12, ), - Field('spanner_is_null_filtered', SCALAR_BOOL, tag_id=12), - Field('is_vector', SCALAR_BOOL, tag_id=13), + Field('spanner_is_null_filtered', SCALAR_BOOL, tag_id=13), + Field('is_vector', SCALAR_BOOL, tag_id=14), ], extra_public_defs=""" const ASTPathExpression* GetDdlTarget() const override { return name_; } @@ -8774,6 +9394,17 @@ def main(argv): bool IsAlterStatement() const override { return true; } """) + gen.AddNode( + name='ASTAlterConnectionStatement', + tag_id=480, + parent='ASTAlterStatementBase', + comment=""" + Represents the statement ALTER CONNECTION SET OPTIONS + + """, + fields=[], + ) + gen.AddNode( name='ASTAlterDatabaseStatement', tag_id=306, diff --git a/zetasql/parser/keywords.cc b/zetasql/parser/keywords.cc index 2b542d49d..d5999bde3 100644 --- a/zetasql/parser/keywords.cc +++ b/zetasql/parser/keywords.cc @@ -218,10 +218,15 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"macro", KW_MACRO, kNotReserved}, {"map", KW_MAP, kNotReserved}, {"match", KW_MATCH}, + {"match_recognize", + ConditionallyReservedToken{KW_MATCH_RECOGNIZE_RESERVED, + KW_MATCH_RECOGNIZE_NONRESERVED}, + kConditionallyReserved}, {"matched", KW_MATCHED}, {"materialized", KW_MATERIALIZED}, {"max", KW_MAX}, {"maxvalue", KW_MAXVALUE}, + {"measures", KW_MEASURES}, {"merge", KW_MERGE, kReserved}, {"message", KW_MESSAGE}, {"metadata", KW_METADATA}, @@ -250,6 +255,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"overwrite", KW_OVERWRITE}, {"partition", KW_PARTITION, kReserved}, {"partitions", KW_PARTITIONS}, + {"pattern", KW_PATTERN}, {"percent", KW_PERCENT}, {"pivot", KW_PIVOT}, {"policies", KW_POLICIES}, @@ -308,6 +314,7 @@ constexpr KeywordInfoPOD kAllKeywords[] = { {"sql", KW_SQL}, {"stable", KW_STABLE}, {"start", KW_START}, + {"static_describe", KW_STATIC_DESCRIBE}, {"stored", KW_STORED}, {"storing", KW_STORING}, {"strict", KW_STRICT}, diff --git a/zetasql/parser/token_disambiguator.cc b/zetasql/parser/lookahead_transformer.cc similarity index 90% rename from zetasql/parser/token_disambiguator.cc rename to zetasql/parser/lookahead_transformer.cc index 920d13b47..974342d27 100644 --- a/zetasql/parser/token_disambiguator.cc +++ b/zetasql/parser/lookahead_transformer.cc @@ -14,7 +14,7 @@ // limitations under the License. // -#include "zetasql/parser/token_disambiguator.h" +#include "zetasql/parser/lookahead_transformer.h" #include #include @@ -49,34 +49,34 @@ namespace zetasql { // lookup on every token. namespace parser_internal { using zetasql::parser::BisonParserMode; -using zetasql::parser::DisambiguatorLexer; +using zetasql::parser::LookaheadTransformer; using TokenKind = int; -void SetForceTerminate(DisambiguatorLexer* disambiguator, int* end_offset) { - return disambiguator->SetForceTerminate(end_offset); +void SetForceTerminate(LookaheadTransformer* lookahead_transformer, + int* end_offset) { + return lookahead_transformer->SetForceTerminate(end_offset); } -void PushBisonParserMode(DisambiguatorLexer* disambiguator, +void PushBisonParserMode(LookaheadTransformer* lookahead_transformer, BisonParserMode mode) { - return disambiguator->PushBisonParserMode(mode); + return lookahead_transformer->PushBisonParserMode(mode); } -void PopBisonParserMode(DisambiguatorLexer* disambiguator) { - return disambiguator->PopBisonParserMode(); +void PopBisonParserMode(LookaheadTransformer* lookahead_transformer) { + return lookahead_transformer->PopBisonParserMode(); } -int GetNextToken(DisambiguatorLexer* disambiguator, absl::string_view* text, - ParseLocationRange* location) { - return disambiguator->GetNextToken(text, location); +int GetNextToken(LookaheadTransformer* lookahead_transformer, + absl::string_view* text, ParseLocationRange* location) { + return lookahead_transformer->GetNextToken(text, location); } -absl::Status OverrideNextTokenLookback(DisambiguatorLexer* disambiguator, - bool parser_lookahead_is_empty, - TokenKind expected_next_token, - TokenKind lookback_token) { - return disambiguator->OverrideNextTokenLookback( +absl::Status OverrideNextTokenLookback( + LookaheadTransformer* lookahead_transformer, bool parser_lookahead_is_empty, + TokenKind expected_next_token, TokenKind lookback_token) { + return lookahead_transformer->OverrideNextTokenLookback( parser_lookahead_is_empty, expected_next_token, lookback_token); } -absl::Status OverrideCurrentTokenLookback(DisambiguatorLexer* disambiguator, - TokenKind new_token_kind) { - return disambiguator->OverrideCurrentTokenLookback(new_token_kind); +absl::Status OverrideCurrentTokenLookback( + LookaheadTransformer* lookahead_transformer, TokenKind new_token_kind) { + return lookahead_transformer->OverrideCurrentTokenLookback(new_token_kind); } } // namespace parser_internal @@ -206,7 +206,7 @@ static bool IsValidPreviousTokenToSqlStatement(TokenKind token) { // - The token is followed by a colon, and the followed by one of the tokens in // [BEGIN, WHILE, LOOP, REPEAT, FOR]. // -// `previous_token`: the token the disambiguator sees before +// `previous_token`: the token the lookahead_transformer sees before // `token_with_location`. static bool IsScriptLabel(TokenKind lookback, const TokenWithLocation& token_with_location, @@ -233,19 +233,24 @@ static bool IsScriptLabel(TokenKind lookback, } } -void DisambiguatorLexer::ApplyConditionallyReservedKeywords(TokenKind& kind) { +void LookaheadTransformer::ApplyConditionallyReservedKeywords(TokenKind& kind) { switch (kind) { case Token::KW_QUALIFY_NONRESERVED: if (language_options_.IsReservedKeyword("QUALIFY")) { kind = Token::KW_QUALIFY_RESERVED; } break; + case Token::KW_MATCH_RECOGNIZE_NONRESERVED: + if (language_options_.IsReservedKeyword("MATCH_RECOGNIZE")) { + kind = Token::KW_MATCH_RECOGNIZE_RESERVED; + } + break; default: break; } } -void DisambiguatorLexer::FetchNextToken( +void LookaheadTransformer::FetchNextToken( const std::optional& current, std::optional& next) { if (current.has_value() && current->token.kind == Token::YYEOF) { @@ -262,6 +267,12 @@ void DisambiguatorLexer::FetchNextToken( next.emplace(); absl::StatusOr next_token = macro_expander_->GetNextToken(); + if (mode_ != BisonParserMode::kTokenizerPreserveComments) { + // Skip comment tokens if we do not need to preserve comments. + while (next_token.ok() && next_token->kind == Token::COMMENT) { + next_token = macro_expander_->GetNextToken(); + } + } if (next_token.ok()) { next->token = *next_token; ApplyConditionallyReservedKeywords(next->token.kind); @@ -316,7 +327,8 @@ static TokenWithLocation FuseTokensIntoTokenKind( }; } -void DisambiguatorLexer::FuseLookahead1IntoCurrent(TokenKind fused_token_kind) { +void LookaheadTransformer::FuseLookahead1IntoCurrent( + TokenKind fused_token_kind) { ABSL_DCHECK(current_token_.has_value()); ABSL_DCHECK(IsAdjacentPrecedingToken(current_token_, lookahead_1_)); current_token_->token = FuseTokensIntoTokenKind( @@ -377,7 +389,7 @@ static bool IsLiteralBeforeAdjacentUnquotedIdentifier( // accept the sequence of tokens are identified to verify that changing the kind // of `token` does not break any unanticipated cases where that sequence would // currently be accepted. -TokenKind DisambiguatorLexer::ApplyTokenDisambiguation( +TokenKind LookaheadTransformer::ApplyTokenDisambiguation( const TokenWithLocation& token_with_location) { const TokenKind token = token_with_location.kind; const Location& location = token_with_location.location; @@ -676,7 +688,7 @@ TokenKind DisambiguatorLexer::ApplyTokenDisambiguation( return token; } -absl::Status DisambiguatorLexer::OverrideNextTokenLookback( +absl::Status LookaheadTransformer::OverrideNextTokenLookback( bool parser_lookahead_is_empty, TokenKind expected_next_token, TokenKind lookback_token) { ZETASQL_RET_CHECK(current_token_.has_value()) << "current_token_ not populated."; @@ -689,7 +701,7 @@ absl::Status DisambiguatorLexer::OverrideNextTokenLookback( return absl::OkStatus(); } -bool DisambiguatorLexer::LookbackTokenCanBeBeforeDotInPathExpression( +bool LookaheadTransformer::LookbackTokenCanBeBeforeDotInPathExpression( TokenKind token_kind) const { ABSL_DCHECK(token_kind != Token::EXP_IN_FLOAT_NO_SIGN); ABSL_DCHECK(token_kind != Token::STANDALONE_EXPONENT_SIGN); @@ -709,7 +721,7 @@ static bool IsPlusOrMinus(TokenKind token_kind) { return token_kind == '+' || token_kind == '-'; } -bool DisambiguatorLexer::FuseExponentPartIntoFloatingPointLiteral() { +bool LookaheadTransformer::FuseExponentPartIntoFloatingPointLiteral() { if (!IsAdjacentPrecedingToken(current_token_, lookahead_1_)) { return false; } @@ -744,7 +756,7 @@ bool DisambiguatorLexer::FuseExponentPartIntoFloatingPointLiteral() { } } -TokenKind DisambiguatorLexer::TransformDotSymbol() { +TokenKind LookaheadTransformer::TransformDotSymbol() { if (LookbackTokenCanBeBeforeDotInPathExpression(Lookback1())) { // This dot is part of a path expression, return '.' directly. current_token_->lookback_override = Token::LB_DOT_IN_PATH_EXPRESSION; @@ -762,7 +774,7 @@ TokenKind DisambiguatorLexer::TransformDotSymbol() { return '.'; } -void DisambiguatorLexer::TransformIntegerLiteral() { +void LookaheadTransformer::TransformIntegerLiteral() { TokenKind initial_kind = current_token_->token.kind; ABSL_DCHECK(initial_kind == Token::DECIMAL_INTEGER_LITERAL || initial_kind == Token::HEX_INTEGER_LITERAL); @@ -812,7 +824,7 @@ void DisambiguatorLexer::TransformIntegerLiteral() { } } -TokenKind DisambiguatorLexer::SetOverrideErrorAndReturnEof( +TokenKind LookaheadTransformer::SetOverrideErrorAndReturnEof( absl::string_view error_message, const Location& error_location) { if (!current_token_.has_value()) { current_token_.emplace(); @@ -839,19 +851,19 @@ class NoOpExpander : public MacroExpanderBase { }; } // namespace -TokenKind DisambiguatorLexer::Lookahead1() const { +TokenKind LookaheadTransformer::Lookahead1() const { return lookahead_1_->token.kind; } -TokenKind DisambiguatorLexer::Lookahead2() const { +TokenKind LookaheadTransformer::Lookahead2() const { return lookahead_2_->token.kind; } -TokenKind DisambiguatorLexer::Lookahead3() const { +TokenKind LookaheadTransformer::Lookahead3() const { return lookahead_3_->token.kind; } -void DisambiguatorLexer::PopulateLookaheads() { +void LookaheadTransformer::PopulateLookaheads() { if (!lookahead_1_.has_value()) { FetchNextToken(current_token_, lookahead_1_); } @@ -863,7 +875,7 @@ void DisambiguatorLexer::PopulateLookaheads() { } } -TokenKind DisambiguatorLexer::Lookback1() const { +TokenKind LookaheadTransformer::Lookback1() const { if (lookback_1_.has_value()) { if (lookback_1_->lookback_override != kNoToken) { return lookback_1_->lookback_override; @@ -874,7 +886,7 @@ TokenKind DisambiguatorLexer::Lookback1() const { return kNoToken; } -TokenKind DisambiguatorLexer::Lookback2() const { +TokenKind LookaheadTransformer::Lookback2() const { if (lookback_2_.has_value()) { if (lookback_2_->lookback_override != kNoToken) { return lookback_2_->lookback_override; @@ -885,8 +897,8 @@ TokenKind DisambiguatorLexer::Lookback2() const { return kNoToken; } -TokenKind DisambiguatorLexer::GetNextToken(absl::string_view* text, - Location* yylloc) { +TokenKind LookaheadTransformer::GetNextToken(absl::string_view* text, + Location* yylloc) { // Advance the token buffers. lookback_2_.swap(lookback_1_); lookback_1_.swap(current_token_); @@ -965,15 +977,15 @@ static std::optional MakeStartModeToken( } } -absl::StatusOr> DisambiguatorLexer::Create( - BisonParserMode mode, absl::string_view filename, absl::string_view input, - int start_offset, const LanguageOptions& language_options, - const macros::MacroCatalog* macro_catalog, zetasql_base::UnsafeArena* arena) { +absl::StatusOr> +LookaheadTransformer::Create(BisonParserMode mode, absl::string_view filename, + absl::string_view input, int start_offset, + const LanguageOptions& language_options, + const macros::MacroCatalog* macro_catalog, + zetasql_base::UnsafeArena* arena) { // TODO: take the token_provider as an injected dependency. auto token_provider = std::make_unique( - filename, input, - /*preserve_comments=*/mode == BisonParserMode::kTokenizerPreserveComments, - start_offset, /*end_offset=*/std::nullopt); + filename, input, start_offset, /*end_offset=*/std::nullopt); std::unique_ptr macro_expander; if (language_options.LanguageFeatureEnabled(FEATURE_V_1_4_SQL_MACROS)) { @@ -988,12 +1000,12 @@ absl::StatusOr> DisambiguatorLexer::Create( ZETASQL_RET_CHECK(macro_catalog == nullptr); macro_expander = std::make_unique(std::move(token_provider)); } - return absl::WrapUnique(new DisambiguatorLexer( + return absl::WrapUnique(new LookaheadTransformer( mode, MakeStartModeToken(mode, filename, start_offset), language_options, std::move(macro_expander))); } -DisambiguatorLexer::DisambiguatorLexer( +LookaheadTransformer::LookaheadTransformer( BisonParserMode mode, std::optional start_token, const LanguageOptions& language_options, std::unique_ptr expander) @@ -1008,7 +1020,7 @@ DisambiguatorLexer::DisambiguatorLexer( PopulateLookaheads(); } -int64_t DisambiguatorLexer::num_lexical_tokens() const { +int64_t LookaheadTransformer::num_lexical_tokens() const { return num_inserted_tokens_ + macro_expander_->num_unexpanded_tokens_consumed(); } @@ -1016,14 +1028,14 @@ int64_t DisambiguatorLexer::num_lexical_tokens() const { // TODO: this overload should also be updated to return the image, and // all callers should be updated. In fact, all callers should simply use // TokenWithLocation, and maybe have the image attached there. -absl::Status DisambiguatorLexer::GetNextToken(ParseLocationRange* location, - TokenKind* token) { +absl::Status LookaheadTransformer::GetNextToken(ParseLocationRange* location, + TokenKind* token) { absl::string_view image; *token = GetNextToken(&image, location); return GetOverrideError(); } -void DisambiguatorLexer::SetForceTerminate(int* end_byte_offset) { +void LookaheadTransformer::SetForceTerminate(int* end_byte_offset) { if (end_byte_offset != nullptr) { if (!current_token_.has_value()) { // If no tokens have been returned, set `end_byte_offset` to 0 to indicate @@ -1057,25 +1069,25 @@ void DisambiguatorLexer::SetForceTerminate(int* end_byte_offset) { ResetToEof(template_token, lookahead_3_); } -void DisambiguatorLexer::ResetToEof( +void LookaheadTransformer::ResetToEof( const TokenWithOverrideError& template_token, std::optional& lookahead) const { lookahead = template_token; lookahead->token.kind = Token::YYEOF; } -void DisambiguatorLexer::PushBisonParserMode(BisonParserMode mode) { +void LookaheadTransformer::PushBisonParserMode(BisonParserMode mode) { restore_modes_.push(mode_); mode_ = mode; } -void DisambiguatorLexer::PopBisonParserMode() { +void LookaheadTransformer::PopBisonParserMode() { ABSL_DCHECK(!restore_modes_.empty()); mode_ = restore_modes_.top(); restore_modes_.pop(); } -bool DisambiguatorLexer::Lookahead1IsRealEndOfInput() const { +bool LookaheadTransformer::Lookahead1IsRealEndOfInput() const { if (lookahead_1_->token.kind != Token::YYEOF) { return false; } @@ -1095,18 +1107,18 @@ bool DisambiguatorLexer::Lookahead1IsRealEndOfInput() const { return true; } -absl::Status DisambiguatorLexer::OverrideCurrentTokenLookback( +absl::Status LookaheadTransformer::OverrideCurrentTokenLookback( TokenKind new_token_kind) { ZETASQL_RET_CHECK(current_token_.has_value()); current_token_->lookback_override = new_token_kind; return absl::OkStatus(); } -void DisambiguatorLexer::PushState(StateType state) { +void LookaheadTransformer::PushState(StateType state) { state_stack_.push(state); } -bool DisambiguatorLexer::PopStateIfMatch(StateType target_state) { +bool LookaheadTransformer::PopStateIfMatch(StateType target_state) { if (state_stack_.empty() || state_stack_.top() != target_state) { return false; } @@ -1114,12 +1126,12 @@ bool DisambiguatorLexer::PopStateIfMatch(StateType target_state) { return true; } -bool DisambiguatorLexer::IsInTemplatedTypeState() const { +bool LookaheadTransformer::IsInTemplatedTypeState() const { return !state_stack_.empty() && state_stack_.top() == kInTemplatedType; } TokenKind -DisambiguatorLexer::EmitInvalidLiteralPrecedesIdentifierTokenIfApplicable() { +LookaheadTransformer::EmitInvalidLiteralPrecedesIdentifierTokenIfApplicable() { ABSL_DCHECK(current_token_.has_value()); TokenKind token = current_token_->token.kind; if (token != Token::INTEGER_LITERAL && diff --git a/zetasql/parser/token_disambiguator.h b/zetasql/parser/lookahead_transformer.h similarity index 87% rename from zetasql/parser/token_disambiguator.h rename to zetasql/parser/lookahead_transformer.h index 1f26df609..bedf48552 100644 --- a/zetasql/parser/token_disambiguator.h +++ b/zetasql/parser/lookahead_transformer.h @@ -14,8 +14,8 @@ // limitations under the License. // -#ifndef ZETASQL_PARSER_TOKEN_DISAMBIGUATOR_H_ -#define ZETASQL_PARSER_TOKEN_DISAMBIGUATOR_H_ +#ifndef ZETASQL_PARSER_LOOKAHEAD_TRANSFORMER_H_ +#define ZETASQL_PARSER_LOOKAHEAD_TRANSFORMER_H_ #include #include @@ -55,16 +55,16 @@ struct TokenWithOverrideError { // `lookback_token` and typically ignore `token.kind`. TokenKind lookback_override = kNoToken; - // The disambiguator may want to return an error directly. It does this by - // returning EOF to the bison parser, which then may or may not spew out its - // own error message. The BisonParser wrapper then grabs the error from here - // instead. + // The lookahead_transformer may want to return an error directly. It does + // this by returning EOF to the bison parser, which then may or may not spew + // out its own error message. The BisonParser wrapper then grabs the error + // from here instead. absl::Status error = absl::OkStatus(); }; -class DisambiguatorLexer final { +class LookaheadTransformer final { public: - static absl::StatusOr> Create( + static absl::StatusOr> Create( BisonParserMode mode, absl::string_view filename, absl::string_view input, int start_offset, const LanguageOptions& language_options, const macros::MacroCatalog* macro_catalog, zetasql_base::UnsafeArena* arena); @@ -93,7 +93,7 @@ class DisambiguatorLexer final { // // Output parameters: // - The output parameters are undefined if - // - The disambiguator has been SetForceTerminate. + // - The lookahead_transformer has been SetForceTerminate. // - Or the returned token has an error. // - Otherwise, they will be updated with the text and location of the // returned token. @@ -163,9 +163,9 @@ class DisambiguatorLexer final { // // This context hint is not useful to affect the lexer's choice of token // for `expected_next_token` because in the case that the parser's LA(1) - // buffer if full, not only has the token already left the disambiguator, - // the parser may have acted on that token kind. This is useful for affecting - // subsequent tokens beyond `expected_next_token`. + // buffer if full, not only has the token already left the + // lookahead_transformer, the parser may have acted on that token kind. This + // is useful for affecting subsequent tokens beyond `expected_next_token`. absl::Status OverrideNextTokenLookback(bool parser_lookahead_is_empty, TokenKind expected_next_token, TokenKind lookback_token); @@ -181,13 +181,13 @@ class DisambiguatorLexer final { private: using StateType = char; - DisambiguatorLexer(BisonParserMode mode, - std::optional start_token, - const LanguageOptions& language_options, - std::unique_ptr expander); + LookaheadTransformer(BisonParserMode mode, + std::optional start_token, + const LanguageOptions& language_options, + std::unique_ptr expander); - DisambiguatorLexer(const DisambiguatorLexer&) = delete; - DisambiguatorLexer& operator=(const DisambiguatorLexer&) = delete; + LookaheadTransformer(const LookaheadTransformer&) = delete; + LookaheadTransformer& operator=(const LookaheadTransformer&) = delete; // This friend is used by the unit test to help test internals. friend class TokenTestThief; @@ -274,7 +274,8 @@ class DisambiguatorLexer final { const LanguageOptions& language_options_; - // The underlying macro expander which feeds tokens to this disambiguator. + // The underlying macro expander which feeds tokens to this + // lookahead_transformer. std::unique_ptr macro_expander_; // If this is set to true, the next token returned will be EOF, even if we're @@ -301,18 +302,19 @@ class DisambiguatorLexer final { // `IsAdjacentPrecedingToken(current_token_, lookahead_1_)` returns true. void FuseLookahead1IntoCurrent(TokenKind fused_token_kind); - // Pushes a state onto the stack used by the disambiguator to handle + // Pushes a state onto the stack used by the lookahead_transformer to handle // paren-balancing operations. Typically, `state` is a character sort that - // is an "open" marker (e.g. '('). Internally, the disambiguator tracks - // opening and closing of chars that are typically balanced (e.g. '(' or '[' - // but not '<' which is used unbalanced for comparison operators). Used in - // conjunction with `PopStateIfMatch` to find the matching close token. + // is an "open" marker (e.g. '('). Internally, the lookahead_transformer + // tracks opening and closing of chars that are typically balanced (e.g. '(' + // or '[' but not '<' which is used unbalanced for comparison operators). Used + // in conjunction with `PopStateIfMatch` to find the matching close token. void PushState(StateType state); // Pops the top of the stack and returns true if the top of `state_stack_` // matches `target_state`. Otherwise does nothing and returns false. bool PopStateIfMatch(StateType target_state); - // Returns whether the disambiguator is in the `kInTemplatedType` state. + // Returns whether the lookahead_transformer is in the `kInTemplatedType` + // state. bool IsInTemplatedTypeState() const; // If the current token is an integer or float literal followed by an @@ -341,8 +343,8 @@ class DisambiguatorLexer final { // point literal, i.e. "." or INTEGER_LITERAL. bool FuseExponentPartIntoFloatingPointLiteral(); - // The token the disambiguator returned the last time when GetNextToken was - // called. + // The token the lookahead_transformer returned the last time when + // GetNextToken was called. std::optional current_token_; // The token returned before `current_token_`. TokenWithOverrideError is used @@ -359,7 +361,8 @@ class DisambiguatorLexer final { // windows (e.g. no more than two or three lookaheads). // // Note the errors stored in the lookaheads should not be exposed. The - // disambiguator should only return the errors stored in `current_token_`. + // lookahead_transformer should only return the errors stored in + // `current_token_`. // // Invariants: // - If `current_token_` or a lookahead is Token::YYEOF, all further @@ -378,11 +381,11 @@ class DisambiguatorLexer final { // Stores the special symbols that affect the token disambiguation behaviors. // // If the top of the stack stores: - // - `kInTemplatedType`: The disambiguator is processing tokens inside a - // templated type, so for example it stops recognizing ">>" as + // - `kInTemplatedType`: The lookahead_transformer is processing tokens inside + // a templated type, so for example it stops recognizing ">>" as // KW_SHIFT_RIGHT but two ">"s. - // - '(' or ')': The disambiguator is processing tokens inside a pair of - // parentheses or after an unpaired ')', mainly used to temporarily leave + // - '(' or ')': The lookahead_transformer is processing tokens inside a pair + // of parentheses or after an unpaired ')', mainly used to temporarily leave // the `kInTemplatedType` state. std::stack state_stack_; }; @@ -390,4 +393,4 @@ class DisambiguatorLexer final { } // namespace parser } // namespace zetasql -#endif // ZETASQL_PARSER_TOKEN_DISAMBIGUATOR_H_ +#endif // ZETASQL_PARSER_LOOKAHEAD_TRANSFORMER_H_ diff --git a/zetasql/parser/flex_tokenizer_test.cc b/zetasql/parser/lookahead_transformer_test.cc similarity index 76% rename from zetasql/parser/flex_tokenizer_test.cc rename to zetasql/parser/lookahead_transformer_test.cc index d2b299e75..89964b83c 100644 --- a/zetasql/parser/flex_tokenizer_test.cc +++ b/zetasql/parser/lookahead_transformer_test.cc @@ -14,7 +14,7 @@ // limitations under the License. // -#include "zetasql/parser/flex_tokenizer.h" +#include "zetasql/parser/lookahead_transformer.h" #include #include @@ -28,7 +28,6 @@ #include "zetasql/parser/macros/token_with_location.h" #include "zetasql/parser/parse_tree.h" #include "zetasql/parser/parser.h" -#include "zetasql/parser/token_disambiguator.h" #include "zetasql/public/language_options.h" #include "zetasql/public/parse_location.h" #include "zetasql/public/parse_resume_location.h" @@ -62,38 +61,38 @@ using Location = ParseLocationRange; // private API. class TokenTestThief { public: - static TokenKind Lookahead1(DisambiguatorLexer& lexer) { - return lexer.Lookahead1(); + static TokenKind Lookahead1(LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.Lookahead1(); } - static TokenKind Lookahead2(DisambiguatorLexer& lexer) { - return lexer.Lookahead2(); + static TokenKind Lookahead2(LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.Lookahead2(); } - static TokenKind Lookahead3(DisambiguatorLexer& lexer) { - return lexer.Lookahead3(); + static TokenKind Lookahead3(LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.Lookahead3(); } - static TokenKind Lookback1(DisambiguatorLexer& lexer) { - return lexer.Lookback1(); + static TokenKind Lookback1(LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.Lookback1(); } - static TokenKind Lookback2(DisambiguatorLexer& lexer) { - return lexer.Lookback2(); + static TokenKind Lookback2(LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.Lookback2(); } static std::optional GetCurrentToken( - const DisambiguatorLexer& lexer) { - return lexer.current_token_; + const LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.current_token_; } static std::optional GetPreviousToken( - const DisambiguatorLexer& lexer) { - return lexer.lookback_1_; + const LookaheadTransformer& lookahead_transformer) { + return lookahead_transformer.lookback_1_; } }; -class FlexTokenizerTest : public ::testing::Test { +class LookaheadTransformerTest : public ::testing::Test { public: std::vector GetAllTokens(BisonParserMode mode, absl::string_view sql) { - auto tokenizer = DisambiguatorLexer::Create( + auto tokenizer = LookaheadTransformer::Create( mode, "fake_file", sql, 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr); ZETASQL_DCHECK_OK(tokenizer); @@ -110,61 +109,59 @@ class FlexTokenizerTest : public ::testing::Test { LanguageOptions options_; }; -TEST_F(FlexTokenizerTest, ParameterKeywordStatementMode) { +TEST_F(LookaheadTransformerTest, ParameterKeywordStatementMode) { EXPECT_THAT(GetAllTokens(BisonParserMode::kStatement, "a @select c"), ElementsAre(Token::MODE_STATEMENT, Token::IDENTIFIER, '@', Token::IDENTIFIER, Token::IDENTIFIER, Token::YYEOF)); } -TEST_F(FlexTokenizerTest, ParameterKeywordTokenizerMode) { +TEST_F(LookaheadTransformerTest, ParameterKeywordTokenizerMode) { EXPECT_THAT(GetAllTokens(BisonParserMode::kTokenizer, "a @select c"), ElementsAre(Token::IDENTIFIER, '@', Token::IDENTIFIER, Token::IDENTIFIER, Token::YYEOF)); } -TEST_F(FlexTokenizerTest, SysvarKeywordStatementMode) { +TEST_F(LookaheadTransformerTest, SysvarKeywordStatementMode) { EXPECT_THAT( GetAllTokens(BisonParserMode::kStatement, "a @@where c"), ElementsAre(Token::MODE_STATEMENT, Token::IDENTIFIER, Token::KW_DOUBLE_AT, Token::IDENTIFIER, Token::IDENTIFIER, Token::YYEOF)); } -TEST_F(FlexTokenizerTest, SysvarKeywordTokenizerMode) { +TEST_F(LookaheadTransformerTest, SysvarKeywordTokenizerMode) { EXPECT_THAT(GetAllTokens(BisonParserMode::kTokenizer, "a @@where c"), ElementsAre(Token::IDENTIFIER, Token::KW_DOUBLE_AT, Token::IDENTIFIER, Token::IDENTIFIER, Token::YYEOF)); } -TEST_F(FlexTokenizerTest, QueryParamCurrentDate) { +TEST_F(LookaheadTransformerTest, QueryParamCurrentDate) { EXPECT_THAT(GetAllTokens(BisonParserMode::kStatement, "a @current_date c"), ElementsAre(Token::MODE_STATEMENT, Token::IDENTIFIER, '@', Token::IDENTIFIER, Token::IDENTIFIER, Token::YYEOF)); } -TEST_F(FlexTokenizerTest, SysvarWithDotId) { +TEST_F(LookaheadTransformerTest, SysvarWithDotId) { EXPECT_THAT( GetAllTokens(BisonParserMode::kStatement, "SELECT @@ORDER.WITH.c"), ElementsAre(Token::MODE_STATEMENT, Token::KW_SELECT, Token::KW_DOUBLE_AT, Token::IDENTIFIER, '.', - // The dot identifier mini-tokenizer needs to kick in after - // the disambiguation of ORDER to identifier. Token::IDENTIFIER, '.', Token::IDENTIFIER, Token::YYEOF)); } -absl::StatusOr GetNextToken(DisambiguatorLexer& tokenizer, +absl::StatusOr GetNextToken(LookaheadTransformer& tokenizer, Location& location) { TokenKind token_kind; ZETASQL_RETURN_IF_ERROR(tokenizer.GetNextToken(&location, &token_kind)); return token_kind; } -TEST_F(FlexTokenizerTest, Lookahead1) { +TEST_F(LookaheadTransformerTest, Lookahead1) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); EXPECT_EQ(TokenTestThief::Lookahead1(tokenizer), Token::IDENTIFIER); @@ -191,15 +188,15 @@ TEST_F(FlexTokenizerTest, Lookahead1) { EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::YYEOF)); } -TEST_F(FlexTokenizerTest, Lookahead1WithForceTerminate) { +TEST_F(LookaheadTransformerTest, Lookahead1WithForceTerminate) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); EXPECT_EQ(TokenTestThief::Lookahead1(tokenizer), Token::IDENTIFIER); @@ -218,14 +215,14 @@ TEST_F(FlexTokenizerTest, Lookahead1WithForceTerminate) { EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::YYEOF)); } -TEST_F(FlexTokenizerTest, Lookahead2) { +TEST_F(LookaheadTransformerTest, Lookahead2) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -254,14 +251,14 @@ TEST_F(FlexTokenizerTest, Lookahead2) { EXPECT_EQ(TokenTestThief::Lookahead2(tokenizer), Token::YYEOF); } -TEST_F(FlexTokenizerTest, Lookahead2BeforeLookahead1) { +TEST_F(LookaheadTransformerTest, Lookahead2BeforeLookahead1) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -277,14 +274,14 @@ TEST_F(FlexTokenizerTest, Lookahead2BeforeLookahead1) { Token::DECIMAL_INTEGER_LITERAL); } -TEST_F(FlexTokenizerTest, Lookahead2NoEnoughTokens) { +TEST_F(LookaheadTransformerTest, Lookahead2NoEnoughTokens) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -302,14 +299,14 @@ TEST_F(FlexTokenizerTest, Lookahead2NoEnoughTokens) { EXPECT_EQ(TokenTestThief::Lookahead1(tokenizer), Token::YYEOF); } -TEST_F(FlexTokenizerTest, Lookahead2ForceTerminate) { +TEST_F(LookaheadTransformerTest, Lookahead2ForceTerminate) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -329,14 +326,14 @@ TEST_F(FlexTokenizerTest, Lookahead2ForceTerminate) { EXPECT_EQ(TokenTestThief::Lookahead2(tokenizer), Token::YYEOF); } -TEST_F(FlexTokenizerTest, Lookahead2ForceTerminateLookahead2First) { +TEST_F(LookaheadTransformerTest, Lookahead2ForceTerminateLookahead2First) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -356,15 +353,15 @@ TEST_F(FlexTokenizerTest, Lookahead2ForceTerminateLookahead2First) { EXPECT_EQ(TokenTestThief::Lookahead1(tokenizer), Token::YYEOF); } -TEST_F(FlexTokenizerTest, DisambiguatorReturnsYyeofWhenErrors) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT * EXCEPT 1", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, LookaheadTransformerReturnsYyeofWhenErrors) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "SELECT * EXCEPT 1", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -384,7 +381,7 @@ TEST_F(FlexTokenizerTest, DisambiguatorReturnsYyeofWhenErrors) { EXPECT_EQ(token_kind, Token::YYEOF); } -static void AdvanceLexer(DisambiguatorLexer& tokenizer, Location& location) { +static void AdvanceLexer(LookaheadTransformer& tokenizer, Location& location) { absl::string_view unused; tokenizer.GetNextToken(&unused, &location); } @@ -401,15 +398,15 @@ MATCHER_P(TokenIs, expected_kind, "") { result_listener); } -TEST_F(FlexTokenizerTest, DisambiguatorHasCorrectPrevToken) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT 1 FULL UNION ALL", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, LookaheadTransformerHasCorrectPrevToken) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "SELECT 1 FULL UNION ALL", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; AdvanceLexer(tokenizer, location); EXPECT_THAT(TokenTestThief::GetCurrentToken(tokenizer), @@ -429,15 +426,16 @@ TEST_F(FlexTokenizerTest, DisambiguatorHasCorrectPrevToken) { TokenIs(Token::KW_FULL_IN_SET_OP)); } -TEST_F(FlexTokenizerTest, DisambiguatorHasCorrectPrevTokenAndError) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT * EXCEPT 1", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerHasCorrectPrevTokenAndError) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "SELECT * EXCEPT 1", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; AdvanceLexer(tokenizer, location); EXPECT_THAT(TokenTestThief::GetCurrentToken(tokenizer), @@ -468,15 +466,16 @@ TEST_F(FlexTokenizerTest, DisambiguatorHasCorrectPrevTokenAndError) { constexpr int kFurtherLookaheadBeyondEof = 5; // Keep calling GetNextToken after YYEOF is returned with no errors. -TEST_F(FlexTokenizerTest, DisambiguatorGetNextTokenAfterEofNoError) { +TEST_F(LookaheadTransformerTest, + LookaheadTransformerGetNextTokenAfterEofNoError) { ZETASQL_ASSERT_OK_AND_ASSIGN( auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT *", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", "SELECT *", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -492,15 +491,16 @@ TEST_F(FlexTokenizerTest, DisambiguatorGetNextTokenAfterEofNoError) { } // Keep calling GetNextToken after YYEOF is returned with errors. -TEST_F(FlexTokenizerTest, DisambiguatorGetNextTokenAfterEofWithError) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT * EXCEPT 1", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerGetNextTokenAfterEofWithError) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "SELECT * EXCEPT 1", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -523,16 +523,16 @@ TEST_F(FlexTokenizerTest, DisambiguatorGetNextTokenAfterEofWithError) { } // SetForceTerminate is called without fetching any tokens. -TEST_F(FlexTokenizerTest, - DisambiguatorGetNextTokenAfterSetForceTerminateNoTokens) { +TEST_F(LookaheadTransformerTest, + LookaheadTransformerGetNextTokenAfterSetForceTerminateNoTokens) { ZETASQL_ASSERT_OK_AND_ASSIGN( auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT *", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", "SELECT *", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; tokenizer.SetForceTerminate(/*end_byte_offset=*/nullptr); // Keep calling GetNextToken should always return YYEOF with no errors. @@ -542,16 +542,16 @@ TEST_F(FlexTokenizerTest, } // Last returned token has no errors and then SetForceTerminate is called. -TEST_F(FlexTokenizerTest, - DisambiguatorGetNextTokenAfterSetForceTerminateNoError) { +TEST_F(LookaheadTransformerTest, + LookaheadTransformerGetNextTokenAfterSetForceTerminateNoError) { ZETASQL_ASSERT_OK_AND_ASSIGN( auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT *", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", "SELECT *", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -564,16 +564,16 @@ TEST_F(FlexTokenizerTest, } // Last returned token reports an error and then SetForceTerminate is called. -TEST_F(FlexTokenizerTest, - DisambiguatorGetNextTokenAfterSetForceTerminateLastTokenErrors) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT * EXCEPT 1", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerGetNextTokenAfterSetForceTerminateLastTokenErrors) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "SELECT * EXCEPT 1", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetNextToken(tokenizer, location), IsOkAndHolds(Token::MODE_STATEMENT)); @@ -597,14 +597,15 @@ TEST_F(FlexTokenizerTest, } } -TEST_F(FlexTokenizerTest, DisambiguatorOverrideLookbackInvalidAnduselessCalls) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kScript, "fake_file", - "BEGIN BEGIN END END", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerOverrideLookbackInvalidAnduselessCalls) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kScript, "fake_file", + "BEGIN BEGIN END END", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(tokenizer.OverrideNextTokenLookback( /*parser_lookahead_is_empty=*/true, Token::KW_BEGIN, @@ -650,15 +651,16 @@ TEST_F(FlexTokenizerTest, DisambiguatorOverrideLookbackInvalidAnduselessCalls) { EXPECT_THAT(TokenTestThief::Lookback2(tokenizer), Eq(Token::KW_END)); } -TEST_F(FlexTokenizerTest, DisambiguatorOverrideLookbackForEmptyParserLA) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kScript, "fake_file", - "BEGIN BEGIN END END", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerOverrideLookbackForEmptyParserLA) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kScript, "fake_file", + "BEGIN BEGIN END END", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(TokenTestThief::Lookback1(tokenizer), Eq(kNoToken)); EXPECT_THAT(GetNextToken(tokenizer, location), @@ -694,15 +696,16 @@ TEST_F(FlexTokenizerTest, DisambiguatorOverrideLookbackForEmptyParserLA) { EXPECT_THAT(TokenTestThief::Lookback2(tokenizer), Eq(Token::KW_END)); } -TEST_F(FlexTokenizerTest, DisambiguatorOverrideLookbackForNonEmptyParserLA) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kScript, "fake_file", - "BEGIN BEGIN END END", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerOverrideLookbackForNonEmptyParserLA) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kScript, "fake_file", + "BEGIN BEGIN END END", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(TokenTestThief::Lookback1(tokenizer), Eq(kNoToken)); EXPECT_THAT(GetNextToken(tokenizer, location), @@ -727,12 +730,13 @@ TEST_F(FlexTokenizerTest, DisambiguatorOverrideLookbackForNonEmptyParserLA) { EXPECT_THAT(TokenTestThief::Lookback1(tokenizer), Eq(Token::KW_END)); } -TEST_F(FlexTokenizerTest, DisambiguatorIdentifyingStartOfExplainExplian) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "EXPLAIN EXPLAIN SELECT 1", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, + LookaheadTransformerIdentifyingStartOfExplainExplian) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "EXPLAIN EXPLAIN SELECT 1", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; EXPECT_THAT(GetNextToken(*lexer, location), @@ -761,9 +765,10 @@ TEST_F(FlexTokenizerTest, DisambiguatorIdentifyingStartOfExplainExplian) { })); } -TEST_F(FlexTokenizerTest, DisambiguatorIdentifyingStartOfHintExplainHint) { +TEST_F(LookaheadTransformerTest, + LookaheadTransformerIdentifyingStartOfHintExplainHint) { ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, - DisambiguatorLexer::Create( + LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "@5 EXPLAIN @{a = 1} EXPLAIN SELECT 1", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); @@ -836,7 +841,7 @@ TEST_F(FlexTokenizerTest, DisambiguatorIdentifyingStartOfHintExplainHint) { } static TokenWithOverrideError GetTokenKindAndError( - DisambiguatorLexer& tokenizer) { + LookaheadTransformer& tokenizer) { absl::string_view unused_text; Location unused_location; TokenKind token_kind = tokenizer.GetNextToken(&unused_text, &unused_location); @@ -855,15 +860,15 @@ MATCHER_P(TokenKindIs, expected_kind, "") { arg, result_listener); } -TEST_F(FlexTokenizerTest, SetForceTerminateBeforeFetchingTokens) { - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - "SELECT * EXCEPT 1", 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); +TEST_F(LookaheadTransformerTest, SetForceTerminateBeforeFetchingTokens) { + ZETASQL_ASSERT_OK_AND_ASSIGN(auto lexer, + LookaheadTransformer::Create( + BisonParserMode::kStatement, "fake_file", + "SELECT * EXCEPT 1", 0, options_, + /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; int end_byte_offset; tokenizer.SetForceTerminate(&end_byte_offset); @@ -879,14 +884,14 @@ TEST_F(FlexTokenizerTest, SetForceTerminateBeforeFetchingTokens) { // The difference between this test case and // SetForceTerminateBeforeFetchingTokens is that the lookahead1 of this test // case is YYEOF. -TEST_F(FlexTokenizerTest, SetForceTerminateNoTokens) { +TEST_F(LookaheadTransformerTest, SetForceTerminateNoTokens) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; int end_byte_offset; tokenizer.SetForceTerminate(&end_byte_offset); @@ -899,15 +904,15 @@ TEST_F(FlexTokenizerTest, SetForceTerminateNoTokens) { } } -TEST_F(FlexTokenizerTest, SetForceTerminateLastTokenErrors) { +TEST_F(LookaheadTransformerTest, SetForceTerminateLastTokenErrors) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", "`", - 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", "`", 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -934,16 +939,16 @@ TEST_F(FlexTokenizerTest, SetForceTerminateLastTokenErrors) { } } -TEST_F(FlexTokenizerTest, SetForceTerminateLastTokenIsYyeof) { +TEST_F(LookaheadTransformerTest, SetForceTerminateLastTokenIsYyeof) { constexpr absl::string_view kInput = " "; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -963,16 +968,16 @@ TEST_F(FlexTokenizerTest, SetForceTerminateLastTokenIsYyeof) { } } -TEST_F(FlexTokenizerTest, SetForceTerminateLookahead1IsYyeof) { +TEST_F(LookaheadTransformerTest, SetForceTerminateLookahead1IsYyeof) { constexpr absl::string_view kInput = "SELECT "; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -992,16 +997,16 @@ TEST_F(FlexTokenizerTest, SetForceTerminateLookahead1IsYyeof) { } } -TEST_F(FlexTokenizerTest, SetForceTerminateLookahead1Errors) { +TEST_F(LookaheadTransformerTest, SetForceTerminateLookahead1Errors) { constexpr absl::string_view kInput = "SELECT $"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1021,16 +1026,16 @@ TEST_F(FlexTokenizerTest, SetForceTerminateLookahead1Errors) { } } -TEST_F(FlexTokenizerTest, SetForceTerminateLookahead1IsNonYyeof) { +TEST_F(LookaheadTransformerTest, SetForceTerminateLookahead1IsNonYyeof) { constexpr absl::string_view kInput = "SELECT 1"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1050,15 +1055,15 @@ TEST_F(FlexTokenizerTest, SetForceTerminateLookahead1IsNonYyeof) { } } -TEST_F(FlexTokenizerTest, GetNextTokenContinuesToReturnYyeof) { +TEST_F(LookaheadTransformerTest, GetNextTokenContinuesToReturnYyeof) { constexpr absl::string_view kInput = "SELECT"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1071,17 +1076,17 @@ TEST_F(FlexTokenizerTest, GetNextTokenContinuesToReturnYyeof) { } } -TEST_F(FlexTokenizerTest, GetNextTokenContinuesToReturnTheSameError) { +TEST_F(LookaheadTransformerTest, GetNextTokenContinuesToReturnTheSameError) { constexpr absl::string_view kInput = "SELECT `"; constexpr absl::string_view kError = "Syntax error: Unclosed identifier literal"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1122,14 +1127,14 @@ MATCHER_P(IsSameOptionalToken, token, "") { arg->token, result_listener); } -TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectNoErrors) { +TEST_F(LookaheadTransformerTest, PreviousTokensAreCorrectNoErrors) { constexpr absl::string_view kInput = "SELECT 1 *"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); + LookaheadTransformer& tokenizer = *lexer; std::optional previous_current_token; do { @@ -1154,16 +1159,16 @@ TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectNoErrors) { } } -TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectWithErrors) { +TEST_F(LookaheadTransformerTest, PreviousTokensAreCorrectWithErrors) { constexpr absl::string_view kInput = "SELECT `"; constexpr absl::string_view kError = "Syntax error: Unclosed identifier literal"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create(BisonParserMode::kStatement, - "fake_file", kInput, 0, options_, - /*macro_catalog=*/nullptr, - /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); + LookaheadTransformer& tokenizer = *lexer; std::optional previous_current_token; do { @@ -1179,8 +1184,8 @@ TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectWithErrors) { EXPECT_THAT(TokenTestThief::GetPreviousToken(tokenizer), IsSameOptionalToken(previous_current_token)); - // The previous token of the disambiguator should remain YYEOF with the same - // error. + // The previous token of the lookahead_transformer should remain YYEOF with + // the same error. Location unused_location; for (int i = 0; i < kFurtherLookaheadBeyondEof; ++i) { AdvanceLexer(tokenizer, unused_location); @@ -1191,14 +1196,15 @@ TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectWithErrors) { } } -TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectWithSetForceTerminate) { +TEST_F(LookaheadTransformerTest, + PreviousTokensAreCorrectWithSetForceTerminate) { constexpr absl::string_view kInput = "SELECT 1 *"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); + LookaheadTransformer& tokenizer = *lexer; Location unused_location; AdvanceLexer(tokenizer, unused_location); @@ -1233,14 +1239,14 @@ TEST_F(FlexTokenizerTest, PreviousTokensAreCorrectWithSetForceTerminate) { TokenIs(Token::YYEOF)); } -TEST_F(FlexTokenizerTest, TokenFusion) { +TEST_F(LookaheadTransformerTest, TokenFusion) { constexpr absl::string_view kInput = ">>"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1249,14 +1255,14 @@ TEST_F(FlexTokenizerTest, TokenFusion) { EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::YYEOF)); } -TEST_F(FlexTokenizerTest, TokensWithWhitespacesInBetweenCannotFuse) { +TEST_F(LookaheadTransformerTest, TokensWithWhitespacesInBetweenCannotFuse) { constexpr absl::string_view kInput = "> >"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, - DisambiguatorLexer::Create(BisonParserMode::kStatement, "fake_file", - kInput, 0, options_, - /*macro_catalog=*/nullptr, /*arena=*/nullptr)); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + /*macro_catalog=*/nullptr, + /*arena=*/nullptr)); + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1290,7 +1296,7 @@ static absl::Status RegisterMacros(absl::string_view source, return absl::OkStatus(); } -TEST_F(FlexTokenizerTest, TokensFromDifferentFilesCannotFuse) { +TEST_F(LookaheadTransformerTest, TokensFromDifferentFilesCannotFuse) { options_.EnableLanguageFeature(FEATURE_V_1_4_SQL_MACROS); macros::MacroCatalog macro_catalog; ZETASQL_ASSERT_OK( @@ -1299,10 +1305,10 @@ TEST_F(FlexTokenizerTest, TokensFromDifferentFilesCannotFuse) { constexpr absl::string_view kInput = ">$greater_than"; ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create(BisonParserMode::kStatement, - "fake_file", kInput, 0, options_, - ¯o_catalog, arena.get())); - DisambiguatorLexer& tokenizer = *lexer; + auto lexer, LookaheadTransformer::Create(BisonParserMode::kStatement, + "fake_file", kInput, 0, options_, + ¯o_catalog, arena.get())); + LookaheadTransformer& tokenizer = *lexer; EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::MODE_STATEMENT)); @@ -1311,7 +1317,7 @@ TEST_F(FlexTokenizerTest, TokensFromDifferentFilesCannotFuse) { EXPECT_THAT(GetTokenKindAndError(tokenizer), TokenKindIs(Token::YYEOF)); } -TEST_F(FlexTokenizerTest, RightShiftIsAllowedAfterParentheses) { +TEST_F(LookaheadTransformerTest, RightShiftIsAllowedAfterParentheses) { EXPECT_THAT( GetAllTokens(BisonParserMode::kStatement, "ARRAY> 2)>"), ElementsAreArray(std::vector{ @@ -1329,7 +1335,7 @@ TEST_F(FlexTokenizerTest, RightShiftIsAllowedAfterParentheses) { })); } -TEST_F(FlexTokenizerTest, RightShiftIsAllowedAfterUnpairedParentheses) { +TEST_F(LookaheadTransformerTest, RightShiftIsAllowedAfterUnpairedParentheses) { EXPECT_THAT(GetAllTokens(BisonParserMode::kStatement, "ARRAY<)>>>>>"), ElementsAreArray(std::vector{ Token::MODE_STATEMENT, @@ -1343,13 +1349,13 @@ TEST_F(FlexTokenizerTest, RightShiftIsAllowedAfterUnpairedParentheses) { })); } -TEST_F(FlexTokenizerTest, Lookahead3) { +TEST_F(LookaheadTransformerTest, Lookahead3) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; // Ok to examine lookaheads before fetching any tokens. EXPECT_EQ(TokenTestThief::Lookahead3(tokenizer), @@ -1368,13 +1374,13 @@ TEST_F(FlexTokenizerTest, Lookahead3) { } } -TEST_F(FlexTokenizerTest, Lookahead3SetForceTerminate) { +TEST_F(LookaheadTransformerTest, Lookahead3SetForceTerminate) { ZETASQL_ASSERT_OK_AND_ASSIGN( - auto lexer, DisambiguatorLexer::Create( + auto lexer, LookaheadTransformer::Create( BisonParserMode::kStatement, "fake_file", "a 1 SELECT", 0, options_, /*macro_catalog=*/nullptr, /*arena=*/nullptr)); Location location; - DisambiguatorLexer& tokenizer = *lexer; + LookaheadTransformer& tokenizer = *lexer; // Ok to examine lookaheads before fetching any tokens. EXPECT_EQ(TokenTestThief::Lookahead3(tokenizer), diff --git a/zetasql/parser/macros/BUILD b/zetasql/parser/macros/BUILD index 7fa989c42..a74742125 100644 --- a/zetasql/parser/macros/BUILD +++ b/zetasql/parser/macros/BUILD @@ -18,6 +18,7 @@ cc_library( srcs = ["quoting.cc"], hdrs = ["quoting.h"], deps = [ + "//zetasql/base:check", "//zetasql/base:ret_check", "//zetasql/base:status", "@com_google_absl//absl/status:statusor", @@ -109,7 +110,7 @@ cc_test( "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/parser:bison_token_codes", - "//zetasql/parser:token_disambiguator", # build_cleaner: keep + "//zetasql/parser:lookahead_transformer", # build_cleaner: keep "//zetasql/public:parse_location", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", @@ -161,6 +162,7 @@ cc_library( "//zetasql/public:language_options", "//zetasql/public:options_cc_proto", "//zetasql/public:parse_location", + "@com_google_absl//absl/container:btree", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", @@ -185,8 +187,7 @@ cc_test( "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/parser", "//zetasql/parser:bison_token_codes", - "//zetasql/parser:token_disambiguator", # build_cleaner: keep - "//zetasql/public:error_helpers", + "//zetasql/parser:lookahead_transformer", # build_cleaner: keep "//zetasql/public:language_options", "//zetasql/public:options_cc_proto", "//zetasql/public:parse_location", diff --git a/zetasql/parser/macros/flex_token_provider.cc b/zetasql/parser/macros/flex_token_provider.cc index 19435e50e..8056d2ffd 100644 --- a/zetasql/parser/macros/flex_token_provider.cc +++ b/zetasql/parser/macros/flex_token_provider.cc @@ -43,22 +43,19 @@ static absl::string_view GetTextBetween(absl::string_view input, size_t start, } FlexTokenProvider::FlexTokenProvider(absl::string_view filename, - absl::string_view input, - bool preserve_comments, int start_offset, + absl::string_view input, int start_offset, std::optional end_offset) : TokenProviderBase(filename, input, start_offset, end_offset), tokenizer_(std::make_unique( - filename, input.substr(0, this->end_offset()), preserve_comments, - start_offset)), - preserve_comments_(preserve_comments), + filename, input.substr(0, this->end_offset()), start_offset)), location_(ParseLocationPoint::FromByteOffset(filename, -1), ParseLocationPoint::FromByteOffset(filename, -1)) {} std::unique_ptr FlexTokenProvider::CreateNewInstance( absl::string_view filename, absl::string_view input, int start_offset, std::optional end_offset) const { - return std::make_unique( - filename, input, preserve_comments_, start_offset, end_offset); + return std::make_unique(filename, input, start_offset, + end_offset); } absl::StatusOr FlexTokenProvider::ConsumeNextTokenImpl() { diff --git a/zetasql/parser/macros/flex_token_provider.h b/zetasql/parser/macros/flex_token_provider.h index 8ed2fbe39..80501a416 100644 --- a/zetasql/parser/macros/flex_token_provider.h +++ b/zetasql/parser/macros/flex_token_provider.h @@ -39,8 +39,7 @@ namespace macros { class FlexTokenProvider : public TokenProviderBase { public: FlexTokenProvider(absl::string_view filename, absl::string_view input, - bool preserve_comments, int start_offset, - std::optional end_offset); + int start_offset, std::optional end_offset); FlexTokenProvider(const FlexTokenProvider&) = delete; FlexTokenProvider& operator=(const FlexTokenProvider&) = delete; @@ -71,10 +70,6 @@ class FlexTokenProvider : public TokenProviderBase { // The ZetaSQL tokenizer which gives us all the tokens. std::unique_ptr tokenizer_; - // Whether the tokenizer should preserve comments. This is passed to the - // tokenizer and is only cached here for CreateNewInstance(). - bool preserve_comments_; - // Used as a buffer when we need a lookahead from the tokenizer. // Any tokens here are still unprocessed by the expander. std::queue input_token_buffer_; diff --git a/zetasql/parser/macros/flex_token_provider_test.cc b/zetasql/parser/macros/flex_token_provider_test.cc index fb4685942..f9428e2c3 100644 --- a/zetasql/parser/macros/flex_token_provider_test.cc +++ b/zetasql/parser/macros/flex_token_provider_test.cc @@ -65,45 +65,30 @@ static ParseLocationRange MakeLocation(int start_offset, int end_offset) { return location; } -static FlexTokenProvider MakeTokenProvider(absl::string_view input, - bool preserve_comments) { - return FlexTokenProvider(kFileName, input, preserve_comments, - /*start_offset=*/0, /*end_offset=*/std::nullopt); -} - static FlexTokenProvider MakeTokenProvider(absl::string_view input) { - return MakeTokenProvider(input, /*preserve_comments=*/false); + return FlexTokenProvider(kFileName, input, + /*start_offset=*/0, /*end_offset=*/std::nullopt); } TEST(FlexTokenProviderTest, RawTokenizerMode) { absl::string_view input = "/*comment*/ 123"; - EXPECT_THAT( - MakeTokenProvider(input, /*preserve_comments=*/false).ConsumeNextToken(), - IsOkAndHoldsToken(TokenWithLocation{ - .kind = DECIMAL_INTEGER_LITERAL, - .location = MakeLocation(12, 15), - .text = "123", - .preceding_whitespaces = "/*comment*/ ", - })); -} + FlexTokenProvider token_provider = MakeTokenProvider(input); + EXPECT_THAT(token_provider.ConsumeNextToken(), + IsOkAndHoldsToken(TokenWithLocation{ + .kind = COMMENT, + .location = MakeLocation(0, 11), + .text = "/*comment*/", + .preceding_whitespaces = "", + })); -TEST(FlexTokenProviderTest, RawTokenizerPreserveCommentsMode) { - absl::string_view input = "/*comment*/ 123"; - FlexTokenProvider provider = - MakeTokenProvider(input, /*preserve_comments=*/true); - EXPECT_THAT(provider.ConsumeNextToken(), IsOkAndHoldsToken(TokenWithLocation{ - .kind = COMMENT, - .location = MakeLocation(0, 11), - .text = "/*comment*/", - .preceding_whitespaces = "", - })); - EXPECT_THAT(provider.ConsumeNextToken(), IsOkAndHoldsToken(TokenWithLocation{ - .kind = DECIMAL_INTEGER_LITERAL, - .location = MakeLocation(12, 15), - .text = "123", - .preceding_whitespaces = " ", - })); + EXPECT_THAT(token_provider.ConsumeNextToken(), + IsOkAndHoldsToken(TokenWithLocation{ + .kind = DECIMAL_INTEGER_LITERAL, + .location = MakeLocation(12, 15), + .text = "123", + .preceding_whitespaces = " ", + })); } TEST(FlexTokenProviderTest, AlwaysEndsWithEOF) { diff --git a/zetasql/parser/macros/macro_expander.cc b/zetasql/parser/macros/macro_expander.cc index 3f169285e..1f0242a8f 100644 --- a/zetasql/parser/macros/macro_expander.cc +++ b/zetasql/parser/macros/macro_expander.cc @@ -43,6 +43,7 @@ #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_location.h" +#include "absl/container/btree_map.h" #include "zetasql/base/check.h" #include "absl/memory/memory.h" #include "absl/status/status.h" @@ -187,10 +188,12 @@ absl::StatusOr MacroExpander::ExpandMacros( WarningCollector warning_collector(diagnostic_options.max_warning_count); ZETASQL_RETURN_IF_ERROR(ExpandMacrosInternal( std::move(token_provider), language_options, macro_catalog, - expansion_output.arena.get(), /*call_arguments=*/{}, diagnostic_options, + expansion_output.arena.get(), + /*call_arguments=*/{}, diagnostic_options, /*parent_location=*/nullptr, &expansion_output.location_map, expansion_output.expanded_tokens, warning_collector, - /*out_max_arg_ref_index=*/nullptr)); + // Only the top-level comments should be preserved. + /*out_max_arg_ref_index=*/nullptr, /*drop_comments=*/false)); expansion_output.warnings = warning_collector.ReleaseWarnings(); return expansion_output; } @@ -333,6 +336,16 @@ absl::Status MacroExpander::LoadPotentiallySplicingTokens() { ZETASQL_RET_CHECK(splicing_buffer_.empty()); ZETASQL_RET_CHECK(output_token_buffer_.empty()); + // Skip the leading comment tokens, if any. + while (true) { + ZETASQL_ASSIGN_OR_RETURN(TokenWithLocation token, token_provider_->PeekNextToken()); + if (token.kind != COMMENT) { + break; + } + ZETASQL_ASSIGN_OR_RETURN(token, token_provider_->ConsumeNextToken()); + splicing_buffer_.push(token); + } + // Special logic to find top-level DEFINE MACRO statement, which do not get // expanded. if (at_statement_start_ && call_arguments_.empty()) { @@ -757,7 +770,8 @@ absl::Status MacroExpander::ParseAndExpandArgs( arg_start_offset, arg_end_offset), language_options_, macro_catalog_, arena_, call_arguments_, diagnostic_options_, parent_location_, location_map_, expanded_arg, - warning_collector_, &max_arg_ref_index_in_current_arg)); + warning_collector_, &max_arg_ref_index_in_current_arg, + /*drop_comments=*/true)); max_arg_ref_index_ = std::max(max_arg_ref_index_, max_arg_ref_index_in_current_arg); @@ -902,7 +916,8 @@ absl::Status MacroExpander::ExpandMacroInvocation( std::move(child_token_provider), language_options_, macro_catalog_, arena_, std::move(expanded_args), child_diagnsotic_options, &stack_frame, location_map_, expanded_tokens, warning_collector_, - &max_arg_ref_in_definition)); + &max_arg_ref_in_definition, + /*drop_comments=*/true)); if (has_explicit_unexpanded_arg && num_explicit_args > max_arg_ref_in_definition) { ZETASQL_RETURN_IF_ERROR(RaiseErrorOrAddWarning(MakeSqlErrorAt( @@ -1062,7 +1077,7 @@ absl::StatusOr MacroExpander::ExpandLiteral( std::move(child_token_provider), language_options_, macro_catalog_, arena_, call_arguments_, diagnostic_options_, parent_location_, location_map_, expanded_tokens, warning_collector_, - /*out_max_arg_ref_index=*/nullptr)); + /*out_max_arg_ref_index=*/nullptr, /*drop_comments=*/true)); ZETASQL_RET_CHECK(!expanded_tokens.empty()) << "A proper expansion should have at least the YYEOF token at " @@ -1139,16 +1154,23 @@ absl::Status MacroExpander::ExpandMacrosInternal( DiagnosticOptions diagnostic_options, StackFrame* parent_location, absl::btree_map* location_map, std::vector& output_token_list, - WarningCollector& warning_collector, int* out_max_arg_ref_index) { + WarningCollector& warning_collector, int* out_max_arg_ref_index, + bool drop_comments) { auto expander = absl::WrapUnique(new MacroExpander( std::move(token_provider), language_options, macro_catalog, arena, call_arguments, diagnostic_options, &warning_collector, parent_location)); expander->location_map_ = location_map; - do { + while (true) { ZETASQL_ASSIGN_OR_RETURN(TokenWithLocation token, expander->GetNextToken()); + if (drop_comments && token.kind == COMMENT) { + // We only preserve top level comments. + continue; + } output_token_list.push_back(std::move(token)); - } while (output_token_list.back().kind != YYEOF); - + if (output_token_list.back().kind == YYEOF) { + break; + } + } if (out_max_arg_ref_index != nullptr) { *out_max_arg_ref_index = expander->max_arg_ref_index_; } diff --git a/zetasql/parser/macros/macro_expander.h b/zetasql/parser/macros/macro_expander.h index 333180093..df5a6ea7b 100644 --- a/zetasql/parser/macros/macro_expander.h +++ b/zetasql/parser/macros/macro_expander.h @@ -31,6 +31,7 @@ #include "zetasql/public/error_location.pb.h" #include "zetasql/public/language_options.h" #include "zetasql/public/parse_location.h" +#include "absl/container/btree_map.h" #include "zetasql/base/check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -158,7 +159,8 @@ class MacroExpander final : public MacroExpanderBase { DiagnosticOptions diagnostic_options, StackFrame* parent_location, absl::btree_map* location_map, std::vector& output_token_list, - WarningCollector& warning_collector, int* out_max_arg_ref_index); + WarningCollector& warning_collector, int* out_max_arg_ref_index, + bool drop_comments); class TokenBuffer { public: diff --git a/zetasql/parser/macros/macro_expander_test.cc b/zetasql/parser/macros/macro_expander_test.cc index e836c956a..c0ae6a2c6 100644 --- a/zetasql/parser/macros/macro_expander_test.cc +++ b/zetasql/parser/macros/macro_expander_test.cc @@ -37,7 +37,6 @@ #include "zetasql/parser/macros/token_with_location.h" #include "zetasql/parser/parse_tree.h" #include "zetasql/parser/parser.h" -#include "zetasql/public/error_helpers.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_location.h" @@ -183,11 +182,11 @@ static absl::StatusOr ExpandMacros( DiagnosticOptions diagnostic_options = { .error_message_options = { .mode = ErrorMessageMode::ERROR_MESSAGE_ONE_LINE}}) { - return MacroExpander::ExpandMacros( - std::make_unique( - kTopFileName, text, /*preserve_comments=*/false, /*start_offset=*/0, - /*end_offset=*/std::nullopt), - language_options, macro_catalog, diagnostic_options); + return MacroExpander::ExpandMacros(std::make_unique( + kTopFileName, text, /*start_offset=*/0, + /*end_offset=*/std::nullopt), + language_options, macro_catalog, + diagnostic_options); } // This function needs to return void due to the assertions. @@ -261,7 +260,6 @@ TEST(MacroExpanderTest, ErrorsCanPrintLocation_EmptyFileNames) { EXPECT_THAT(MacroExpander::ExpandMacros( std::make_unique( "", "$m2()", - /*preserve_comments=*/false, /*start_offset=*/0, /*end_offset=*/std::nullopt), options, macro_catalog, {{.mode = ErrorMessageMode::ERROR_MESSAGE_ONE_LINE}}), @@ -321,7 +319,7 @@ TEST(MacroExpanderTest, TracksCountOfUnexpandedTokensConsumedIncludingEOF) { LanguageOptions options = GetLanguageOptions(/*is_strict=*/false); auto token_provider = std::make_unique( - kTopFileName, "\t$empty\r\n$empty$empty", /*preserve_comments=*/false, + kTopFileName, "\t$empty\r\n$empty$empty", /*start_offset=*/0, /*end_offset=*/std::nullopt); auto arena = std::make_unique(/*block_size=*/1024); MacroExpander expander(std::move(token_provider), options, macro_catalog, @@ -342,7 +340,7 @@ TEST(MacroExpanderTest, LanguageOptions options = GetLanguageOptions(/*is_strict=*/false); auto token_provider = std::make_unique( - kTopFileName, "$m", /*preserve_comments=*/false, /*start_offset=*/0, + kTopFileName, "$m", /*start_offset=*/0, /*end_offset=*/std::nullopt); auto arena = std::make_unique(/*block_size=*/1024); MacroExpander expander(std::move(token_provider), options, macro_catalog, @@ -1879,6 +1877,49 @@ TEST_P(MacroExpanderParameterizedTest, RightShift) { EXPECT_EQ(TokensToString(output.expanded_tokens), ">>"); } +TEST_P(MacroExpanderParameterizedTest, TopLevelCommentsArePreserved) { + MacroCatalog macro_catalog; + RegisterMacros("DEFINE MACRO m /* dropped_comment */ 1;", macro_catalog); + ZETASQL_ASSERT_OK_AND_ASSIGN( + ExpansionOutput output, + ExpandMacros("/* preserved_comment */ $m()", macro_catalog, + GetLanguageOptions(GetParam()))); + EXPECT_THAT(output, + TokensEq(std::vector{ + {COMMENT, MakeLocation(0, 23), "/* preserved_comment */", ""}, + {DECIMAL_INTEGER_LITERAL, MakeLocation(kDefsFileName, 37, 38), + "1", " ", MakeLocation(24, 28)}, + {YYEOF, MakeLocation(28, 28), "", ""}, + })); +} + +TEST_P(MacroExpanderParameterizedTest, + TopLevelCommentsArePreservedExpandingDefineMacroStatements) { + MacroCatalog macro_catalog; + RegisterMacros("DEFINE MACRO m1 /*internal comment*/ 123;", macro_catalog); + ZETASQL_ASSERT_OK_AND_ASSIGN( + ExpansionOutput output, + ExpandMacros( + "select 1; /*comment 1*/ /*another comment*/ DEFINE MACRO m2 " + "/*comment 2*/ $m1", + macro_catalog, GetLanguageOptions(GetParam()))); + + EXPECT_THAT(output, + TokensEq(std::vector{ + {KW_SELECT, MakeLocation(0, 6), "select", ""}, + {DECIMAL_INTEGER_LITERAL, MakeLocation(7, 8), "1", " "}, + {';', MakeLocation(8, 9), ";", ""}, + {COMMENT, MakeLocation(10, 23), "/*comment 1*/", " "}, + {COMMENT, MakeLocation(24, 43), "/*another comment*/", " "}, + {KW_DEFINE_FOR_MACROS, MakeLocation(44, 50), "DEFINE", " "}, + {KW_MACRO, MakeLocation(51, 56), "MACRO", " "}, + {IDENTIFIER, MakeLocation(57, 59), "m2", " "}, + {COMMENT, MakeLocation(60, 73), "/*comment 2*/", " "}, + {MACRO_INVOCATION, MakeLocation(74, 77), "$m1", " "}, + {YYEOF, MakeLocation(77, 77), "", ""}, + })); +} + INSTANTIATE_TEST_SUITE_P(MacroExpanderParameterizedTest, MacroExpanderParameterizedTest, Bool()); diff --git a/zetasql/parser/macros/quoting.h b/zetasql/parser/macros/quoting.h index 5e6dcc667..0573434fe 100644 --- a/zetasql/parser/macros/quoting.h +++ b/zetasql/parser/macros/quoting.h @@ -20,6 +20,7 @@ #include #include +#include "zetasql/base/check.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "zetasql/base/ret_check.h" diff --git a/zetasql/parser/macros/standalone_macro_expansion_impl.h b/zetasql/parser/macros/standalone_macro_expansion_impl.h index e2846e955..b72665842 100644 --- a/zetasql/parser/macros/standalone_macro_expansion_impl.h +++ b/zetasql/parser/macros/standalone_macro_expansion_impl.h @@ -36,9 +36,8 @@ inline absl::StatusOr ExpandMacros( const MacroCatalog& catalog, const LanguageOptions& language_options, DiagnosticOptions diagnostic_options) { return MacroExpander::ExpandMacros( - std::make_unique( - filename, input, /*preserve_comments=*/false, /*start_offset=*/0, - /*end_offset=*/std::nullopt), + std::make_unique(filename, input, /*start_offset=*/0, + /*end_offset=*/std::nullopt), language_options, catalog, diagnostic_options); } diff --git a/zetasql/parser/parse_tree.cc b/zetasql/parser/parse_tree.cc index 88047852d..93b0010db 100644 --- a/zetasql/parser/parse_tree.cc +++ b/zetasql/parser/parse_tree.cc @@ -325,6 +325,19 @@ std::string ASTSelectAs::SingleNodeDebugString() const { } } +std::string ASTGroupBy::SingleNodeDebugString() const { + return absl::StrCat(ASTNode::SingleNodeDebugString(), + and_order_by() ? "(and_order_by=true)" : ""); +} + +std::string ASTGroupingItemOrder::SingleNodeDebugString() const { + // There is no UNSPECIFIED case in this node. + return absl::StrCat(ASTNode::SingleNodeDebugString(), + ordering_spec_ == ASTOrderingExpression::UNSPECIFIED + ? "(UNSPECIFIED)" + : (descending() ? "(DESC)" : "(ASC)")); +} + std::string ASTAlias::GetAsString() const { return identifier()->GetAsString(); } @@ -1616,6 +1629,8 @@ absl::string_view SchemaObjectKindToName(SchemaObjectKind schema_object_kind) { return "AGGREGATE FUNCTION"; case SchemaObjectKind::kApproxView: return "APPROX VIEW"; + case SchemaObjectKind::kConnection: + return "CONNECTION"; case SchemaObjectKind::kConstant: return "CONSTANT"; case SchemaObjectKind::kDatabase: diff --git a/zetasql/parser/parse_tree_test.cc b/zetasql/parser/parse_tree_test.cc index a569b7647..827d8434e 100644 --- a/zetasql/parser/parse_tree_test.cc +++ b/zetasql/parser/parse_tree_test.cc @@ -28,20 +28,20 @@ #include "zetasql/parser/parse_tree_visitor.h" #include "zetasql/parser/parser.h" #include "zetasql/proto/internal_error_location.pb.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/public/error_location.pb.h" #include "zetasql/public/parse_location.h" #include "zetasql/testdata/test_schema.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "zetasql/base/check.h" +#include "absl/log/log.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" -#include "zetasql/base/source_location.h" -#include "zetasql/base/status.h" #include "zetasql/base/status_builder.h" -#include "zetasql/base/status_payload.h" namespace zetasql { namespace testing { @@ -169,11 +169,11 @@ TEST(ParseTreeTest, NodeKindCategories_IfStatement) { const std::string sql = "if true then select 5; end if"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK( - ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/ERROR_MESSAGE_WITH_PAYLOAD == - ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = true, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); auto statement_list = parser_output->script()->statement_list(); ASSERT_EQ(statement_list.size(), 1); const ASTStatement* statement = statement_list[0]; @@ -192,11 +192,11 @@ TEST(ParseTreeTest, NodeKindCategories_DdlStatement_IsCreateStatement) { const std::string sql = "create table t as select 1 x"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK( - ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/ERROR_MESSAGE_WITH_PAYLOAD == - ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = true, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); auto statement_list = parser_output->script()->statement_list(); ASSERT_EQ(statement_list.size(), 1); const ASTDdlStatement* statement = @@ -211,11 +211,13 @@ TEST(ParseTreeTest, NodeKindCategories_DdlStatement_IsAlterStatement) { const std::string sql = "alter table t set options()"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK( - ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/ERROR_MESSAGE_WITH_PAYLOAD == - ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = + (ERROR_MESSAGE_WITH_PAYLOAD == + ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD), + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); auto statement_list = parser_output->script()->statement_list(); ASSERT_EQ(statement_list.size(), 1); const ASTDdlStatement* statement = @@ -230,11 +232,11 @@ TEST(ParseTreeTest, NodeKindCategories_LoopStatement) { const std::string sql = "LOOP END LOOP;"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK( - ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/ERROR_MESSAGE_WITH_PAYLOAD == - ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = true, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); ASSERT_EQ(parser_output->script()->statement_list().size(), 1); const ASTStatement* statement = parser_output->script()->statement_list()[0]; @@ -252,11 +254,11 @@ TEST(ParseTreeTest, NodeKindCategories_WhileStatement) { const std::string sql = "WHILE TRUE DO END WHILE;"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK( - ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/ERROR_MESSAGE_WITH_PAYLOAD == - ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = true, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); ASSERT_EQ(parser_output->script()->statement_list().size(), 1); const ASTStatement* statement = parser_output->script()->statement_list()[0]; diff --git a/zetasql/parser/parser.cc b/zetasql/parser/parser.cc index 952ba87b9..3406f4066 100644 --- a/zetasql/parser/parser.cc +++ b/zetasql/parser/parser.cc @@ -142,8 +142,7 @@ absl::Status ParseStatement(absl::string_view statement_string, absl::Status ParseScript(absl::string_view script_string, const ParserOptions& parser_options_in, - ErrorMessageMode error_message_mode, - bool keep_error_location_payload, + ErrorMessageOptions error_message_options, std::unique_ptr* output) { ParserOptions parser_options = parser_options_in; parser_options.CreateDefaultArenasIfNotSet(); @@ -165,11 +164,7 @@ absl::Status ParseScript(absl::string_view script_string, script = absl::WrapUnique(ast_node.release()->GetAsOrDie()); } ZETASQL_RETURN_IF_ERROR(ConvertInternalErrorLocationAndAdjustErrorString( - ErrorMessageOptions{ - .mode = error_message_mode, - .attach_error_location_payload = keep_error_location_payload, - .stability = GetDefaultErrorMessageStability()}, - script_string, status)); + error_message_options, script_string, status)); *output = std::make_unique( parser_options.id_string_pool(), parser_options.arena(), std::move(other_allocated_ast_nodes), std::move(script), diff --git a/zetasql/parser/parser.h b/zetasql/parser/parser.h index fe4ee199b..fd61c5ce6 100644 --- a/zetasql/parser/parser.h +++ b/zetasql/parser/parser.h @@ -24,11 +24,13 @@ #include #include "zetasql/base/arena.h" +#include "zetasql/common/errors.h" #include "zetasql/parser/ast_node_kind.h" #include "zetasql/parser/macros/macro_catalog.h" #include "zetasql/parser/parse_tree.h" #include "zetasql/parser/parser_runtime_info.h" #include "zetasql/parser/statement_properties.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" #include "absl/base/attributes.h" @@ -224,23 +226,36 @@ absl::Status ParseStatement(absl::string_view statement_string, // // A terminating semicolon is optional for the last statement in the script, // and mandatory for all other statements. -// -// describes how errors should be represented in the -// returned Status - whether as a payload, or as part of the string. absl::Status ParseScript(absl::string_view script_string, const ParserOptions& parser_options_in, - ErrorMessageMode error_message_mode, - bool keep_error_location_payload, + ErrorMessageOptions error_message_options, std::unique_ptr* output); +ABSL_DEPRECATED("Inline me!") +inline absl::Status ParseScript(absl::string_view script_string, + const ParserOptions& parser_options_in, + ErrorMessageMode error_message_mode, + bool keep_error_location_payload, + std::unique_ptr* output) { + return ParseScript( + script_string, parser_options_in, + {.mode = error_message_mode, + .attach_error_location_payload = keep_error_location_payload, + .stability = GetDefaultErrorMessageStability()}, + output); +} + ABSL_DEPRECATED("Inline me!") inline absl::Status ParseScript(absl::string_view script_string, const ParserOptions& parser_options_in, ErrorMessageMode error_message_mode, std::unique_ptr* output) { - return ParseScript(script_string, parser_options_in, error_message_mode, - /*keep_error_location_payload=*/ - error_message_mode == ERROR_MESSAGE_WITH_PAYLOAD, output); + return ParseScript(script_string, parser_options_in, + {.mode = error_message_mode, + .attach_error_location_payload = + (error_message_mode == ERROR_MESSAGE_WITH_PAYLOAD), + .stability = GetDefaultErrorMessageStability()}, + output); } // Parses one statement from a string that may contain multiple statements. diff --git a/zetasql/parser/parser_internal.h b/zetasql/parser/parser_internal.h index 070b96caf..d386b881b 100644 --- a/zetasql/parser/parser_internal.h +++ b/zetasql/parser/parser_internal.h @@ -24,6 +24,7 @@ #include #include "zetasql/common/errors.h" +#include "zetasql/parser/ast_node_kind.h" #include "zetasql/parser/bison_parser.h" #include "zetasql/parser/bison_parser_mode.h" #include "zetasql/parser/parse_tree.h" @@ -31,10 +32,12 @@ #include "zetasql/public/strings.h" #include "absl/base/optimization.h" #include "zetasql/base/check.h" +#include "absl/log/log.h" #include "absl/status/status.h" #include "absl/strings/match.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" // Shorthand to call parser->CreateASTNode<>(). The "node_type" must be a // AST... class from the zetasql namespace. The "..." are the arguments to @@ -76,6 +79,29 @@ parser->GetInputText(right), "\"")); \ } +// Define the handling of our custom ParseLocationRange. +// If there are empty productions on the RHS, skip them when including the range +// If all symbols on the RHS are empty, return the range of the first symbol. +#define YYLLOC_DEFAULT(Cur, Rhs, N) \ + do { \ + if ((N) == 0) { \ + (Cur).set_start(YYRHSLOC(Rhs, 0).end()); \ + (Cur).set_end(YYRHSLOC(Rhs, 0).end()); \ + } else { \ + /* YYRHSLOC must be called with the range [1, N] */ \ + int i = 1; \ + /* i < N so that we don't run off the end of stack. If all empty we */ \ + /* grab the last symbol's location (index N) */ \ + while (i < (N) && YYRHSLOC(Rhs, i).IsEmpty()) { \ + i++; \ + } \ + (Cur).set_start(YYRHSLOC(Rhs, i).start()); \ + /* If any of the RHS is empty, the end location is inherited from */ \ + /* the top of the stack: they cling to the previous symbol. */ \ + (Cur).set_end(YYRHSLOC(Rhs, (N)).end()); \ + } \ + } while (0) + // Used like a ZETASQL_RET_CHECK inside the parser. #define ABORT_CHECK(location, condition, msg) \ do { \ @@ -110,7 +136,7 @@ ZETASQL_DCHECK_OK(s); // Overrides the lookback token kind to be `new_token_kind` for the most -// recently returned token by the disambiguator. `location` is the error +// recently returned token by the lookahead transformer. `location` is the error // location to report when the override fails. #define OVERRIDE_CURRENT_TOKEN_LOOKBACK(location, new_token_kind) \ ABORT_CHECK(location, PARSER_LA_IS_EMPTY(), \ @@ -126,22 +152,24 @@ namespace zetasql { // Forward declarations to avoid an interface and a v-table lookup on every // token. namespace parser { -class DisambiguatorLexer; +class LookaheadTransformer; } // namespace parser namespace parser_internal { // Forward declarations of wrappers so that the generated parser can call the -// disambiguator without an interface and a v-table lookup on every token. +// lookahead transformer without an interface and a v-table lookup on every +// token. using zetasql::parser::BisonParserMode; -using zetasql::parser::DisambiguatorLexer; +using zetasql::parser::LookaheadTransformer; -void SetForceTerminate(DisambiguatorLexer*, int*); -void PushBisonParserMode(DisambiguatorLexer*, BisonParserMode); -void PopBisonParserMode(DisambiguatorLexer*); -int GetNextToken(DisambiguatorLexer*, absl::string_view*, ParseLocationRange*); -absl::Status OverrideNextTokenLookback(DisambiguatorLexer*, bool, int, int); -absl::Status OverrideCurrentTokenLookback(DisambiguatorLexer*, int); +void SetForceTerminate(LookaheadTransformer*, int*); +void PushBisonParserMode(LookaheadTransformer*, BisonParserMode); +void PopBisonParserMode(LookaheadTransformer*); +int GetNextToken(LookaheadTransformer*, absl::string_view*, + ParseLocationRange*); +absl::Status OverrideNextTokenLookback(LookaheadTransformer*, bool, int, int); +absl::Status OverrideCurrentTokenLookback(LookaheadTransformer*, int); enum class NotKeywordPresence { kPresent, kAbsent }; @@ -274,7 +302,7 @@ class SeparatedIdentifierTmpNode final : public zetasql::ASTNode { template inline int zetasql_bison_parserlex(SemanticType* yylval, ParseLocationRange* yylloc, - DisambiguatorLexer* tokenizer) { + LookaheadTransformer* tokenizer) { ABSL_DCHECK(tokenizer != nullptr); absl::string_view text; int token = GetNextToken(tokenizer, &text, yylloc); @@ -353,9 +381,9 @@ inline absl::Status IsIdentifierOrKeyword(absl::string_view text) { ABSL_DCHECK(!text.empty()); if (text.front() == '`') { // This is a quoted identifier. No other token coming from the lexer & - // disambiguator starts with a backtick, not even error recovery ones (i.e. - // tokens defined to capture some wrong pattern and provide a better context - // or error message). + // lookahead transformer starts with a backtick, not even error recovery + // ones (i.e. tokens defined to capture some wrong pattern and provide a + // better context or error message). std::string str; std::string error_string; int error_offset; @@ -379,6 +407,24 @@ inline absl::Status IsIdentifierOrKeyword(absl::string_view text) { return absl::OkStatus(); } +template +inline zetasql::ASTRowPatternExpression* MakeOrCombineRowPatternOperation( + const zetasql::ASTRowPatternOperation::OperationType op, + zetasql::parser::BisonParser* parser, const Location& location, + zetasql::ASTRowPatternExpression* left, + zetasql::ASTRowPatternExpression* right) { + if (left->node_kind() == zetasql::AST_ROW_PATTERN_OPERATION && + left->GetAsOrDie()->op_type() == op && + !left->parenthesized()) { + // Embrace and extend left's ASTNode to flatten a series of `op`. + return parser->WithEndLocation(WithExtraChildren(left, {right}), location); + } else { + auto* new_root = MAKE_NODE(ASTRowPatternOperation, location, {left, right}); + new_root->set_op_type(op); + return new_root; + } +} + using zetasql::ASTInsertStatement; } // namespace parser_internal diff --git a/zetasql/parser/parser_macro_expansion_test.cc b/zetasql/parser/parser_macro_expansion_test.cc index 3df301600..05d552080 100644 --- a/zetasql/parser/parser_macro_expansion_test.cc +++ b/zetasql/parser/parser_macro_expansion_test.cc @@ -267,4 +267,76 @@ TEST(ParserMacroExpansionTest, CorrectErrorWhenMacroNameIsMissingAtSemicolon) { HasSubstr("Syntax error: Expected macro name but got \";\""))); } +TEST(ParserMacroExpansionTest, TopLevelCommentsArePreserved) { + MacroCatalog macro_catalog; + RegisterMacros("DEFINE MACRO m /* dropped_comment */ 1;", macro_catalog); + ParserOptions parser_options(GetLanguageOptions(), ¯o_catalog); + ParseResumeLocation resume_location = ParseResumeLocation::FromStringView( + "/* preserved_comment */ SELECT $m()"); + bool at_end_of_input; + std::unique_ptr parser_output; + ZETASQL_ASSERT_OK(ParseNextStatement(&resume_location, parser_options, &parser_output, + &at_end_of_input)); + EXPECT_TRUE(at_end_of_input); + EXPECT_EQ(parser_output->statement()->DebugString(), + R"(QueryStatement [24-35] + Query [24-35] + Select [24-35] + SelectList [31-35] + SelectColumn [31-35] + IntLiteral($m()) [31-35] +)"); +} + +TEST(ParserMacroExpansionTest, + TopLevelCommentsArePreservedExpandingDefineMacroStatements) { + MacroCatalog macro_catalog; + RegisterMacros("DEFINE MACRO m1 /*internal comment*/ 123;", macro_catalog); + ParserOptions parser_options(GetLanguageOptions(), ¯o_catalog); + ParseResumeLocation resume_location = ParseResumeLocation::FromStringView( + "select 1; /*comment 1*/ DEFINE MACRO m2 /*comment 2*/ $m1; SELECT " + "$m1()"); + bool at_end_of_input; + std::unique_ptr parser_output; + + // "select 1;" does not have comments. + ZETASQL_ASSERT_OK(ParseNextStatement(&resume_location, parser_options, &parser_output, + &at_end_of_input)); + EXPECT_FALSE(at_end_of_input); + EXPECT_EQ(parser_output->statement()->DebugString(), + R"(QueryStatement [0-8] + Query [0-8] + Select [0-8] + SelectList [7-8] + SelectColumn [7-8] + IntLiteral(1) [7-8] +)"); + + // "/*comment 1*/ DEFINE MACRO m2 /*comment 2*/ $m1;" has both the comments + // preserved, and "$m1" is not expanded. + ZETASQL_ASSERT_OK(ParseNextStatement(&resume_location, parser_options, &parser_output, + &at_end_of_input)); + EXPECT_FALSE(at_end_of_input); + EXPECT_EQ(parser_output->statement()->DebugString(), + R"(DefineMacroStatement [24-57] + Identifier(m2) [37-39] + MacroBody($m1) [54-57] +)"); + + // The last statement "SELECT $m1()" has "$m1" expanded, and the comment + // "/*internal comment*/" is not dropped, i.e. the statement that the parser + // sees is: SELECT 123 + ZETASQL_ASSERT_OK(ParseNextStatement(&resume_location, parser_options, &parser_output, + &at_end_of_input)); + EXPECT_TRUE(at_end_of_input); + EXPECT_EQ(parser_output->statement()->DebugString(), + R"(QueryStatement [59-71] + Query [59-71] + Select [59-71] + SelectList [66-71] + SelectColumn [66-71] + IntLiteral($m1()) [66-71] +)"); +} + } // namespace zetasql diff --git a/zetasql/parser/run_parser_test.cc b/zetasql/parser/run_parser_test.cc index d29193aec..ad1db0ca8 100644 --- a/zetasql/parser/run_parser_test.cc +++ b/zetasql/parser/run_parser_test.cc @@ -102,6 +102,7 @@ class RunParserTest : public ::testing::Test { "supported_generic_sub_entity_types"; // Indicates that QUALIFY is a reserved keyword. const std::string kQualifyReserved = "qualify_reserved"; + const std::string kReserveMatchRecognize = "reserve_match_recognize"; // Show the text of the SQL fragment for each parse location, rather than only // the integer range. const std::string kShowParseLocationText = "show_parse_location_text"; @@ -115,6 +116,7 @@ class RunParserTest : public ::testing::Test { test_case_options_.RegisterBool(kTestGetParseTokens, true); test_case_options_.RegisterBool(kTestUnparse, true); test_case_options_.RegisterBool(kQualifyReserved, true); + test_case_options_.RegisterBool(kReserveMatchRecognize, true); test_case_options_.RegisterString(kSupportedGenericEntityTypes, ""); test_case_options_.RegisterString(kSupportedGenericSubEntityTypes, ""); test_case_options_.RegisterBool(kShowParseLocationText, true); @@ -602,6 +604,9 @@ class RunParserTest : public ::testing::Test { if (test_case_options_.GetBool(kQualifyReserved)) { ZETASQL_EXPECT_OK(language_options_->EnableReservableKeyword("QUALIFY")); } + if (test_case_options_.GetBool(kReserveMatchRecognize)) { + ZETASQL_EXPECT_OK(language_options_->EnableReservableKeyword("MATCH_RECOGNIZE")); + } std::string entity_types_config = test_case_options_.GetString(kSupportedGenericEntityTypes); @@ -616,7 +621,8 @@ class RunParserTest : public ::testing::Test { language_options_->SetSupportedGenericSubEntityTypes(sub_entity_types); return ParserOptions(/*id_string_pool=*/nullptr, /*arena=*/nullptr, - language_options_.get()); + // ParserOptions wants a copy of LanguageOptions. + *language_options_); } void CheckExtractedStatementProperties( diff --git a/zetasql/parser/testdata/aggregation.test b/zetasql/parser/testdata/aggregation.test index 3427a863a..f06031e1e 100644 --- a/zetasql/parser/testdata/aggregation.test +++ b/zetasql/parser/testdata/aggregation.test @@ -2160,16 +2160,6 @@ FROM T == -# GROUP BY doesn't support aliases. -select COUNT(*) -from t -GROUP BY x AS y --- -ERROR: Syntax error: GROUP BY does not support aliases [at 3:12] -GROUP BY x AS y - ^ -== - # GROUP BY aliases without AS. select COUNT(*) from t diff --git a/zetasql/parser/testdata/alter_set_options.test b/zetasql/parser/testdata/alter_set_options.test index 745069cdd..6ce6d7081 100644 --- a/zetasql/parser/testdata/alter_set_options.test +++ b/zetasql/parser/testdata/alter_set_options.test @@ -2,7 +2,13 @@ # verified as correct. [default no_show_parse_location_text] -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo set OPTIONS +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo set OPTIONS +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Expected "(" but got end of statement [at 1:33] +ALTER CONNECTION foo set OPTIONS + ^ -- ALTERNATION GROUP: DATABASE -- @@ -54,7 +60,13 @@ ALTER APPROX VIEW foo set OPTIONS ^ == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo set timestamp; +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo set timestamp; +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Expected keyword AS or keyword DEFAULT or keyword ON or keyword OPTIONS but got keyword TIMESTAMP [at 1:26] +ALTER CONNECTION foo set timestamp; + ^ -- ALTERNATION GROUP: DATABASE -- @@ -106,7 +118,13 @@ ALTER APPROX VIEW foo set timestamp; ^ == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo drop; +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo drop; +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Unexpected ";" [at 1:26] +ALTER CONNECTION foo drop; + ^ -- ALTERNATION GROUP: DATABASE -- @@ -157,7 +175,7 @@ ALTER APPROX VIEW foo drop; ^ == -ALTER {{AGGREGATE FUNCTION|CONSTANT|DATABASE|EXTERNAL TABLE|FUNCTION|INDEX|MATERIALIZED VIEW|MODEL|PROCEDURE|SCHEMA|EXTERNAL SCHEMA|TABLE FUNCTION|APPROX VIEW}}; +ALTER {{AGGREGATE FUNCTION|CONNECTION|CONSTANT|DATABASE|EXTERNAL TABLE|FUNCTION|INDEX|MATERIALIZED VIEW|MODEL|PROCEDURE|SCHEMA|EXTERNAL SCHEMA|TABLE FUNCTION|APPROX VIEW}}; -- ALTERNATION GROUP: AGGREGATE FUNCTION -- @@ -165,6 +183,12 @@ ERROR: Syntax error: Unexpected ";" [at 1:25] ALTER AGGREGATE FUNCTION; ^ -- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Unexpected ";" [at 1:17] +ALTER CONNECTION; + ^ +-- ALTERNATION GROUP: CONSTANT -- ERROR: Syntax error: Unexpected ";" [at 1:15] @@ -245,7 +269,7 @@ ALTER ; ^ == -ALTER {{Aggregate FUNCTION|CONSTANT|DATABASE|EXTERNAL TABLE|FUNCTION|INDEX|MATERIALIZED VIEW|MODEL|SCHEMA|EXTERNAL SCHEMA|TABLE|TABLE FUNCTION|APPROX VIEW}} adsdw.foo +ALTER {{Aggregate FUNCTION|CONNECTION|CONSTANT|DATABASE|EXTERNAL TABLE|FUNCTION|INDEX|MATERIALIZED VIEW|MODEL|SCHEMA|EXTERNAL SCHEMA|TABLE|TABLE FUNCTION|APPROX VIEW}} adsdw.foo SET OPTIONS (); -- ALTERNATION GROUP: Aggregate FUNCTION @@ -254,6 +278,18 @@ ERROR: ALTER AGGREGATE FUNCTION is not supported [at 1:7] ALTER Aggregate FUNCTION adsdw.foo ^ -- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-41] + PathExpression [17-26] + Identifier(adsdw) [17-22] + Identifier(foo) [23-26] + AlterActionList [27-41] + SetOptionsAction [27-41] + OptionsList [39-41] +-- +ALTER CONNECTION adsdw.foo SET OPTIONS() +-- ALTERNATION GROUP: CONSTANT -- ERROR: ALTER CONSTANT is not supported [at 1:7] @@ -369,7 +405,23 @@ AlterApproxViewStatement [0-42] ALTER APPROX VIEW adsdw.foo SET OPTIONS() == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW}} adsdw.foo SET OPTIONS (), SET OPTIONS (), SET OPTIONS (); +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW}} adsdw.foo SET OPTIONS (), SET OPTIONS (), SET OPTIONS (); +-- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-73] + PathExpression [17-26] + Identifier(adsdw) [17-22] + Identifier(foo) [23-26] + AlterActionList [27-73] + SetOptionsAction [27-41] + OptionsList [39-41] + SetOptionsAction [43-57] + OptionsList [55-57] + SetOptionsAction [59-73] + OptionsList [71-73] +-- +ALTER CONNECTION adsdw.foo SET OPTIONS(), SET OPTIONS(), SET OPTIONS() -- ALTERNATION GROUP: DATABASE -- @@ -472,7 +524,19 @@ AlterMaterializedViewStatement [0-80] ALTER MATERIALIZED VIEW adsdw.foo SET OPTIONS(), SET OPTIONS(), SET OPTIONS() == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|APPROX VIEW}} IF EXISTS adsdw.foo SET OPTIONS (); +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|APPROX VIEW}} IF EXISTS adsdw.foo SET OPTIONS (); +-- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement(is_if_exists) [0-51] + PathExpression [27-36] + Identifier(adsdw) [27-32] + Identifier(foo) [33-36] + AlterActionList [37-51] + SetOptionsAction [37-51] + OptionsList [49-51] +-- +ALTER CONNECTION IF EXISTS adsdw.foo SET OPTIONS() -- ALTERNATION GROUP: DATABASE -- @@ -559,9 +623,25 @@ AlterApproxViewStatement(is_if_exists) [0-52] ALTER APPROX VIEW IF EXISTS adsdw.foo SET OPTIONS() == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} adsdw.foo SET OPTIONS ( +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} adsdw.foo SET OPTIONS ( quota_accounting_owner='adsdw-etl@google.com'); -- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-89] + PathExpression [17-26] + Identifier(adsdw) [17-22] + Identifier(foo) [23-26] + AlterActionList [27-89] + SetOptionsAction [27-89] + OptionsList [39-89] + OptionsEntry [43-88] + Identifier(quota_accounting_owner) [43-65] + StringLiteral [66-88] + StringLiteralComponent('adsdw-etl@google.com') [66-88] +-- +ALTER CONNECTION adsdw.foo SET OPTIONS(quota_accounting_owner = 'adsdw-etl@google.com') +-- ALTERNATION GROUP: DATABASE -- AlterDatabaseStatement [0-87] @@ -696,7 +776,22 @@ AlterApproxViewStatement [0-90] ALTER APPROX VIEW adsdw.foo SET OPTIONS(quota_accounting_owner = 'adsdw-etl@google.com') == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} adsdw.foo SET OPTIONS (ttl_seconds=3600); +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} adsdw.foo SET OPTIONS (ttl_seconds=3600); +-- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-57] + PathExpression [17-26] + Identifier(adsdw) [17-22] + Identifier(foo) [23-26] + AlterActionList [27-57] + SetOptionsAction [27-57] + OptionsList [39-57] + OptionsEntry [40-56] + Identifier(ttl_seconds) [40-51] + IntLiteral(3600) [52-56] +-- +ALTER CONNECTION adsdw.foo SET OPTIONS(ttl_seconds = 3600) -- ALTERNATION GROUP: DATABASE -- @@ -819,7 +914,22 @@ AlterApproxViewStatement [0-58] ALTER APPROX VIEW adsdw.foo SET OPTIONS(ttl_seconds = 3600) == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} adsdw.foo SET OPTIONS (ttl_seconds=NULL); +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} adsdw.foo SET OPTIONS (ttl_seconds=NULL); +-- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-57] + PathExpression [17-26] + Identifier(adsdw) [17-22] + Identifier(foo) [23-26] + AlterActionList [27-57] + SetOptionsAction [27-57] + OptionsList [39-57] + OptionsEntry [40-56] + Identifier(ttl_seconds) [40-51] + NullLiteral(NULL) [52-56] +-- +ALTER CONNECTION adsdw.foo SET OPTIONS(ttl_seconds = NULL) -- ALTERNATION GROUP: DATABASE -- @@ -942,9 +1052,27 @@ AlterApproxViewStatement [0-58] ALTER APPROX VIEW adsdw.foo SET OPTIONS(ttl_seconds = NULL) == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo SET OPTIONS ( +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL|APPROX VIEW}} foo SET OPTIONS ( quota_accounting_owner = 'adsdw-etl@google.com', ttl_seconds=3600); -- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-103] + PathExpression [17-20] + Identifier(foo) [17-20] + AlterActionList [21-103] + SetOptionsAction [21-103] + OptionsList [33-103] + OptionsEntry [37-84] + Identifier(quota_accounting_owner) [37-59] + StringLiteral [62-84] + StringLiteralComponent('adsdw-etl@google.com') [62-84] + OptionsEntry [86-102] + Identifier(ttl_seconds) [86-97] + IntLiteral(3600) [98-102] +-- +ALTER CONNECTION foo SET OPTIONS(quota_accounting_owner = 'adsdw-etl@google.com', ttl_seconds = 3600) +-- ALTERNATION GROUP: DATABASE -- AlterDatabaseStatement [0-101] @@ -1092,10 +1220,30 @@ AlterApproxViewStatement [0-104] ALTER APPROX VIEW foo SET OPTIONS(quota_accounting_owner = 'adsdw-etl@google.com', ttl_seconds = 3600) == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW}} foo SET OPTIONS ( +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW}} foo SET OPTIONS ( quota_accounting_owner = 'adsdw-etl@google.com', quota_accounting_owner = 'adsdw-etl@google.com'); -- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-136] + PathExpression [17-20] + Identifier(foo) [17-20] + AlterActionList [21-136] + SetOptionsAction [21-136] + OptionsList [33-136] + OptionsEntry [37-84] + Identifier(quota_accounting_owner) [37-59] + StringLiteral [62-84] + StringLiteralComponent('adsdw-etl@google.com') [62-84] + OptionsEntry [88-135] + Identifier(quota_accounting_owner) [88-110] + StringLiteral [113-135] + StringLiteralComponent('adsdw-etl@google.com') [113-135] +-- +ALTER CONNECTION foo SET OPTIONS(quota_accounting_owner = 'adsdw-etl@google.com', quota_accounting_owner = + 'adsdw-etl@google.com') +-- ALTERNATION GROUP: DATABASE -- AlterDatabaseStatement [0-134] @@ -1217,12 +1365,45 @@ ALTER MATERIALIZED VIEW foo SET OPTIONS(quota_accounting_owner = 'adsdw-etl@goog 'adsdw-etl@google.com') == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo SET OPTIONS (a = 'a', b = 1), SET OPTIONS (c = 'c'), SET OPTIONS (a = 'a', b = 1) ; -- +ALTERNATION GROUP: CONNECTION +-- +AlterConnectionStatement [0-102] + PathExpression [17-20] + Identifier(foo) [17-20] + AlterActionList [21-102] + SetOptionsAction [21-49] + OptionsList [33-49] + OptionsEntry [34-41] + Identifier(a) [34-35] + StringLiteral [38-41] + StringLiteralComponent('a') [38-41] + OptionsEntry [43-48] + Identifier(b) [43-44] + IntLiteral(1) [47-48] + SetOptionsAction [51-72] + OptionsList [63-72] + OptionsEntry [64-71] + Identifier(c) [64-65] + StringLiteral [68-71] + StringLiteralComponent('c') [68-71] + SetOptionsAction [74-102] + OptionsList [86-102] + OptionsEntry [87-94] + Identifier(a) [87-88] + StringLiteral [91-94] + StringLiteralComponent('a') [91-94] + OptionsEntry [96-101] + Identifier(b) [96-97] + IntLiteral(1) [100-101] +-- +ALTER CONNECTION foo SET OPTIONS(a = 'a', b = 1), SET OPTIONS(c = 'c'), SET OPTIONS(a = 'a', b = 1) +-- ALTERNATION GROUP: DATABASE -- AlterDatabaseStatement [0-100] @@ -1457,7 +1638,13 @@ AlterModelStatement [0-97] ALTER MODEL foo SET OPTIONS(a = 'a', b = 1), SET OPTIONS(c = 'c'), SET OPTIONS(a = 'a', b = 1) == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Unexpected end of statement [at 1:17] +ALTER CONNECTION + ^ -- ALTERNATION GROUP: DATABASE -- @@ -1502,7 +1689,13 @@ ALTER MODEL ^ == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo; +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo; +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Unexpected ";" [at 1:21] +ALTER CONNECTION foo; + ^ -- ALTERNATION GROUP: DATABASE -- @@ -1547,7 +1740,13 @@ ALTER MODEL foo; ^ == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo SET OPTIONS (a = 'a', b = 1), +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo SET OPTIONS (a = 'a', b = 1), +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Unexpected end of statement [at 1:51] +ALTER CONNECTION foo SET OPTIONS (a = 'a', b = 1), + ^ -- ALTERNATION GROUP: DATABASE -- @@ -1592,7 +1791,14 @@ ALTER MODEL foo SET OPTIONS (a = 'a', b = 1), ^ == -ALTER {{DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo SET OPTIONS (a = 'a', b = 1), SET +ALTER {{CONNECTION|DATABASE|SCHEMA|EXTERNAL SCHEMA|TABLE|VIEW|MATERIALIZED VIEW|MODEL}} foo SET OPTIONS (a = 'a', b = 1), SET +-- +ALTERNATION GROUP: CONNECTION +-- +ERROR: Syntax error: Expected keyword AS or keyword DEFAULT or keyword ON or keyword OPTIONS but got end of statement [at 1:55] +ALTER CONNECTION foo SET OPTIONS (a = 'a', b = 1), SET + ^ + -- ALTERNATION GROUP: DATABASE -- diff --git a/zetasql/parser/testdata/analytic_functions.test b/zetasql/parser/testdata/analytic_functions.test index 69b727eb3..d679bd262 100644 --- a/zetasql/parser/testdata/analytic_functions.test +++ b/zetasql/parser/testdata/analytic_functions.test @@ -1322,8 +1322,8 @@ order by f() over (), (f() over ())+1 QueryStatement [0-79] Query [0-79] SetOperation(UNION ALL) [0-41] - SetOperationMetadataList [15-25] - SetOperationMetadata [15-25] + SetOperationMetadataList [16-25] + SetOperationMetadata [16-25] SetOperationType [16-21] SetOperationAllOrDistinct [22-25] Select [0-15] diff --git a/zetasql/parser/testdata/corresponding.test b/zetasql/parser/testdata/corresponding.test index 92b48dcbb..989fe347b 100644 --- a/zetasql/parser/testdata/corresponding.test +++ b/zetasql/parser/testdata/corresponding.test @@ -5,8 +5,8 @@ SELECT b FROM B QueryStatement [0-55] [SELECT a FROM...ELECT b FROM B] Query [0-55] [SELECT a FROM...ELECT b FROM B] SetOperation(UNION ALL) [0-55] [SELECT a FROM...ELECT b FROM B] - SetOperationMetadataList [15-39] [UNION ALL CORRESPONDING] - SetOperationMetadata [15-39] [UNION ALL CORRESPONDING] + SetOperationMetadataList [16-39] [UNION ALL CORRESPONDING] + SetOperationMetadata [16-39] [UNION ALL CORRESPONDING] SetOperationType [16-21] [UNION] SetOperationAllOrDistinct [22-25] [ALL] SetOperationColumnMatchMode [26-39] [CORRESPONDING] @@ -49,12 +49,12 @@ SELECT c FROM C QueryStatement [0-95] [SELECT a FROM...ELECT c FROM C] Query [0-95] [SELECT a FROM...ELECT c FROM C] SetOperation(UNION ALL) [0-95] [SELECT a FROM...ELECT c FROM C] - SetOperationMetadataList [15-79] [UNION ALL...CORRESPONDING] - SetOperationMetadata [15-39] [UNION ALL CORRESPONDING] + SetOperationMetadataList [16-79] [UNION ALL...CORRESPONDING] + SetOperationMetadata [16-39] [UNION ALL CORRESPONDING] SetOperationType [16-21] [UNION] SetOperationAllOrDistinct [22-25] [ALL] SetOperationColumnMatchMode [26-39] [CORRESPONDING] - SetOperationMetadata [55-79] [UNION ALL CORRESPONDING] + SetOperationMetadata [56-79] [UNION ALL CORRESPONDING] SetOperationType [56-61] [UNION] SetOperationAllOrDistinct [62-65] [ALL] SetOperationColumnMatchMode [66-79] [CORRESPONDING] @@ -113,14 +113,14 @@ SELECT NULL FROM T2 QueryStatement [0-82] [(SELECT abc...NULL FROM T2] Query [0-82] [(SELECT abc...NULL FROM T2] SetOperation(UNION ALL) [0-82] [(SELECT abc...NULL FROM T2] - SetOperationMetadataList [52-62] [UNION ALL] - SetOperationMetadata [52-62] [UNION ALL] + SetOperationMetadataList [53-62] [UNION ALL] + SetOperationMetadata [53-62] [UNION ALL] SetOperationType [53-58] [UNION] SetOperationAllOrDistinct [59-62] [ALL] Query [1-51] [SELECT abc...SPONDING SELECT 5] SetOperation(UNION ALL) [1-51] [SELECT abc...SPONDING SELECT 5] - SetOperationMetadataList [18-42] [UNION ALL CORRESPONDING] - SetOperationMetadata [18-42] [UNION ALL CORRESPONDING] + SetOperationMetadataList [19-42] [UNION ALL CORRESPONDING] + SetOperationMetadata [19-42] [UNION ALL CORRESPONDING] SetOperationType [19-24] [UNION] SetOperationAllOrDistinct [25-28] [ALL] SetOperationColumnMatchMode [29-42] [CORRESPONDING] @@ -169,8 +169,8 @@ SELECT 777 QueryStatement [0-61] [SELECT abc...SELECT 777] Query [0-61] [SELECT abc...SELECT 777] SetOperation(UNION ALL) [0-61] [SELECT abc...SELECT 777] - SetOperationMetadataList [17-50] [UNION ALL...ESPONDING BY (abc)] - SetOperationMetadata [17-50] [UNION ALL...ESPONDING BY (abc)] + SetOperationMetadataList [18-50] [UNION ALL...ESPONDING BY (abc)] + SetOperationMetadata [18-50] [UNION ALL...ESPONDING BY (abc)] SetOperationType [18-23] [UNION] SetOperationAllOrDistinct [24-27] [ALL] SetOperationColumnMatchMode [28-44] [CORRESPONDING BY] @@ -207,8 +207,8 @@ SELECT 777 QueryStatement [0-66] [SELECT abc...SELECT 777] Query [0-66] [SELECT abc...SELECT 777] SetOperation(UNION ALL) [0-66] [SELECT abc...SELECT 777] - SetOperationMetadataList [17-55] [UNION ALL...(abc, def)] - SetOperationMetadata [17-55] [UNION ALL...(abc, def)] + SetOperationMetadataList [18-55] [UNION ALL...(abc, def)] + SetOperationMetadata [18-55] [UNION ALL...(abc, def)] SetOperationType [18-23] [UNION] SetOperationAllOrDistinct [24-27] [ALL] SetOperationColumnMatchMode [28-44] [CORRESPONDING BY] @@ -272,8 +272,8 @@ FROM B QueryStatement [0-70] [SELECT * FROM...ELECT * FROM B] Query [0-70] [SELECT * FROM...ELECT * FROM B] SetOperation(EXCEPT ALL) [0-70] [SELECT * FROM...ELECT * FROM B] - SetOperationMetadataList [15-54] [EXCEPT ALL...RESPONDING BY (a)] - SetOperationMetadata [15-54] [EXCEPT ALL...RESPONDING BY (a)] + SetOperationMetadataList [16-54] [EXCEPT ALL...RESPONDING BY (a)] + SetOperationMetadata [16-54] [EXCEPT ALL...RESPONDING BY (a)] SetOperationType [16-22] [EXCEPT] SetOperationAllOrDistinct [23-26] [ALL] SetOperationColumnMatchMode [34-50] [CORRESPONDING BY] @@ -321,15 +321,15 @@ FROM C QueryStatement [0-123] [SELECT * FROM...ELECT * FROM C] Query [0-123] [SELECT * FROM...ELECT * FROM C] SetOperation(UNION ALL) [0-123] [SELECT * FROM...ELECT * FROM C] - SetOperationMetadataList [15-107] [UNION ALL...RRESPONDING BY (a)] - SetOperationMetadata [15-53] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadataList [16-107] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadata [16-53] [UNION ALL...RRESPONDING BY (a)] SetOperationType [16-21] [UNION] SetOperationAllOrDistinct [22-25] [ALL] SetOperationColumnMatchMode [33-49] [CORRESPONDING BY] SetOperationColumnPropagationMode [26-32] [STRICT] ColumnList [50-53] [(a)] Identifier(a) [51-52] [a] - SetOperationMetadata [69-107] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadata [70-107] [UNION ALL...RRESPONDING BY (a)] SetOperationType [70-75] [UNION] SetOperationAllOrDistinct [76-79] [ALL] SetOperationColumnMatchMode [87-103] [CORRESPONDING BY] diff --git a/zetasql/parser/testdata/corresponding_all_combinations.test b/zetasql/parser/testdata/corresponding_all_combinations.test index 282755df3..8ef121892 100644 --- a/zetasql/parser/testdata/corresponding_all_combinations.test +++ b/zetasql/parser/testdata/corresponding_all_combinations.test @@ -8,8 +8,8 @@ ALTERNATION GROUP: UNION,ALL,, QueryStatement [0-30] [SELECT 1 UNION ALL SELECT 2] Query [0-30] [SELECT 1 UNION ALL SELECT 2] SetOperation(UNION ALL) [0-30] [SELECT 1 UNION ALL SELECT 2] - SetOperationMetadataList [8-19] [UNION ALL] - SetOperationMetadata [8-19] [UNION ALL] + SetOperationMetadataList [10-19] [UNION ALL] + SetOperationMetadata [10-19] [UNION ALL] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-19] [ALL] Select [0-8] [SELECT 1] @@ -32,8 +32,8 @@ ALTERNATION GROUP: UNION,ALL,,CORRESPONDING QueryStatement [0-43] [SELECT 1...RESPONDING SELECT 2] Query [0-43] [SELECT 1...RESPONDING SELECT 2] SetOperation(UNION ALL) [0-43] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-34] [UNION ALL CORRESPONDING] - SetOperationMetadata [8-34] [UNION ALL CORRESPONDING] + SetOperationMetadataList [10-34] [UNION ALL CORRESPONDING] + SetOperationMetadata [10-34] [UNION ALL CORRESPONDING] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnMatchMode [21-34] [CORRESPONDING] @@ -57,8 +57,8 @@ ALTERNATION GROUP: UNION,ALL,,CORRESPONDING BY (a) QueryStatement [0-50] [SELECT 1...) SELECT 2] Query [0-50] [SELECT 1...) SELECT 2] SetOperation(UNION ALL) [0-50] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-41] [UNION ALL...RRESPONDING BY (a)] - SetOperationMetadata [8-41] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadataList [10-41] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadata [10-41] [UNION ALL...RRESPONDING BY (a)] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnMatchMode [21-37] [CORRESPONDING BY] @@ -84,8 +84,8 @@ ALTERNATION GROUP: UNION,ALL,STRICT, QueryStatement [0-36] [SELECT 1...SELECT 2] Query [0-36] [SELECT 1...SELECT 2] SetOperation(UNION ALL) [0-36] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-26] [UNION ALL STRICT] - SetOperationMetadata [8-26] [UNION ALL STRICT] + SetOperationMetadataList [10-26] [UNION ALL STRICT] + SetOperationMetadata [10-26] [UNION ALL STRICT] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnPropagationMode [20-26] [STRICT] @@ -109,8 +109,8 @@ ALTERNATION GROUP: UNION,ALL,STRICT,CORRESPONDING QueryStatement [0-49] [SELECT 1...RESPONDING SELECT 2] Query [0-49] [SELECT 1...RESPONDING SELECT 2] SetOperation(UNION ALL) [0-49] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-40] [UNION ALL STRICT CORRESPONDING] - SetOperationMetadata [8-40] [UNION ALL STRICT CORRESPONDING] + SetOperationMetadataList [10-40] [UNION ALL STRICT CORRESPONDING] + SetOperationMetadata [10-40] [UNION ALL STRICT CORRESPONDING] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnMatchMode [27-40] [CORRESPONDING] @@ -135,8 +135,8 @@ ALTERNATION GROUP: UNION,ALL,STRICT,CORRESPONDING BY (a) QueryStatement [0-56] [SELECT 1...) SELECT 2] Query [0-56] [SELECT 1...) SELECT 2] SetOperation(UNION ALL) [0-56] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-47] [UNION ALL...RRESPONDING BY (a)] - SetOperationMetadata [8-47] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadataList [10-47] [UNION ALL...RRESPONDING BY (a)] + SetOperationMetadata [10-47] [UNION ALL...RRESPONDING BY (a)] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnMatchMode [27-43] [CORRESPONDING BY] @@ -163,8 +163,8 @@ ALTERNATION GROUP: UNION,DISTINCT,, QueryStatement [0-35] [SELECT 1...SELECT 2] Query [0-35] [SELECT 1...SELECT 2] SetOperation(UNION DISTINCT) [0-35] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-24] [UNION DISTINCT] - SetOperationMetadata [8-24] [UNION DISTINCT] + SetOperationMetadataList [10-24] [UNION DISTINCT] + SetOperationMetadata [10-24] [UNION DISTINCT] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-24] [DISTINCT] Select [0-8] [SELECT 1] @@ -187,8 +187,8 @@ ALTERNATION GROUP: UNION,DISTINCT,,CORRESPONDING QueryStatement [0-48] [SELECT 1...RESPONDING SELECT 2] Query [0-48] [SELECT 1...RESPONDING SELECT 2] SetOperation(UNION DISTINCT) [0-48] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-39] [UNION DISTINCT CORRESPONDING] - SetOperationMetadata [8-39] [UNION DISTINCT CORRESPONDING] + SetOperationMetadataList [10-39] [UNION DISTINCT CORRESPONDING] + SetOperationMetadata [10-39] [UNION DISTINCT CORRESPONDING] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnMatchMode [26-39] [CORRESPONDING] @@ -212,8 +212,8 @@ ALTERNATION GROUP: UNION,DISTINCT,,CORRESPONDING BY (a) QueryStatement [0-55] [SELECT 1...) SELECT 2] Query [0-55] [SELECT 1...) SELECT 2] SetOperation(UNION DISTINCT) [0-55] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-46] [UNION DISTINCT...ONDING BY (a)] - SetOperationMetadata [8-46] [UNION DISTINCT...ONDING BY (a)] + SetOperationMetadataList [10-46] [UNION DISTINCT...ONDING BY (a)] + SetOperationMetadata [10-46] [UNION DISTINCT...ONDING BY (a)] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnMatchMode [26-42] [CORRESPONDING BY] @@ -239,8 +239,8 @@ ALTERNATION GROUP: UNION,DISTINCT,STRICT, QueryStatement [0-41] [SELECT 1...SELECT 2] Query [0-41] [SELECT 1...SELECT 2] SetOperation(UNION DISTINCT) [0-41] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-31] [UNION DISTINCT STRICT] - SetOperationMetadata [8-31] [UNION DISTINCT STRICT] + SetOperationMetadataList [10-31] [UNION DISTINCT STRICT] + SetOperationMetadata [10-31] [UNION DISTINCT STRICT] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnPropagationMode [25-31] [STRICT] @@ -264,8 +264,8 @@ ALTERNATION GROUP: UNION,DISTINCT,STRICT,CORRESPONDING QueryStatement [0-54] [SELECT 1...RESPONDING SELECT 2] Query [0-54] [SELECT 1...RESPONDING SELECT 2] SetOperation(UNION DISTINCT) [0-54] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-45] [UNION DISTINCT...CORRESPONDING] - SetOperationMetadata [8-45] [UNION DISTINCT...CORRESPONDING] + SetOperationMetadataList [10-45] [UNION DISTINCT...CORRESPONDING] + SetOperationMetadata [10-45] [UNION DISTINCT...CORRESPONDING] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnMatchMode [32-45] [CORRESPONDING] @@ -290,8 +290,8 @@ ALTERNATION GROUP: UNION,DISTINCT,STRICT,CORRESPONDING BY (a) QueryStatement [0-61] [SELECT 1...) SELECT 2] Query [0-61] [SELECT 1...) SELECT 2] SetOperation(UNION DISTINCT) [0-61] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-52] [UNION DISTINCT...ONDING BY (a)] - SetOperationMetadata [8-52] [UNION DISTINCT...ONDING BY (a)] + SetOperationMetadataList [10-52] [UNION DISTINCT...ONDING BY (a)] + SetOperationMetadata [10-52] [UNION DISTINCT...ONDING BY (a)] SetOperationType [10-15] [UNION] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnMatchMode [32-48] [CORRESPONDING BY] @@ -318,8 +318,8 @@ ALTERNATION GROUP: INTERSECT,ALL,, QueryStatement [0-34] [SELECT 1...SELECT 2] Query [0-34] [SELECT 1...SELECT 2] SetOperation(INTERSECT ALL) [0-34] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-23] [INTERSECT ALL] - SetOperationMetadata [8-23] [INTERSECT ALL] + SetOperationMetadataList [10-23] [INTERSECT ALL] + SetOperationMetadata [10-23] [INTERSECT ALL] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-23] [ALL] Select [0-8] [SELECT 1] @@ -342,8 +342,8 @@ ALTERNATION GROUP: INTERSECT,ALL,,CORRESPONDING QueryStatement [0-47] [SELECT 1...RESPONDING SELECT 2] Query [0-47] [SELECT 1...RESPONDING SELECT 2] SetOperation(INTERSECT ALL) [0-47] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-38] [INTERSECT ALL CORRESPONDING] - SetOperationMetadata [8-38] [INTERSECT ALL CORRESPONDING] + SetOperationMetadataList [10-38] [INTERSECT ALL CORRESPONDING] + SetOperationMetadata [10-38] [INTERSECT ALL CORRESPONDING] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-23] [ALL] SetOperationColumnMatchMode [25-38] [CORRESPONDING] @@ -367,8 +367,8 @@ ALTERNATION GROUP: INTERSECT,ALL,,CORRESPONDING BY (a) QueryStatement [0-54] [SELECT 1...) SELECT 2] Query [0-54] [SELECT 1...) SELECT 2] SetOperation(INTERSECT ALL) [0-54] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-45] [INTERSECT...RRESPONDING BY (a)] - SetOperationMetadata [8-45] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadataList [10-45] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadata [10-45] [INTERSECT...RRESPONDING BY (a)] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-23] [ALL] SetOperationColumnMatchMode [25-41] [CORRESPONDING BY] @@ -394,8 +394,8 @@ ALTERNATION GROUP: INTERSECT,ALL,STRICT, QueryStatement [0-40] [SELECT 1...SELECT 2] Query [0-40] [SELECT 1...SELECT 2] SetOperation(INTERSECT ALL) [0-40] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-30] [INTERSECT ALL STRICT] - SetOperationMetadata [8-30] [INTERSECT ALL STRICT] + SetOperationMetadataList [10-30] [INTERSECT ALL STRICT] + SetOperationMetadata [10-30] [INTERSECT ALL STRICT] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-23] [ALL] SetOperationColumnPropagationMode [24-30] [STRICT] @@ -419,8 +419,8 @@ ALTERNATION GROUP: INTERSECT,ALL,STRICT,CORRESPONDING QueryStatement [0-53] [SELECT 1...RESPONDING SELECT 2] Query [0-53] [SELECT 1...RESPONDING SELECT 2] SetOperation(INTERSECT ALL) [0-53] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-44] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-44] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [10-44] [INTERSECT...CORRESPONDING] + SetOperationMetadata [10-44] [INTERSECT...CORRESPONDING] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-23] [ALL] SetOperationColumnMatchMode [31-44] [CORRESPONDING] @@ -445,8 +445,8 @@ ALTERNATION GROUP: INTERSECT,ALL,STRICT,CORRESPONDING BY (a) QueryStatement [0-60] [SELECT 1...) SELECT 2] Query [0-60] [SELECT 1...) SELECT 2] SetOperation(INTERSECT ALL) [0-60] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-51] [INTERSECT...RRESPONDING BY (a)] - SetOperationMetadata [8-51] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadataList [10-51] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadata [10-51] [INTERSECT...RRESPONDING BY (a)] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-23] [ALL] SetOperationColumnMatchMode [31-47] [CORRESPONDING BY] @@ -473,8 +473,8 @@ ALTERNATION GROUP: INTERSECT,DISTINCT,, QueryStatement [0-39] [SELECT 1...SELECT 2] Query [0-39] [SELECT 1...SELECT 2] SetOperation(INTERSECT DISTINCT) [0-39] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-28] [INTERSECT DISTINCT] - SetOperationMetadata [8-28] [INTERSECT DISTINCT] + SetOperationMetadataList [10-28] [INTERSECT DISTINCT] + SetOperationMetadata [10-28] [INTERSECT DISTINCT] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-28] [DISTINCT] Select [0-8] [SELECT 1] @@ -497,8 +497,8 @@ ALTERNATION GROUP: INTERSECT,DISTINCT,,CORRESPONDING QueryStatement [0-52] [SELECT 1...RESPONDING SELECT 2] Query [0-52] [SELECT 1...RESPONDING SELECT 2] SetOperation(INTERSECT DISTINCT) [0-52] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-43] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-43] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [10-43] [INTERSECT...CORRESPONDING] + SetOperationMetadata [10-43] [INTERSECT...CORRESPONDING] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-28] [DISTINCT] SetOperationColumnMatchMode [30-43] [CORRESPONDING] @@ -522,8 +522,8 @@ ALTERNATION GROUP: INTERSECT,DISTINCT,,CORRESPONDING BY (a) QueryStatement [0-59] [SELECT 1...) SELECT 2] Query [0-59] [SELECT 1...) SELECT 2] SetOperation(INTERSECT DISTINCT) [0-59] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-50] [INTERSECT...RRESPONDING BY (a)] - SetOperationMetadata [8-50] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadataList [10-50] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadata [10-50] [INTERSECT...RRESPONDING BY (a)] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-28] [DISTINCT] SetOperationColumnMatchMode [30-46] [CORRESPONDING BY] @@ -549,8 +549,8 @@ ALTERNATION GROUP: INTERSECT,DISTINCT,STRICT, QueryStatement [0-45] [SELECT 1...SELECT 2] Query [0-45] [SELECT 1...SELECT 2] SetOperation(INTERSECT DISTINCT) [0-45] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-35] [INTERSECT DISTINCT STRICT] - SetOperationMetadata [8-35] [INTERSECT DISTINCT STRICT] + SetOperationMetadataList [10-35] [INTERSECT DISTINCT STRICT] + SetOperationMetadata [10-35] [INTERSECT DISTINCT STRICT] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-28] [DISTINCT] SetOperationColumnPropagationMode [29-35] [STRICT] @@ -574,8 +574,8 @@ ALTERNATION GROUP: INTERSECT,DISTINCT,STRICT,CORRESPONDING QueryStatement [0-58] [SELECT 1...RESPONDING SELECT 2] Query [0-58] [SELECT 1...RESPONDING SELECT 2] SetOperation(INTERSECT DISTINCT) [0-58] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-49] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-49] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [10-49] [INTERSECT...CORRESPONDING] + SetOperationMetadata [10-49] [INTERSECT...CORRESPONDING] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-28] [DISTINCT] SetOperationColumnMatchMode [36-49] [CORRESPONDING] @@ -600,8 +600,8 @@ ALTERNATION GROUP: INTERSECT,DISTINCT,STRICT,CORRESPONDING BY (a) QueryStatement [0-65] [SELECT 1...) SELECT 2] Query [0-65] [SELECT 1...) SELECT 2] SetOperation(INTERSECT DISTINCT) [0-65] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-56] [INTERSECT...RRESPONDING BY (a)] - SetOperationMetadata [8-56] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadataList [10-56] [INTERSECT...RRESPONDING BY (a)] + SetOperationMetadata [10-56] [INTERSECT...RRESPONDING BY (a)] SetOperationType [10-19] [INTERSECT] SetOperationAllOrDistinct [20-28] [DISTINCT] SetOperationColumnMatchMode [36-52] [CORRESPONDING BY] @@ -628,8 +628,8 @@ ALTERNATION GROUP: EXCEPT,ALL,, QueryStatement [0-31] [SELECT 1...SELECT 2] Query [0-31] [SELECT 1...SELECT 2] SetOperation(EXCEPT ALL) [0-31] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-20] [EXCEPT ALL] - SetOperationMetadata [8-20] [EXCEPT ALL] + SetOperationMetadataList [10-20] [EXCEPT ALL] + SetOperationMetadata [10-20] [EXCEPT ALL] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-20] [ALL] Select [0-8] [SELECT 1] @@ -652,8 +652,8 @@ ALTERNATION GROUP: EXCEPT,ALL,,CORRESPONDING QueryStatement [0-44] [SELECT 1...RESPONDING SELECT 2] Query [0-44] [SELECT 1...RESPONDING SELECT 2] SetOperation(EXCEPT ALL) [0-44] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-35] [EXCEPT ALL CORRESPONDING] - SetOperationMetadata [8-35] [EXCEPT ALL CORRESPONDING] + SetOperationMetadataList [10-35] [EXCEPT ALL CORRESPONDING] + SetOperationMetadata [10-35] [EXCEPT ALL CORRESPONDING] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-20] [ALL] SetOperationColumnMatchMode [22-35] [CORRESPONDING] @@ -677,8 +677,8 @@ ALTERNATION GROUP: EXCEPT,ALL,,CORRESPONDING BY (a) QueryStatement [0-51] [SELECT 1...) SELECT 2] Query [0-51] [SELECT 1...) SELECT 2] SetOperation(EXCEPT ALL) [0-51] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-42] [EXCEPT ALL...RESPONDING BY (a)] - SetOperationMetadata [8-42] [EXCEPT ALL...RESPONDING BY (a)] + SetOperationMetadataList [10-42] [EXCEPT ALL...RESPONDING BY (a)] + SetOperationMetadata [10-42] [EXCEPT ALL...RESPONDING BY (a)] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-20] [ALL] SetOperationColumnMatchMode [22-38] [CORRESPONDING BY] @@ -704,8 +704,8 @@ ALTERNATION GROUP: EXCEPT,ALL,STRICT, QueryStatement [0-37] [SELECT 1...SELECT 2] Query [0-37] [SELECT 1...SELECT 2] SetOperation(EXCEPT ALL) [0-37] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-27] [EXCEPT ALL STRICT] - SetOperationMetadata [8-27] [EXCEPT ALL STRICT] + SetOperationMetadataList [10-27] [EXCEPT ALL STRICT] + SetOperationMetadata [10-27] [EXCEPT ALL STRICT] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-20] [ALL] SetOperationColumnPropagationMode [21-27] [STRICT] @@ -729,8 +729,8 @@ ALTERNATION GROUP: EXCEPT,ALL,STRICT,CORRESPONDING QueryStatement [0-50] [SELECT 1...RESPONDING SELECT 2] Query [0-50] [SELECT 1...RESPONDING SELECT 2] SetOperation(EXCEPT ALL) [0-50] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-41] [EXCEPT ALL...CORRESPONDING] - SetOperationMetadata [8-41] [EXCEPT ALL...CORRESPONDING] + SetOperationMetadataList [10-41] [EXCEPT ALL...CORRESPONDING] + SetOperationMetadata [10-41] [EXCEPT ALL...CORRESPONDING] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-20] [ALL] SetOperationColumnMatchMode [28-41] [CORRESPONDING] @@ -755,8 +755,8 @@ ALTERNATION GROUP: EXCEPT,ALL,STRICT,CORRESPONDING BY (a) QueryStatement [0-57] [SELECT 1...) SELECT 2] Query [0-57] [SELECT 1...) SELECT 2] SetOperation(EXCEPT ALL) [0-57] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-48] [EXCEPT ALL...RESPONDING BY (a)] - SetOperationMetadata [8-48] [EXCEPT ALL...RESPONDING BY (a)] + SetOperationMetadataList [10-48] [EXCEPT ALL...RESPONDING BY (a)] + SetOperationMetadata [10-48] [EXCEPT ALL...RESPONDING BY (a)] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-20] [ALL] SetOperationColumnMatchMode [28-44] [CORRESPONDING BY] @@ -783,8 +783,8 @@ ALTERNATION GROUP: EXCEPT,DISTINCT,, QueryStatement [0-36] [SELECT 1...SELECT 2] Query [0-36] [SELECT 1...SELECT 2] SetOperation(EXCEPT DISTINCT) [0-36] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-25] [EXCEPT DISTINCT] - SetOperationMetadata [8-25] [EXCEPT DISTINCT] + SetOperationMetadataList [10-25] [EXCEPT DISTINCT] + SetOperationMetadata [10-25] [EXCEPT DISTINCT] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-25] [DISTINCT] Select [0-8] [SELECT 1] @@ -807,8 +807,8 @@ ALTERNATION GROUP: EXCEPT,DISTINCT,,CORRESPONDING QueryStatement [0-49] [SELECT 1...RESPONDING SELECT 2] Query [0-49] [SELECT 1...RESPONDING SELECT 2] SetOperation(EXCEPT DISTINCT) [0-49] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-40] [EXCEPT DISTINCT CORRESPONDING] - SetOperationMetadata [8-40] [EXCEPT DISTINCT CORRESPONDING] + SetOperationMetadataList [10-40] [EXCEPT DISTINCT CORRESPONDING] + SetOperationMetadata [10-40] [EXCEPT DISTINCT CORRESPONDING] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-25] [DISTINCT] SetOperationColumnMatchMode [27-40] [CORRESPONDING] @@ -832,8 +832,8 @@ ALTERNATION GROUP: EXCEPT,DISTINCT,,CORRESPONDING BY (a) QueryStatement [0-56] [SELECT 1...) SELECT 2] Query [0-56] [SELECT 1...) SELECT 2] SetOperation(EXCEPT DISTINCT) [0-56] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-47] [EXCEPT DISTINCT...NDING BY (a)] - SetOperationMetadata [8-47] [EXCEPT DISTINCT...NDING BY (a)] + SetOperationMetadataList [10-47] [EXCEPT DISTINCT...NDING BY (a)] + SetOperationMetadata [10-47] [EXCEPT DISTINCT...NDING BY (a)] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-25] [DISTINCT] SetOperationColumnMatchMode [27-43] [CORRESPONDING BY] @@ -859,8 +859,8 @@ ALTERNATION GROUP: EXCEPT,DISTINCT,STRICT, QueryStatement [0-42] [SELECT 1...SELECT 2] Query [0-42] [SELECT 1...SELECT 2] SetOperation(EXCEPT DISTINCT) [0-42] [SELECT 1...SELECT 2] - SetOperationMetadataList [8-32] [EXCEPT DISTINCT STRICT] - SetOperationMetadata [8-32] [EXCEPT DISTINCT STRICT] + SetOperationMetadataList [10-32] [EXCEPT DISTINCT STRICT] + SetOperationMetadata [10-32] [EXCEPT DISTINCT STRICT] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-25] [DISTINCT] SetOperationColumnPropagationMode [26-32] [STRICT] @@ -884,8 +884,8 @@ ALTERNATION GROUP: EXCEPT,DISTINCT,STRICT,CORRESPONDING QueryStatement [0-55] [SELECT 1...RESPONDING SELECT 2] Query [0-55] [SELECT 1...RESPONDING SELECT 2] SetOperation(EXCEPT DISTINCT) [0-55] [SELECT 1...RESPONDING SELECT 2] - SetOperationMetadataList [8-46] [EXCEPT DISTINCT...ORRESPONDING] - SetOperationMetadata [8-46] [EXCEPT DISTINCT...ORRESPONDING] + SetOperationMetadataList [10-46] [EXCEPT DISTINCT...ORRESPONDING] + SetOperationMetadata [10-46] [EXCEPT DISTINCT...ORRESPONDING] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-25] [DISTINCT] SetOperationColumnMatchMode [33-46] [CORRESPONDING] @@ -910,8 +910,8 @@ ALTERNATION GROUP: EXCEPT,DISTINCT,STRICT,CORRESPONDING BY (a) QueryStatement [0-62] [SELECT 1...) SELECT 2] Query [0-62] [SELECT 1...) SELECT 2] SetOperation(EXCEPT DISTINCT) [0-62] [SELECT 1...) SELECT 2] - SetOperationMetadataList [8-53] [EXCEPT DISTINCT...NDING BY (a)] - SetOperationMetadata [8-53] [EXCEPT DISTINCT...NDING BY (a)] + SetOperationMetadataList [10-53] [EXCEPT DISTINCT...NDING BY (a)] + SetOperationMetadata [10-53] [EXCEPT DISTINCT...NDING BY (a)] SetOperationType [10-16] [EXCEPT] SetOperationAllOrDistinct [17-25] [DISTINCT] SetOperationColumnMatchMode [33-49] [CORRESPONDING BY] diff --git a/zetasql/parser/testdata/create_index.test b/zetasql/parser/testdata/create_index.test index 5f3432c6a..e7a5e3af8 100644 --- a/zetasql/parser/testdata/create_index.test +++ b/zetasql/parser/testdata/create_index.test @@ -1314,3 +1314,371 @@ ALTERNATION GROUP: or replace ERROR: Syntax error: Expected keyword INDEX but got keyword SEARCH [at 1:26] create or replace vector search index on i1 on t1(a); ^ + +== + +# Partition by error, keyword by is missing in the partition by clause. +create vector index i1 on t1(a) partition a options() +-- +ERROR: Syntax error: Expected keyword BY but got identifier "a" [at 1:43] +create vector index i1 on t1(a) partition a options() + ^ + +== + +# Partition by error, hint is unsupported in the partition by clause. +create search index i1 on t1(a) partition @{hint_name=5} by a options() +-- +ERROR: Syntax error: Expected keyword BY but got "@" [at 1:43] +create search index i1 on t1(a) partition @{hint_name=5} by a options() + ^ + +== + +# Partition by error, expression missing in the partition by clause. +create vector index i1 on t1(a) storing (s) partition by +-- +ERROR: Syntax error: Unexpected end of statement [at 1:57] +create vector index i1 on t1(a) storing (s) partition by + ^ + +== + +create search index i1 on t1(a) storing (s) partition by a options() +-- +CreateIndexStatement(SEARCH) [0-68] [create search...options()] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-31] [(a)] + OrderingExpression(ASC) [29-30] [a] + PathExpression [29-30] [a] + Identifier(a) [29-30] [a] + IndexStoringExpressionList [40-43] [(s)] + PathExpression [41-42] [s] + Identifier(s) [41-42] [s] + PartitionBy [44-58] [partition by a] + PathExpression [57-58] [a] + Identifier(a) [57-58] [a] + OptionsList [66-68] [()] +-- +CREATE SEARCH INDEX i1 ON t1(a) +STORING(s) +PARTITION BY a +OPTIONS() + +== + +create vector index i1 on t1(a) partition by p1, p2 options() +-- +CreateIndexStatement(VECTOR) [0-61] [create vector...options()] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-31] [(a)] + OrderingExpression(ASC) [29-30] [a] + PathExpression [29-30] [a] + Identifier(a) [29-30] [a] + PartitionBy [32-51] [partition by p1, p2] + PathExpression [45-47] [p1] + Identifier(p1) [45-47] [p1] + PathExpression [49-51] [p2] + Identifier(p2) [49-51] [p2] + OptionsList [59-61] [()] +-- +CREATE VECTOR INDEX i1 ON t1(a) +PARTITION BY p1, p2 +OPTIONS() + +== + +create search index i1 on t1(a) partition by DATE_TRUNC(_PARTITIONTIME, MONTH) options() +-- +CreateIndexStatement(SEARCH) [0-88] [create search...options()] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-31] [(a)] + OrderingExpression(ASC) [29-30] [a] + PathExpression [29-30] [a] + Identifier(a) [29-30] [a] + PartitionBy [32-78] [partition...TITIONTIME, MONTH)] + FunctionCall [45-78] [DATE_TRUNC...ITIONTIME, MONTH)] + PathExpression [45-55] [DATE_TRUNC] + Identifier(DATE_TRUNC) [45-55] [DATE_TRUNC] + PathExpression [56-70] [_PARTITIONTIME] + Identifier(_PARTITIONTIME) [56-70] [_PARTITIONTIME] + PathExpression [72-77] [MONTH] + Identifier(MONTH) [72-77] [MONTH] + OptionsList [86-88] [()] +-- +CREATE SEARCH INDEX i1 ON t1(a) +PARTITION BY DATE_TRUNC(_PARTITIONTIME, MONTH) +OPTIONS() + +== + +# Specify key only in OPTIONS is invalid. +create search index i1 on t1(a options(k)) +-- +ERROR: Syntax error: Expected "=" but got ")" [at 1:41] +create search index i1 on t1(a options(k)) + ^ + +== + +# Specify value only in OPTIONS is invalid +create search index i1 on t1(a options(=v)) +-- +ERROR: Syntax error: Unexpected "=" [at 1:40] +create search index i1 on t1(a options(=v)) + ^ + +== + +# Specify empty OPTIONS. +create search index i1 on t1(a options()) +-- +CreateIndexStatement(SEARCH) [0-41] [create search...options())] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-41] [(a options())] + OrderingExpression(ASC) [29-40] [a options()] + PathExpression [29-30] [a] + Identifier(a) [29-30] [a] + OptionsList [38-40] [()] +-- +CREATE SEARCH INDEX i1 ON t1(a OPTIONS()) + +== + +# Specify per-column options +create search index i1 on t1(a options(k1=v1)) +-- +CreateIndexStatement(SEARCH) [0-46] [create search...ptions(k1=v1))] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-46] [(a options(k1=v1))] + OrderingExpression(ASC) [29-45] [a options(k1=v1)] + PathExpression [29-30] [a] + Identifier(a) [29-30] [a] + OptionsList [38-45] [(k1=v1)] + OptionsEntry [39-44] [k1=v1] + Identifier(k1) [39-41] [k1] + PathExpression [42-44] [v1] + Identifier(v1) [42-44] [v1] +-- +CREATE SEARCH INDEX i1 ON t1(a OPTIONS(k1 = v1)) + +== + +# Specify multiple per-column options +create search index i1 on t1(c1 options(k1=v1), c2 options({{k2=v2, k3=v3|}})) +-- +ALTERNATION GROUP: k2=v2, k3=v3 +-- +CreateIndexStatement(SEARCH) [0-73] [create search...v2, k3=v3))] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-73] [(c1 options...v2, k3=v3))] + OrderingExpression(ASC) [29-46] [c1 options(k1=v1)] + PathExpression [29-31] [c1] + Identifier(c1) [29-31] [c1] + OptionsList [39-46] [(k1=v1)] + OptionsEntry [40-45] [k1=v1] + Identifier(k1) [40-42] [k1] + PathExpression [43-45] [v1] + Identifier(v1) [43-45] [v1] + OrderingExpression(ASC) [48-72] [c2 options(k2=v2, k3=v3)] + PathExpression [48-50] [c2] + Identifier(c2) [48-50] [c2] + OptionsList [58-72] [(k2=v2, k3=v3)] + OptionsEntry [59-64] [k2=v2] + Identifier(k2) [59-61] [k2] + PathExpression [62-64] [v2] + Identifier(v2) [62-64] [v2] + OptionsEntry [66-71] [k3=v3] + Identifier(k3) [66-68] [k3] + PathExpression [69-71] [v3] + Identifier(v3) [69-71] [v3] + +-- +CREATE SEARCH INDEX i1 ON t1(c1 OPTIONS(k1 = v1), c2 OPTIONS(k2 = v2, k3 = v3)) +-- +ALTERNATION GROUP: +-- +CreateIndexStatement(SEARCH) [0-61] [create search...options())] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-61] [(c1 options...options())] + OrderingExpression(ASC) [29-46] [c1 options(k1=v1)] + PathExpression [29-31] [c1] + Identifier(c1) [29-31] [c1] + OptionsList [39-46] [(k1=v1)] + OptionsEntry [40-45] [k1=v1] + Identifier(k1) [40-42] [k1] + PathExpression [43-45] [v1] + Identifier(v1) [43-45] [v1] + OrderingExpression(ASC) [48-60] [c2 options()] + PathExpression [48-50] [c2] + Identifier(c2) [48-50] [c2] + OptionsList [58-60] [()] +-- +CREATE SEARCH INDEX i1 ON t1(c1 OPTIONS(k1 = v1), c2 OPTIONS()) + +== + +# Specify multiple per-column options with overlapping option keys +create search index i1 on t1(c1 options(k1=v1), c2 options(k1=v1, k2=v2)) +-- +CreateIndexStatement(SEARCH) [0-73] [create search...v1, k2=v2))] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-73] [(c1 options...v1, k2=v2))] + OrderingExpression(ASC) [29-46] [c1 options(k1=v1)] + PathExpression [29-31] [c1] + Identifier(c1) [29-31] [c1] + OptionsList [39-46] [(k1=v1)] + OptionsEntry [40-45] [k1=v1] + Identifier(k1) [40-42] [k1] + PathExpression [43-45] [v1] + Identifier(v1) [43-45] [v1] + OrderingExpression(ASC) [48-72] [c2 options(k1=v1, k2=v2)] + PathExpression [48-50] [c2] + Identifier(c2) [48-50] [c2] + OptionsList [58-72] [(k1=v1, k2=v2)] + OptionsEntry [59-64] [k1=v1] + Identifier(k1) [59-61] [k1] + PathExpression [62-64] [v1] + Identifier(v1) [62-64] [v1] + OptionsEntry [66-71] [k2=v2] + Identifier(k2) [66-68] [k2] + PathExpression [69-71] [v2] + Identifier(v2) [69-71] [v2] +-- +CREATE SEARCH INDEX i1 ON t1(c1 OPTIONS(k1 = v1), c2 OPTIONS(k1 = v1, k2 = v2)) + +== + +# Specify empty list of columns inside WITH COLUMN OPTIONS +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS ()) +-- +ERROR: Syntax error: Unexpected ")" [at 1:64] +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS ()) + ^ + +== + +# Specify key only ordering column option in WITH COLUMN OPTIONS is invalid. +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS (c1 OPTIONS(k))) +-- +ERROR: Syntax error: Expected "=" but got ")" [at 1:76] +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS (c1 OPTIONS(k))) + ^ + +== + +# Specify value only in WITH COLUMN OPTIONS is invalid. +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS (c1 OPTIONS(=v))) +-- +ERROR: Syntax error: Unexpected "=" [at 1:75] +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS (c1 OPTIONS(=v))) + ^ + +== + +# Specify empty list of OPTIONS for a column in WITH COLUMN OPTIONS. +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS (c1 OPTIONS())) +-- +CreateIndexStatement(SEARCH) [0-77] [create search...OPTIONS()))] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [29-77] [(ALL COLUMNS...OPTIONS()))] + OrderingExpression(ASC) [29-77] [(ALL COLUMNS...OPTIONS()))] + IndexAllColumns(ALL COLUMNS) [29-77] [(ALL COLUMNS...OPTIONS()))] + IndexItemList [62-75] [(c1 OPTIONS()] + OrderingExpression(ASC) [63-75] [c1 OPTIONS()] + PathExpression [63-65] [c1] + Identifier(c1) [63-65] [c1] + OptionsList [73-75] [()] + +-- +CREATE SEARCH INDEX i1 ON t1(ALL COLUMNS WITH COLUMN OPTIONS(c1 OPTIONS())) + +== + +# Specify WITH COLUMN OPTIONS with ALL COLUMNS +create search index i1 on t1 (ALL COLUMNS WITH COLUMN OPTIONS (c1 OPTIONS (k1=v1))) +-- +CreateIndexStatement(SEARCH) [0-83] [create search...(k1=v1)))] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [29-83] [(ALL COLUMNS...(k1=v1)))] + OrderingExpression(ASC) [29-83] [(ALL COLUMNS...(k1=v1)))] + IndexAllColumns(ALL COLUMNS) [29-83] [(ALL COLUMNS...(k1=v1)))] + IndexItemList [62-81] [(c1 OPTIONS (k1=v1)] + OrderingExpression(ASC) [63-81] [c1 OPTIONS (k1=v1)] + PathExpression [63-65] [c1] + Identifier(c1) [63-65] [c1] + OptionsList [74-81] [(k1=v1)] + OptionsEntry [75-80] [k1=v1] + Identifier(k1) [75-77] [k1] + PathExpression [78-80] [v1] + Identifier(v1) [78-80] [v1] +-- +CREATE SEARCH INDEX i1 ON t1(ALL COLUMNS WITH COLUMN OPTIONS(c1 OPTIONS(k1 = v1))) + +== + +# Specify multiple per-column options with ALL COLUMNS +create search index i1 on t1(ALL COLUMNS WITH COLUMN OPTIONS (c1 options(k1=v1), c2 options(k2=v2, k3=v3))) +-- +CreateIndexStatement(SEARCH) [0-107] [create search..., k3=v3)))] + PathExpression [20-22] [i1] + Identifier(i1) [20-22] [i1] + PathExpression [26-28] [t1] + Identifier(t1) [26-28] [t1] + IndexItemList [28-107] [(ALL COLUMNS..., k3=v3)))] + OrderingExpression(ASC) [28-107] [(ALL COLUMNS..., k3=v3)))] + IndexAllColumns(ALL COLUMNS) [28-107] [(ALL COLUMNS..., k3=v3)))] + IndexItemList [61-105] [(c1 options...v2, k3=v3)] + OrderingExpression(ASC) [62-79] [c1 options(k1=v1)] + PathExpression [62-64] [c1] + Identifier(c1) [62-64] [c1] + OptionsList [72-79] [(k1=v1)] + OptionsEntry [73-78] [k1=v1] + Identifier(k1) [73-75] [k1] + PathExpression [76-78] [v1] + Identifier(v1) [76-78] [v1] + OrderingExpression(ASC) [81-105] [c2 options(k2=v2, k3=v3)] + PathExpression [81-83] [c2] + Identifier(c2) [81-83] [c2] + OptionsList [91-105] [(k2=v2, k3=v3)] + OptionsEntry [92-97] [k2=v2] + Identifier(k2) [92-94] [k2] + PathExpression [95-97] [v2] + Identifier(v2) [95-97] [v2] + OptionsEntry [99-104] [k3=v3] + Identifier(k3) [99-101] [k3] + PathExpression [102-104] [v3] + Identifier(v3) [102-104] [v3] +-- +CREATE SEARCH INDEX i1 ON t1(ALL COLUMNS WITH COLUMN OPTIONS(c1 OPTIONS(k1 = v1), c2 OPTIONS(k2 = v2, + k3 = v3))) diff --git a/zetasql/parser/testdata/create_model.test b/zetasql/parser/testdata/create_model.test index 997175fff..375ac37c3 100644 --- a/zetasql/parser/testdata/create_model.test +++ b/zetasql/parser/testdata/create_model.test @@ -464,8 +464,8 @@ CreateModelStatement(is_temp) [0-96] [create temp...cross join t4] Identifier(`t 2`) [30-35] [`t 2`] Query [39-96] [select 1 from...cross join t4] SetOperation(UNION ALL) [39-96] [select 1 from...cross join t4] - SetOperationMetadataList [55-65] [union all] - SetOperationMetadata [55-65] [union all] + SetOperationMetadataList [56-65] [union all] + SetOperationMetadata [56-65] [union all] SetOperationType [56-61] [union] SetOperationAllOrDistinct [62-65] [all] Select [39-55] [select 1 from t2] diff --git a/zetasql/parser/testdata/create_procedure.test b/zetasql/parser/testdata/create_procedure.test index 3f03c6908..c779a0707 100644 --- a/zetasql/parser/testdata/create_procedure.test +++ b/zetasql/parser/testdata/create_procedure.test @@ -89,22 +89,22 @@ CreateProcedureStatement [0-222] [CREATE PROCEDURE...BEGIN END] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-212] [( param_a...TABLE )] - FunctionParameter [32-49] [param_a string] + FunctionParameter [35-49] [param_a string] Identifier(param_a) [35-42] [param_a] SimpleType [43-49] [string] PathExpression [43-49] [string] Identifier(string) [43-49] [string] - FunctionParameter [50-66] [param_b int32] + FunctionParameter [53-66] [param_b int32] Identifier(param_b) [53-60] [param_b] SimpleType [61-66] [int32] PathExpression [61-66] [int32] Identifier(int32) [61-66] [int32] - FunctionParameter [67-85] [param_c numeric] + FunctionParameter [70-85] [param_c numeric] Identifier(param_c) [70-77] [param_c] SimpleType [78-85] [numeric] PathExpression [78-85] [numeric] Identifier(numeric) [78-85] [numeric] - FunctionParameter [86-116] [param_d table] + FunctionParameter [89-116] [param_d table] Identifier(param_d) [89-96] [param_d] TVFSchema [97-116] [table] TVFSchemaColumn [103-108] [int32] @@ -115,13 +115,13 @@ CreateProcedureStatement [0-222] [CREATE PROCEDURE...BEGIN END] SimpleType [110-115] [int32] PathExpression [110-115] [int32] Identifier(int32) [110-115] [int32] - FunctionParameter [117-136] [param_e ANY TYPE] + FunctionParameter [120-136] [param_e ANY TYPE] Identifier(param_e) [120-127] [param_e] TemplatedParameterType [128-136] [ANY TYPE] FunctionParameter(mode=OUT) [140-160] [OUT param_f ANY TYPE] Identifier(param_f) [144-151] [param_f] TemplatedParameterType [152-160] [ANY TYPE] - FunctionParameter [161-181] [param_g ANY TABLE] + FunctionParameter [164-181] [param_g ANY TABLE] Identifier(param_g) [164-171] [param_g] TemplatedParameterType [172-181] [ANY TABLE] FunctionParameter(mode=INOUT) [185-208] [INOUT param_h ANY TABLE] @@ -300,7 +300,7 @@ CreateProcedureStatement [0-58] [CREATE PROCEDURE...BEGIN END] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [32-48] [( param_a int32)] - FunctionParameter [33-47] [param_a int32] + FunctionParameter [34-47] [param_a int32] Identifier(param_a) [34-41] [param_a] SimpleType [42-47] [int32] PathExpression [42-47] [int32] @@ -571,7 +571,7 @@ CreateProcedureStatement [0-176] [CREATE PROCEDURE...ython code"] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-97] [( param_a...ANY TYPE)] - FunctionParameter [32-48] [param_a int32] + FunctionParameter [35-48] [param_a int32] Identifier(param_a) [35-42] [param_a] SimpleType [43-48] [int32] PathExpression [43-48] [int32] @@ -619,7 +619,7 @@ CreateProcedureStatement [0-182] [CREATE PROCEDURE...ython code"] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-97] [( param_a...ANY TYPE)] - FunctionParameter [32-48] [param_a int32] + FunctionParameter [35-48] [param_a int32] Identifier(param_a) [35-42] [param_a] SimpleType [43-48] [int32] PathExpression [43-48] [int32] @@ -668,7 +668,7 @@ CreateProcedureStatement [0-170] [CREATE PROCEDURE...ython code"] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-97] [( param_a...ANY TYPE)] - FunctionParameter [32-48] [param_a int32] + FunctionParameter [35-48] [param_a int32] Identifier(param_a) [35-42] [param_a] SimpleType [43-48] [int32] PathExpression [43-48] [int32] @@ -708,7 +708,7 @@ CreateProcedureStatement [0-152] [CREATE PROCEDURE...ython code"] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-97] [( param_a...ANY TYPE)] - FunctionParameter [32-48] [param_a int32] + FunctionParameter [35-48] [param_a int32] Identifier(param_a) [35-42] [param_a] SimpleType [43-48] [int32] PathExpression [43-48] [int32] @@ -752,7 +752,7 @@ CreateProcedureStatement [0-135] [CREATE PROCEDURE...UAGE PYTHON] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-97] [( param_a...ANY TYPE)] - FunctionParameter [32-48] [param_a int32] + FunctionParameter [35-48] [param_a int32] Identifier(param_a) [35-42] [param_a] SimpleType [43-48] [int32] PathExpression [43-48] [int32] @@ -957,7 +957,7 @@ CreateProcedureStatement [0-90] [CREATE PROCEDURE...LECT 1; END] PathExpression [17-31] [procedure_name] Identifier(procedure_name) [17-31] [procedure_name] FunctionParameters [31-68] [( param_a...-> INT64>)] - FunctionParameter [32-67] [param_a FUNCTION...-> INT64>] + FunctionParameter [35-67] [param_a FUNCTION...-> INT64>] Identifier(param_a) [35-42] [param_a] FunctionType [43-67] [FUNCTION INT64>] FunctionTypeArgList [52-57] [INT64] diff --git a/zetasql/parser/testdata/create_row_access_policy.test b/zetasql/parser/testdata/create_row_access_policy.test index fede47adb..76828ab49 100644 --- a/zetasql/parser/testdata/create_row_access_policy.test +++ b/zetasql/parser/testdata/create_row_access_policy.test @@ -302,7 +302,7 @@ CreateRowAccessPolicyStatement [0-84] [create row...region = "us")] GranteeList [44-60] ["bar@google.com"] StringLiteral [44-60] ["bar@google.com"] StringLiteralComponent("bar@google.com") [44-60] ["bar@google.com"] - FilterUsingClause [61-84] [using (region = "us")] + FilterUsingClause [63-84] [using (region = "us")] BinaryExpression(=) [70-83] [region = "us"] PathExpression [70-76] [region] Identifier(region) [70-76] [region] @@ -354,7 +354,7 @@ CreateRowAccessPolicyStatement [0-78] [create row...region = "us")] GranteeList [38-54] ["bar@google.com"] StringLiteral [38-54] ["bar@google.com"] StringLiteralComponent("bar@google.com") [38-54] ["bar@google.com"] - FilterUsingClause [55-78] [using (region = "us")] + FilterUsingClause [57-78] [using (region = "us")] BinaryExpression(=) [64-77] [region = "us"] PathExpression [64-70] [region] Identifier(region) [64-70] [region] diff --git a/zetasql/parser/testdata/create_row_policy.test b/zetasql/parser/testdata/create_row_policy.test index c26a0036b..eb07dd185 100644 --- a/zetasql/parser/testdata/create_row_policy.test +++ b/zetasql/parser/testdata/create_row_policy.test @@ -7,7 +7,7 @@ CreateRowAccessPolicyStatement [0-61] [create row...c1 = 'foo')] GranteeList [27-43] ['foo@google.com'] StringLiteral [27-43] ['foo@google.com'] StringLiteralComponent('foo@google.com') [27-43] ['foo@google.com'] - FilterUsingClause [43-61] [using(c1 = 'foo')] + FilterUsingClause [44-61] [using(c1 = 'foo')] BinaryExpression(=) [50-60] [c1 = 'foo'] PathExpression [50-52] [c1] Identifier(c1) [50-52] [c1] @@ -26,7 +26,7 @@ CreateRowAccessPolicyStatement [0-61] [create row...c2 = 'foo')] GranteeList [30-43] ['mdbuser/bar'] StringLiteral [30-43] ['mdbuser/bar'] StringLiteralComponent('mdbuser/bar') [30-43] ['mdbuser/bar'] - FilterUsingClause [43-61] [using(c2 = 'foo')] + FilterUsingClause [44-61] [using(c2 = 'foo')] BinaryExpression(=) [50-60] [c2 = 'foo'] PathExpression [50-52] [c2] Identifier(c2) [50-52] [c2] @@ -49,7 +49,7 @@ CreateRowAccessPolicyStatement [0-69] [create row...using(c1)] StringLiteralComponent('foo@google.com') [27-43] ['foo@google.com'] StringLiteral [45-59] ['mdbgroup/bar'] StringLiteralComponent('mdbgroup/bar') [45-59] ['mdbgroup/bar'] - FilterUsingClause [59-69] [using(c1)] + FilterUsingClause [60-69] [using(c1)] PathExpression [66-68] [c1] Identifier(c1) [66-68] [c1] -- @@ -68,7 +68,7 @@ CreateRowAccessPolicyStatement [0-72] [create row...using(c1)] StringLiteralComponent('foo@google.com') [30-46] ['foo@google.com'] StringLiteral [48-62] ['mdbgroup/bar'] StringLiteralComponent('mdbgroup/bar') [48-62] ['mdbgroup/bar'] - FilterUsingClause [62-72] [using(c1)] + FilterUsingClause [63-72] [using(c1)] PathExpression [69-71] [c1] Identifier(c1) [69-71] [c1] -- @@ -87,7 +87,7 @@ CreateRowAccessPolicyStatement [0-71] [create row...' using(1)] StringLiteralComponent('foo@google.com') [30-46] ['foo@google.com'] StringLiteral [48-62] ['mdbgroup/bar'] StringLiteralComponent('mdbgroup/bar') [48-62] ['mdbgroup/bar'] - FilterUsingClause [62-71] [using(1)] + FilterUsingClause [63-71] [using(1)] IntLiteral(1) [69-70] [1] -- CREATE ROW POLICY ON n1.t1 TO 'foo@google.com', 'mdbgroup/bar' USING (1) @@ -106,7 +106,7 @@ CreateRowAccessPolicyStatement [0-38] [create row...using(true)] PathExpression [21-26] [n1.t1] Identifier(n1) [21-23] [n1] Identifier(t1) [24-26] [t1] - FilterUsingClause [26-38] [using(true)] + FilterUsingClause [27-38] [using(true)] BooleanLiteral(true) [33-37] [true] -- CREATE ROW POLICY ON n1.t1 USING (true) @@ -179,7 +179,7 @@ CreateRowAccessPolicyStatement [0-57] [create row...using(true)] GranteeList [27-45] [@test_param_string] ParameterExpr [27-45] [@test_param_string] Identifier(test_param_string) [28-45] [test_param_string] - FilterUsingClause [45-57] [using(true)] + FilterUsingClause [46-57] [using(true)] BooleanLiteral(true) [52-56] [true] -- CREATE ROW POLICY ON t1 TO @test_param_string USING (true) @@ -195,7 +195,7 @@ CreateRowAccessPolicyStatement [0-47] [create row...using(true)] SystemVariableExpr [27-35] [@@sysvar] PathExpression [29-35] [sysvar] Identifier(sysvar) [29-35] [sysvar] - FilterUsingClause [35-47] [using(true)] + FilterUsingClause [36-47] [using(true)] BooleanLiteral(true) [42-46] [true] -- CREATE ROW POLICY ON t1 TO @@sysvar USING (true) @@ -231,7 +231,7 @@ CreateRowAccessPolicyStatement [0-41] [create row...using(true)] GranteeList [27-29] [""] StringLiteral [27-29] [""] StringLiteralComponent("") [27-29] [""] - FilterUsingClause [29-41] [using(true)] + FilterUsingClause [30-41] [using(true)] BooleanLiteral(true) [36-40] [true] -- CREATE ROW POLICY ON t1 TO "" USING (true) diff --git a/zetasql/parser/testdata/create_table_as_select.test b/zetasql/parser/testdata/create_table_as_select.test index 9ee6f9479..0a2679c54 100644 --- a/zetasql/parser/testdata/create_table_as_select.test +++ b/zetasql/parser/testdata/create_table_as_select.test @@ -155,8 +155,8 @@ CreateTableStatement(is_temp) [0-96] [create temp...cross join t4] Identifier(`t 2`) [30-35] [`t 2`] Query [39-96] [select 1 from...cross join t4] SetOperation(UNION ALL) [39-96] [select 1 from...cross join t4] - SetOperationMetadataList [55-65] [union all] - SetOperationMetadata [55-65] [union all] + SetOperationMetadataList [56-65] [union all] + SetOperationMetadata [56-65] [union all] SetOperationType [56-61] [union] SetOperationAllOrDistinct [62-65] [all] Select [39-55] [select 1 from t2] diff --git a/zetasql/parser/testdata/create_view.test b/zetasql/parser/testdata/create_view.test index 3b659a300..87eb9f3c6 100644 --- a/zetasql/parser/testdata/create_view.test +++ b/zetasql/parser/testdata/create_view.test @@ -145,8 +145,8 @@ CreateViewStatement(is_temp) [0-95] [create temp...cross join t4] Identifier(`t 2`) [29-34] [`t 2`] Query [38-95] [select 1 from...cross join t4] SetOperation(UNION ALL) [38-95] [select 1 from...cross join t4] - SetOperationMetadataList [54-64] [union all] - SetOperationMetadata [54-64] [union all] + SetOperationMetadataList [55-64] [union all] + SetOperationMetadata [55-64] [union all] SetOperationType [55-60] [union] SetOperationAllOrDistinct [61-64] [all] Select [38-54] [select 1 from t2] diff --git a/zetasql/parser/testdata/dml_insert.test b/zetasql/parser/testdata/dml_insert.test index fee0eec2c..e74dab392 100644 --- a/zetasql/parser/testdata/dml_insert.test +++ b/zetasql/parser/testdata/dml_insert.test @@ -1523,8 +1523,8 @@ InsertStatement(insert_mode=REPLACE) [0-110] [insert or...rt_rows_modified 0] Identifier(T) [23-24] [T] Query [25-87] [select * from...x limit 5] SetOperation(UNION ALL) [25-68] [select * from...* from q2] - SetOperationMetadataList [41-51] [union all] - SetOperationMetadata [41-51] [union all] + SetOperationMetadataList [42-51] [union all] + SetOperationMetadata [42-51] [union all] SetOperationType [42-47] [union] SetOperationAllOrDistinct [48-51] [all] Select [25-41] [select * from q1] @@ -1674,8 +1674,8 @@ InsertStatement [0-47] [insert into...select 2))] Identifier(T) [12-13] [T] Query [15-46] [(select 1)...(select 2)] SetOperation(UNION ALL) [15-46] [(select 1)...(select 2)] - SetOperationMetadataList [25-35] [union all] - SetOperationMetadata [25-35] [union all] + SetOperationMetadataList [26-35] [union all] + SetOperationMetadata [26-35] [union all] SetOperationType [26-31] [union] SetOperationAllOrDistinct [32-35] [all] Query [16-24] [select 1] @@ -1710,8 +1710,8 @@ InsertStatement [0-57] [insert into...select 8,9] Identifier(c2) [19-21] [c2] Query [23-57] [(select 5,...select 8,9] SetOperation(UNION ALL) [23-57] [(select 5,...select 8,9] - SetOperationMetadataList [36-46] [union all] - SetOperationMetadata [36-46] [union all] + SetOperationMetadataList [37-46] [union all] + SetOperationMetadata [37-46] [union all] SetOperationType [37-42] [union] SetOperationAllOrDistinct [43-46] [all] Query [24-35] [select 5, 7] diff --git a/zetasql/parser/testdata/drop.test b/zetasql/parser/testdata/drop.test index 958fd953e..6d0af0bf4 100644 --- a/zetasql/parser/testdata/drop.test +++ b/zetasql/parser/testdata/drop.test @@ -199,7 +199,7 @@ drop SNAPSHOT TABLE # The follow object types are syntactically valid (no syntax errors), # although AGGREGATE FUNCTION and TABLE FUNCTION are not currently supported. # Other object types are not valid. -drop {{AGGREGATE FUNCTION|CONSTANT|DATABASE|EXTERNAL TABLE|FUNCTION|INDEX|MATERIALIZED VIEW|MODEL|PROCEDURE|TABLE|TABLE FUNCTION|VIEW|EXTERNAL TABLE FUNCTION|SCHEMA|EXTERNAL SCHEMA|SNAPSHOT TABLE}} foo {{|RESTRICT|Restrict|CASCADE|INVALID_DROP_MODE|DROP_MODE_UNSPECIFIED}}; +drop {{AGGREGATE FUNCTION|CONNECTION|CONSTANT|DATABASE|EXTERNAL TABLE|FUNCTION|INDEX|MATERIALIZED VIEW|MODEL|PROCEDURE|TABLE|TABLE FUNCTION|VIEW|EXTERNAL TABLE FUNCTION|SCHEMA|EXTERNAL SCHEMA|SNAPSHOT TABLE}} foo {{|RESTRICT|Restrict|CASCADE|INVALID_DROP_MODE|DROP_MODE_UNSPECIFIED}}; -- ALTERNATION GROUP: AGGREGATE FUNCTION, -- @@ -237,6 +237,44 @@ ERROR: DROP AGGREGATE FUNCTION is not supported, use DROP FUNCTION [at 1:6] drop AGGREGATE FUNCTION foo DROP_MODE_UNSPECIFIED; ^ -- +ALTERNATION GROUP: CONNECTION, +-- +DropStatement CONNECTION [0-19] [drop CONNECTION foo] + PathExpression [16-19] [foo] + Identifier(foo) [16-19] [foo] +-- +DROP CONNECTION foo +-- +ALTERNATION GROUP: CONNECTION,RESTRICT +-- +ERROR: Syntax error: 'RESTRICT' is not supported for DROP CONNECTION [at 1:21] +drop CONNECTION foo RESTRICT; + ^ +-- +ALTERNATION GROUP: CONNECTION,Restrict +-- +ERROR: Syntax error: 'RESTRICT' is not supported for DROP CONNECTION [at 1:21] +drop CONNECTION foo Restrict; + ^ +-- +ALTERNATION GROUP: CONNECTION,CASCADE +-- +ERROR: Syntax error: 'CASCADE' is not supported for DROP CONNECTION [at 1:21] +drop CONNECTION foo CASCADE; + ^ +-- +ALTERNATION GROUP: CONNECTION,INVALID_DROP_MODE +-- +ERROR: Syntax error: Expected end of input but got identifier "INVALID_DROP_MODE" [at 1:21] +drop CONNECTION foo INVALID_DROP_MODE; + ^ +-- +ALTERNATION GROUP: CONNECTION,DROP_MODE_UNSPECIFIED +-- +ERROR: Syntax error: Expected end of input but got identifier "DROP_MODE_UNSPECIFIED" [at 1:21] +drop CONNECTION foo DROP_MODE_UNSPECIFIED; + ^ +-- ALTERNATION GROUP: CONSTANT, -- DropStatement CONSTANT [0-17] [drop CONSTANT foo] @@ -905,3 +943,21 @@ DropStatement TABLE [0-22] [drop table foo-bar.baz] Identifier(baz) [19-22] [baz] -- DROP TABLE `foo-bar`.baz +== + +DROP CONNECTION IF EXISTS foo +-- +DropStatement CONNECTION(is_if_exists) [0-29] [DROP CONNECTION IF EXISTS foo] + PathExpression [26-29] [foo] + Identifier(foo) [26-29] [foo] +-- +DROP CONNECTION IF EXISTS foo +== + +DROP CONNECTION foo +-- +DropStatement CONNECTION [0-19] [DROP CONNECTION foo] + PathExpression [16-19] [foo] + Identifier(foo) [16-19] [foo] +-- +DROP CONNECTION foo diff --git a/zetasql/parser/testdata/expression_subquery.test b/zetasql/parser/testdata/expression_subquery.test index a466bf43c..76da3de5b 100644 --- a/zetasql/parser/testdata/expression_subquery.test +++ b/zetasql/parser/testdata/expression_subquery.test @@ -254,8 +254,8 @@ QueryStatement [0-42] [select EXISTS...select 2)] ExpressionSubquery(modifier=EXISTS) [7-42] [EXISTS(select...select 2)] Query [14-41] [select 1 union all select 2] SetOperation(UNION ALL) [14-41] [select 1 union all select 2] - SetOperationMetadataList [22-32] [union all] - SetOperationMetadata [22-32] [union all] + SetOperationMetadataList [23-32] [union all] + SetOperationMetadata [23-32] [union all] SetOperationType [23-28] [union] SetOperationAllOrDistinct [29-32] [all] Select [14-22] [select 1] diff --git a/zetasql/parser/testdata/from.test b/zetasql/parser/testdata/from.test index a1d719859..e216ceb36 100644 --- a/zetasql/parser/testdata/from.test +++ b/zetasql/parser/testdata/from.test @@ -1490,6 +1490,8 @@ select @{a=b} DISTINCT AS foo from t ^ == +# Standalone FROM is not supported with default options. +# Tests with it enabled are in pipe_from_query.test. from Table -- ERROR: Syntax error: Unexpected FROM [at 1:1] diff --git a/zetasql/parser/testdata/generalized_field.test b/zetasql/parser/testdata/generalized_field.test index 15d466193..59e92c1d2 100644 --- a/zetasql/parser/testdata/generalized_field.test +++ b/zetasql/parser/testdata/generalized_field.test @@ -291,3 +291,26 @@ from unnest(a.b).(c) ERROR: Syntax error: Generalized field access is not allowed in the FROM clause without UNNEST; Use UNNEST() [at 2:18] from unnest(a.b).(c) ^ +== + +# Regression test for b/331836328; int and float in Lhs need to maintain parens. +select (1).(x), (1.).(x) +-- +QueryStatement [0-24] [select (1).(x), (1.).(x)] + Query [0-24] [select (1).(x), (1.).(x)] + Select [0-24] [select (1).(x), (1.).(x)] + SelectList [7-24] [(1).(x), (1.).(x)] + SelectColumn [7-14] [(1).(x)] + DotGeneralizedField [7-14] [(1).(x)] + IntLiteral(1) [8-9] [1] + PathExpression [12-13] [x] + Identifier(x) [12-13] [x] + SelectColumn [16-24] [(1.).(x)] + DotGeneralizedField [16-24] [(1.).(x)] + FloatLiteral(1.) [17-19] [1.] + PathExpression [22-23] [x] + Identifier(x) [22-23] [x] +-- +SELECT + (1).(x), + (1.).(x) diff --git a/zetasql/parser/testdata/in.test b/zetasql/parser/testdata/in.test index f547e2ed6..5345880da 100644 --- a/zetasql/parser/testdata/in.test +++ b/zetasql/parser/testdata/in.test @@ -407,8 +407,8 @@ QueryStatement [0-47] [select 1 IN...select 2) )] Location [9-11] [IN] Query [14-45] [(select 1)...(select 2)] SetOperation(UNION ALL) [14-45] [(select 1)...(select 2)] - SetOperationMetadataList [24-34] [UNION ALL] - SetOperationMetadata [24-34] [UNION ALL] + SetOperationMetadataList [25-34] [UNION ALL] + SetOperationMetadata [25-34] [UNION ALL] SetOperationType [25-30] [UNION] SetOperationAllOrDistinct [31-34] [ALL] Query [15-23] [select 1] @@ -521,8 +521,8 @@ QueryStatement [0-63] [select 1 IN...select 3) )] ExpressionSubquery [14-49] [( (select...select 2) )] Query [16-47] [(select 1)...(select 2)] SetOperation(UNION ALL) [16-47] [(select 1)...(select 2)] - SetOperationMetadataList [26-36] [UNION ALL] - SetOperationMetadata [26-36] [UNION ALL] + SetOperationMetadataList [27-36] [UNION ALL] + SetOperationMetadata [27-36] [UNION ALL] SetOperationType [27-32] [UNION] SetOperationAllOrDistinct [33-36] [ALL] Query [17-25] [select 1] @@ -581,8 +581,8 @@ QueryStatement [0-65] [select 1 IN...select 3) )] ExpressionSubquery [14-51] [( (select...select 2 x) )] Query [16-49] [(select 1)...select 2 x)] SetOperation(UNION ALL) [16-49] [(select 1)...select 2 x)] - SetOperationMetadataList [26-36] [UNION ALL] - SetOperationMetadata [26-36] [UNION ALL] + SetOperationMetadataList [27-36] [UNION ALL] + SetOperationMetadata [27-36] [UNION ALL] SetOperationType [27-32] [UNION] SetOperationAllOrDistinct [33-36] [ALL] Query [17-25] [select 1] diff --git a/zetasql/parser/testdata/limit.test b/zetasql/parser/testdata/limit.test index c35dbea4e..397fc1f71 100644 --- a/zetasql/parser/testdata/limit.test +++ b/zetasql/parser/testdata/limit.test @@ -169,8 +169,8 @@ LIMIT 10 OFFSET 2 QueryStatement [0-83] [(SELECT 1...10 OFFSET 2] Query [0-83] [(SELECT 1...10 OFFSET 2] SetOperation(UNION ALL) [0-65] [(SELECT 1...OFFSET 2)] - SetOperationMetadataList [27-37] [UNION ALL] - SetOperationMetadata [27-37] [UNION ALL] + SetOperationMetadataList [28-37] [UNION ALL] + SetOperationMetadata [28-37] [UNION ALL] SetOperationType [28-33] [UNION] SetOperationAllOrDistinct [34-37] [ALL] Query [1-26] [SELECT 1 LIMIT 1 OFFSET 1] @@ -340,8 +340,8 @@ LIMIT 2 OFFSET 1 QueryStatement [0-69] [select a from...2 OFFSET 1] Query [0-69] [select a from...2 OFFSET 1] SetOperation(UNION ALL) [0-41] [select a from...elect b from t] - SetOperationMetadataList [15-25] [union all] - SetOperationMetadata [15-25] [union all] + SetOperationMetadataList [16-25] [union all] + SetOperationMetadata [16-25] [union all] SetOperationType [16-21] [union] SetOperationAllOrDistinct [22-25] [all] Select [0-15] [select a from t] diff --git a/zetasql/parser/testdata/match_recognize.test b/zetasql/parser/testdata/match_recognize.test new file mode 100644 index 000000000..1bb7aefd3 --- /dev/null +++ b/zetasql/parser/testdata/match_recognize.test @@ -0,0 +1,1124 @@ +[default reserve_match_recognize] +# With and without aliases before and after +select * from t {{| inner_alias | AS inner_alias }} match_recognize( + ORDER BY a + MEASURES max(A.b) AS m1 + PATTERN ( A ) + DEFINE A AS true +) {{| outer_alias | AS outer_alias }} +-- +ALTERNATION GROUP: +-- +QueryStatement [44-153] [select * from...AS true )] + Query [44-153] [select * from...AS true )] + Select [44-153] [select * from...AS true )] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-153] [from t match_rec...AS true )] + TablePathExpression [58-153] [t match_recogniz...AS true )] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + MatchRecognizeClause [61-153] [match_recognize...AS true )] + OrderBy [80-90] [ORDER BY a] + OrderingExpression(ASC) [89-90] [a] + PathExpression [89-90] [a] + Identifier(a) [89-90] [a] + SelectList [102-116] [max(A.b) AS m1] + SelectColumn [102-116] [max(A.b) AS m1] + FunctionCall [102-110] [max(A.b)] + PathExpression [102-105] [max] + Identifier(max) [102-105] [max] + PathExpression [106-109] [A.b] + Identifier(A) [106-107] [A] + Identifier(b) [108-109] [b] + Alias [111-116] [AS m1] + Identifier(m1) [114-116] [m1] + RowPatternVariable [129-130] [A] + Identifier(A) [129-130] [A] + SelectList [142-151] [A AS true] + SelectColumn [142-151] [A AS true] + BooleanLiteral(true) [147-151] [true] + Alias [142-146] [A AS] + Identifier(A) [142-143] [A] +-- +SELECT + * +FROM + t MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) +-- +ALTERNATION GROUP: outer_alias +-- +QueryStatement [44-166] [select * from...outer_alias] + Query [44-166] [select * from...outer_alias] + Select [44-166] [select * from...outer_alias] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-166] [from t match_rec...uter_alias] + TablePathExpression [58-166] [t match_recogniz...uter_alias] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + MatchRecognizeClause [61-166] [match_recognize...outer_alias] + OrderBy [80-90] [ORDER BY a] + OrderingExpression(ASC) [89-90] [a] + PathExpression [89-90] [a] + Identifier(a) [89-90] [a] + SelectList [102-116] [max(A.b) AS m1] + SelectColumn [102-116] [max(A.b) AS m1] + FunctionCall [102-110] [max(A.b)] + PathExpression [102-105] [max] + Identifier(max) [102-105] [max] + PathExpression [106-109] [A.b] + Identifier(A) [106-107] [A] + Identifier(b) [108-109] [b] + Alias [111-116] [AS m1] + Identifier(m1) [114-116] [m1] + RowPatternVariable [129-130] [A] + Identifier(A) [129-130] [A] + SelectList [142-151] [A AS true] + SelectColumn [142-151] [A AS true] + BooleanLiteral(true) [147-151] [true] + Alias [142-146] [A AS] + Identifier(A) [142-143] [A] + Alias [155-166] [outer_alias] + Identifier(outer_alias) [155-166] [outer_alias] +-- +SELECT + * +FROM + t MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) + AS outer_alias +-- +ALTERNATION GROUP: AS outer_alias +-- +QueryStatement [44-169] [select * from...outer_alias] + Query [44-169] [select * from...outer_alias] + Select [44-169] [select * from...outer_alias] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-169] [from t match_rec...uter_alias] + TablePathExpression [58-169] [t match_recogniz...uter_alias] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + MatchRecognizeClause [61-169] [match_recognize...outer_alias] + OrderBy [80-90] [ORDER BY a] + OrderingExpression(ASC) [89-90] [a] + PathExpression [89-90] [a] + Identifier(a) [89-90] [a] + SelectList [102-116] [max(A.b) AS m1] + SelectColumn [102-116] [max(A.b) AS m1] + FunctionCall [102-110] [max(A.b)] + PathExpression [102-105] [max] + Identifier(max) [102-105] [max] + PathExpression [106-109] [A.b] + Identifier(A) [106-107] [A] + Identifier(b) [108-109] [b] + Alias [111-116] [AS m1] + Identifier(m1) [114-116] [m1] + RowPatternVariable [129-130] [A] + Identifier(A) [129-130] [A] + SelectList [142-151] [A AS true] + SelectColumn [142-151] [A AS true] + BooleanLiteral(true) [147-151] [true] + Alias [142-146] [A AS] + Identifier(A) [142-143] [A] + Alias [155-169] [AS outer_alias] + Identifier(outer_alias) [158-169] [outer_alias] +-- +SELECT + * +FROM + t MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) + AS outer_alias +-- +ALTERNATION GROUP: inner_alias , +-- +QueryStatement [44-166] [select * from...AS true )] + Query [44-166] [select * from...AS true )] + Select [44-166] [select * from...AS true )] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-166] [from t inner_ali...AS true )] + TablePathExpression [58-166] [t inner_alias...AS true )] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + Alias [61-72] [inner_alias] + Identifier(inner_alias) [61-72] [inner_alias] + MatchRecognizeClause [74-166] [match_recognize...AS true )] + OrderBy [93-103] [ORDER BY a] + OrderingExpression(ASC) [102-103] [a] + PathExpression [102-103] [a] + Identifier(a) [102-103] [a] + SelectList [115-129] [max(A.b) AS m1] + SelectColumn [115-129] [max(A.b) AS m1] + FunctionCall [115-123] [max(A.b)] + PathExpression [115-118] [max] + Identifier(max) [115-118] [max] + PathExpression [119-122] [A.b] + Identifier(A) [119-120] [A] + Identifier(b) [121-122] [b] + Alias [124-129] [AS m1] + Identifier(m1) [127-129] [m1] + RowPatternVariable [142-143] [A] + Identifier(A) [142-143] [A] + SelectList [155-164] [A AS true] + SelectColumn [155-164] [A AS true] + BooleanLiteral(true) [160-164] [true] + Alias [155-159] [A AS] + Identifier(A) [155-156] [A] +-- +SELECT + * +FROM + t AS inner_alias MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) +-- +ALTERNATION GROUP: inner_alias , outer_alias +-- +QueryStatement [44-179] [select * from...outer_alias] + Query [44-179] [select * from...outer_alias] + Select [44-179] [select * from...outer_alias] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-179] [from t inner_ali...uter_alias] + TablePathExpression [58-179] [t inner_alias...outer_alias] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + Alias [61-72] [inner_alias] + Identifier(inner_alias) [61-72] [inner_alias] + MatchRecognizeClause [74-179] [match_recognize...outer_alias] + OrderBy [93-103] [ORDER BY a] + OrderingExpression(ASC) [102-103] [a] + PathExpression [102-103] [a] + Identifier(a) [102-103] [a] + SelectList [115-129] [max(A.b) AS m1] + SelectColumn [115-129] [max(A.b) AS m1] + FunctionCall [115-123] [max(A.b)] + PathExpression [115-118] [max] + Identifier(max) [115-118] [max] + PathExpression [119-122] [A.b] + Identifier(A) [119-120] [A] + Identifier(b) [121-122] [b] + Alias [124-129] [AS m1] + Identifier(m1) [127-129] [m1] + RowPatternVariable [142-143] [A] + Identifier(A) [142-143] [A] + SelectList [155-164] [A AS true] + SelectColumn [155-164] [A AS true] + BooleanLiteral(true) [160-164] [true] + Alias [155-159] [A AS] + Identifier(A) [155-156] [A] + Alias [168-179] [outer_alias] + Identifier(outer_alias) [168-179] [outer_alias] +-- +SELECT + * +FROM + t AS inner_alias MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) + AS outer_alias +-- +ALTERNATION GROUP: inner_alias , AS outer_alias +-- +QueryStatement [44-182] [select * from...outer_alias] + Query [44-182] [select * from...outer_alias] + Select [44-182] [select * from...outer_alias] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-182] [from t inner_ali...uter_alias] + TablePathExpression [58-182] [t inner_alias...outer_alias] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + Alias [61-72] [inner_alias] + Identifier(inner_alias) [61-72] [inner_alias] + MatchRecognizeClause [74-182] [match_recognize...outer_alias] + OrderBy [93-103] [ORDER BY a] + OrderingExpression(ASC) [102-103] [a] + PathExpression [102-103] [a] + Identifier(a) [102-103] [a] + SelectList [115-129] [max(A.b) AS m1] + SelectColumn [115-129] [max(A.b) AS m1] + FunctionCall [115-123] [max(A.b)] + PathExpression [115-118] [max] + Identifier(max) [115-118] [max] + PathExpression [119-122] [A.b] + Identifier(A) [119-120] [A] + Identifier(b) [121-122] [b] + Alias [124-129] [AS m1] + Identifier(m1) [127-129] [m1] + RowPatternVariable [142-143] [A] + Identifier(A) [142-143] [A] + SelectList [155-164] [A AS true] + SelectColumn [155-164] [A AS true] + BooleanLiteral(true) [160-164] [true] + Alias [155-159] [A AS] + Identifier(A) [155-156] [A] + Alias [168-182] [AS outer_alias] + Identifier(outer_alias) [171-182] [outer_alias] +-- +SELECT + * +FROM + t AS inner_alias MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) + AS outer_alias +-- +ALTERNATION GROUP: AS inner_alias , +-- +QueryStatement [44-169] [select * from...AS true )] + Query [44-169] [select * from...AS true )] + Select [44-169] [select * from...AS true )] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-169] [from t AS...AS true )] + TablePathExpression [58-169] [t AS inner_alias...AS true )] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + Alias [61-75] [AS inner_alias] + Identifier(inner_alias) [64-75] [inner_alias] + MatchRecognizeClause [77-169] [match_recognize...AS true )] + OrderBy [96-106] [ORDER BY a] + OrderingExpression(ASC) [105-106] [a] + PathExpression [105-106] [a] + Identifier(a) [105-106] [a] + SelectList [118-132] [max(A.b) AS m1] + SelectColumn [118-132] [max(A.b) AS m1] + FunctionCall [118-126] [max(A.b)] + PathExpression [118-121] [max] + Identifier(max) [118-121] [max] + PathExpression [122-125] [A.b] + Identifier(A) [122-123] [A] + Identifier(b) [124-125] [b] + Alias [127-132] [AS m1] + Identifier(m1) [130-132] [m1] + RowPatternVariable [145-146] [A] + Identifier(A) [145-146] [A] + SelectList [158-167] [A AS true] + SelectColumn [158-167] [A AS true] + BooleanLiteral(true) [163-167] [true] + Alias [158-162] [A AS] + Identifier(A) [158-159] [A] +-- +SELECT + * +FROM + t AS inner_alias MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) +-- +ALTERNATION GROUP: AS inner_alias , outer_alias +-- +QueryStatement [44-182] [select * from...outer_alias] + Query [44-182] [select * from...outer_alias] + Select [44-182] [select * from...outer_alias] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-182] [from t AS...outer_alias] + TablePathExpression [58-182] [t AS inner_alias...uter_alias] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + Alias [61-75] [AS inner_alias] + Identifier(inner_alias) [64-75] [inner_alias] + MatchRecognizeClause [77-182] [match_recognize...outer_alias] + OrderBy [96-106] [ORDER BY a] + OrderingExpression(ASC) [105-106] [a] + PathExpression [105-106] [a] + Identifier(a) [105-106] [a] + SelectList [118-132] [max(A.b) AS m1] + SelectColumn [118-132] [max(A.b) AS m1] + FunctionCall [118-126] [max(A.b)] + PathExpression [118-121] [max] + Identifier(max) [118-121] [max] + PathExpression [122-125] [A.b] + Identifier(A) [122-123] [A] + Identifier(b) [124-125] [b] + Alias [127-132] [AS m1] + Identifier(m1) [130-132] [m1] + RowPatternVariable [145-146] [A] + Identifier(A) [145-146] [A] + SelectList [158-167] [A AS true] + SelectColumn [158-167] [A AS true] + BooleanLiteral(true) [163-167] [true] + Alias [158-162] [A AS] + Identifier(A) [158-159] [A] + Alias [171-182] [outer_alias] + Identifier(outer_alias) [171-182] [outer_alias] +-- +SELECT + * +FROM + t AS inner_alias MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) + AS outer_alias +-- +ALTERNATION GROUP: AS inner_alias , AS outer_alias +-- +QueryStatement [44-185] [select * from...outer_alias] + Query [44-185] [select * from...outer_alias] + Select [44-185] [select * from...outer_alias] + SelectList [51-52] [*] + SelectColumn [51-52] [*] + Star(*) [51-52] [*] + FromClause [53-185] [from t AS...outer_alias] + TablePathExpression [58-185] [t AS inner_alias...uter_alias] + PathExpression [58-59] [t] + Identifier(t) [58-59] [t] + Alias [61-75] [AS inner_alias] + Identifier(inner_alias) [64-75] [inner_alias] + MatchRecognizeClause [77-185] [match_recognize...outer_alias] + OrderBy [96-106] [ORDER BY a] + OrderingExpression(ASC) [105-106] [a] + PathExpression [105-106] [a] + Identifier(a) [105-106] [a] + SelectList [118-132] [max(A.b) AS m1] + SelectColumn [118-132] [max(A.b) AS m1] + FunctionCall [118-126] [max(A.b)] + PathExpression [118-121] [max] + Identifier(max) [118-121] [max] + PathExpression [122-125] [A.b] + Identifier(A) [122-123] [A] + Identifier(b) [124-125] [b] + Alias [127-132] [AS m1] + Identifier(m1) [130-132] [m1] + RowPatternVariable [145-146] [A] + Identifier(A) [145-146] [A] + SelectList [158-167] [A AS true] + SelectColumn [158-167] [A AS true] + BooleanLiteral(true) [163-167] [true] + Alias [158-162] [A AS] + Identifier(A) [158-159] [A] + Alias [171-185] [AS outer_alias] + Identifier(outer_alias) [174-185] [outer_alias] +-- +SELECT + * +FROM + t AS inner_alias MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) + AS outer_alias +== + +# Does not combine with other table operators +select * from t match_recognize( + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B | C D) E `quoted $`) + DEFINE A AS true +) as outer_alias {{ MATCH_RECOGNIZE | TABLESAMPLE | PIVOT | UNPIVOT | FOR SYSTEM TIME | WITH OFFSET }} +-- +ALTERNATION GROUP: MATCH_RECOGNIZE +-- +ERROR: Syntax error: Expected end of input but got keyword MATCH_RECOGNIZE [at 6:19] +) as outer_alias MATCH_RECOGNIZE + ^ +-- +ALTERNATION GROUP: TABLESAMPLE +-- +ERROR: Syntax error: Unexpected end of statement [at 6:30] +) as outer_alias TABLESAMPLE + ^ +-- +ALTERNATION GROUP: PIVOT +-- +ERROR: Syntax error: Expected end of input but got keyword PIVOT [at 6:19] +) as outer_alias PIVOT + ^ +-- +ALTERNATION GROUP: UNPIVOT +-- +ERROR: Syntax error: Expected end of input but got keyword UNPIVOT [at 6:19] +) as outer_alias UNPIVOT + ^ +-- +ALTERNATION GROUP: FOR SYSTEM TIME +-- +ERROR: Syntax error: Expected end of input but got keyword FOR [at 6:19] +) as outer_alias FOR SYSTEM TIME + ^ +-- +ALTERNATION GROUP: WITH OFFSET +-- +ERROR: Syntax error: Expected end of input but got keyword WITH [at 6:19] +) as outer_alias WITH OFFSET + ^ +== + +# joins +select * from t1 inner join t2 match_recognize( + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B| C D) E `quoted $`) + DEFINE A AS true +) +-- + +QueryStatement [0-171] [select * from...AS true )] + Query [0-171] [select * from...AS true )] + Select [0-171] [select * from...AS true )] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-171] [from t1 inner...AS true )] + Join(INNER) [14-171] [t1 inner join...AS true )] + TablePathExpression [14-16] [t1] + PathExpression [14-16] [t1] + Identifier(t1) [14-16] [t1] + Location [17-27] [inner join] + TablePathExpression [28-171] [t2 match_recogniz...AS true )] + PathExpression [28-30] [t2] + Identifier(t2) [28-30] [t2] + MatchRecognizeClause [31-171] [match_recognize...AS true )] + OrderBy [50-71] [ORDER BY c DESC, f(d)] + OrderingExpression(DESC) [59-65] [c DESC] + PathExpression [59-60] [c] + Identifier(c) [59-60] [c] + OrderingExpression(ASC) [67-71] [f(d)] + FunctionCall [67-71] [f(d)] + PathExpression [67-68] [f] + Identifier(f) [67-68] [f] + PathExpression [69-70] [d] + Identifier(d) [69-70] [d] + SelectList [83-113] [max(A.x) AS m1, min(B.y) AS m2] + SelectColumn [83-97] [max(A.x) AS m1] + FunctionCall [83-91] [max(A.x)] + PathExpression [83-86] [max] + Identifier(max) [83-86] [max] + PathExpression [87-90] [A.x] + Identifier(A) [87-88] [A] + Identifier(x) [89-90] [x] + Alias [92-97] [AS m1] + Identifier(m1) [95-97] [m1] + SelectColumn [99-113] [min(B.y) AS m2] + FunctionCall [99-107] [min(B.y)] + PathExpression [99-102] [min] + Identifier(min) [99-102] [min] + PathExpression [103-106] [B.y] + Identifier(B) [103-104] [B] + Identifier(y) [105-106] [y] + Alias [108-113] [AS m2] + Identifier(m2) [111-113] [m2] + RowPatternOperation [126-149] [(A B| C D) E `quoted $`] + RowPatternOperation [127-135] [A B| C D] + RowPatternOperation [127-130] [A B] + RowPatternVariable [127-128] [A] + Identifier(A) [127-128] [A] + RowPatternVariable [129-130] [B] + Identifier(B) [129-130] [B] + RowPatternOperation [132-135] [C D] + RowPatternVariable [132-133] [C] + Identifier(C) [132-133] [C] + RowPatternVariable [134-135] [D] + Identifier(D) [134-135] [D] + RowPatternVariable [137-138] [E] + Identifier(E) [137-138] [E] + RowPatternVariable [139-149] [`quoted $`] + Identifier(`quoted $`) [139-149] [`quoted $`] + SelectList [160-169] [A AS true] + SelectColumn [160-169] [A AS true] + BooleanLiteral(true) [165-169] [true] + Alias [160-164] [A AS] + Identifier(A) [160-161] [A] +-- +SELECT + * +FROM + t1 + INNER JOIN + t2 MATCH_RECOGNIZE( + ORDER BY c DESC, f(d) + MEASURES + max(A.x) AS m1, + min(B.y) AS m2 + PATTERN ((A B | C D) E `quoted $`) + DEFINE A AS true) +== + +# TVF +SELECT * FROM + tvf() MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) +-- +QueryStatement [0-125] [SELECT * FROM...A AS true)] + Query [0-125] [SELECT * FROM...A AS true)] + Select [0-125] [SELECT * FROM...A AS true)] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-125] [FROM tvf...A AS true)] + TVF [16-125] [tvf() MATCH_RECOG...A AS true)] + PathExpression [16-19] [tvf] + Identifier(tvf) [16-19] [tvf] + MatchRecognizeClause [22-125] [MATCH_RECOGNIZE...A AS true)] + OrderBy [43-53] [ORDER BY a] + OrderingExpression(ASC) [52-53] [a] + PathExpression [52-53] [a] + Identifier(a) [52-53] [a] + SelectList [73-87] [max(A.b) AS m1] + SelectColumn [73-87] [max(A.b) AS m1] + FunctionCall [73-81] [max(A.b)] + PathExpression [73-76] [max] + Identifier(max) [73-76] [max] + PathExpression [77-80] [A.b] + Identifier(A) [77-78] [A] + Identifier(b) [79-80] [b] + Alias [82-87] [AS m1] + Identifier(m1) [85-87] [m1] + RowPatternVariable [101-102] [A] + Identifier(A) [101-102] [A] + SelectList [115-124] [A AS true] + SelectColumn [115-124] [A AS true] + BooleanLiteral(true) [120-124] [true] + Alias [115-119] [A AS] + Identifier(A) [115-116] [A] +-- +SELECT + * +FROM + tvf() MATCH_RECOGNIZE( + ORDER BY a + MEASURES + max(A.b) AS m1 + PATTERN (A) + DEFINE A AS true) +== + +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B | C D) E `quoted $`) + DEFINE A AS x.y, + B AS y IN(f(z), g(z.p)) +) +-- +QueryStatement [0-215] [select * from...g(z.p)) )] + Query [0-215] [select * from...g(z.p)) )] + Select [0-215] [select * from...g(z.p)) )] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-215] [from t match_reco...g(z.p)) )] + TablePathExpression [14-215] [t match_recognize...g(z.p)) )] + PathExpression [14-15] [t] + Identifier(t) [14-15] [t] + MatchRecognizeClause [16-215] [match_recognize...g(z.p)) )] + PartitionBy [35-57] [PARTITION BY a, f.g(b)] + PathExpression [48-49] [a] + Identifier(a) [48-49] [a] + FunctionCall [51-57] [f.g(b)] + PathExpression [51-54] [f.g] + Identifier(f) [51-52] [f] + Identifier(g) [53-54] [g] + PathExpression [55-56] [b] + Identifier(b) [55-56] [b] + OrderBy [60-81] [ORDER BY c DESC, f(d)] + OrderingExpression(DESC) [69-75] [c DESC] + PathExpression [69-70] [c] + Identifier(c) [69-70] [c] + OrderingExpression(ASC) [77-81] [f(d)] + FunctionCall [77-81] [f(d)] + PathExpression [77-78] [f] + Identifier(f) [77-78] [f] + PathExpression [79-80] [d] + Identifier(d) [79-80] [d] + SelectList [93-123] [max(A.x) AS m1, min(B.y) AS m2] + SelectColumn [93-107] [max(A.x) AS m1] + FunctionCall [93-101] [max(A.x)] + PathExpression [93-96] [max] + Identifier(max) [93-96] [max] + PathExpression [97-100] [A.x] + Identifier(A) [97-98] [A] + Identifier(x) [99-100] [x] + Alias [102-107] [AS m1] + Identifier(m1) [105-107] [m1] + SelectColumn [109-123] [min(B.y) AS m2] + FunctionCall [109-117] [min(B.y)] + PathExpression [109-112] [min] + Identifier(min) [109-112] [min] + PathExpression [113-116] [B.y] + Identifier(B) [113-114] [B] + Identifier(y) [115-116] [y] + Alias [118-123] [AS m2] + Identifier(m2) [121-123] [m2] + RowPatternOperation [136-160] [(A B | C D) E `quoted $`] + RowPatternOperation [137-146] [A B | C D] + RowPatternOperation [137-140] [A B] + RowPatternVariable [137-138] [A] + Identifier(A) [137-138] [A] + RowPatternVariable [139-140] [B] + Identifier(B) [139-140] [B] + RowPatternOperation [143-146] [C D] + RowPatternVariable [143-144] [C] + Identifier(C) [143-144] [C] + RowPatternVariable [145-146] [D] + Identifier(D) [145-146] [D] + RowPatternVariable [148-149] [E] + Identifier(E) [148-149] [E] + RowPatternVariable [150-160] [`quoted $`] + Identifier(`quoted $`) [150-160] [`quoted $`] + SelectList [171-213] [A AS x.y,...), g(z.p))] + SelectColumn [171-179] [A AS x.y] + PathExpression [176-179] [x.y] + Identifier(x) [176-177] [x] + Identifier(y) [178-179] [y] + Alias [171-175] [A AS] + Identifier(A) [171-172] [A] + SelectColumn [190-213] [B AS y IN(f(z), g(z.p))] + InExpression(IN) [195-213] [y IN(f(z), g(z.p))] + PathExpression [195-196] [y] + Identifier(y) [195-196] [y] + Location [197-199] [IN] + InList [200-212] [f(z), g(z.p)] + FunctionCall [200-204] [f(z)] + PathExpression [200-201] [f] + Identifier(f) [200-201] [f] + PathExpression [202-203] [z] + Identifier(z) [202-203] [z] + FunctionCall [206-212] [g(z.p)] + PathExpression [206-207] [g] + Identifier(g) [206-207] [g] + PathExpression [208-211] [z.p] + Identifier(z) [208-209] [z] + Identifier(p) [210-211] [p] + Alias [190-194] [B AS] + Identifier(B) [190-191] [B] +-- +SELECT + * +FROM + t MATCH_RECOGNIZE( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES + max(A.x) AS m1, + min(B.y) AS m2 + PATTERN ((A B | C D) E `quoted $`) + DEFINE A AS x.y, B AS y IN (f(z), g(z.p))) +== + +# partition by is optional +select * from t match_recognize( + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B | C D) E `quoted $`) + DEFINE A AS x.y, + B AS y IN(f(z), g(z.p)) +) +-- +QueryStatement [0-190] [select * from...g(z.p)) )] + Query [0-190] [select * from...g(z.p)) )] + Select [0-190] [select * from...g(z.p)) )] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-190] [from t match_reco...g(z.p)) )] + TablePathExpression [14-190] [t match_recognize...g(z.p)) )] + PathExpression [14-15] [t] + Identifier(t) [14-15] [t] + MatchRecognizeClause [16-190] [match_recognize...g(z.p)) )] + OrderBy [35-56] [ORDER BY c DESC, f(d)] + OrderingExpression(DESC) [44-50] [c DESC] + PathExpression [44-45] [c] + Identifier(c) [44-45] [c] + OrderingExpression(ASC) [52-56] [f(d)] + FunctionCall [52-56] [f(d)] + PathExpression [52-53] [f] + Identifier(f) [52-53] [f] + PathExpression [54-55] [d] + Identifier(d) [54-55] [d] + SelectList [68-98] [max(A.x) AS m1, min(B.y) AS m2] + SelectColumn [68-82] [max(A.x) AS m1] + FunctionCall [68-76] [max(A.x)] + PathExpression [68-71] [max] + Identifier(max) [68-71] [max] + PathExpression [72-75] [A.x] + Identifier(A) [72-73] [A] + Identifier(x) [74-75] [x] + Alias [77-82] [AS m1] + Identifier(m1) [80-82] [m1] + SelectColumn [84-98] [min(B.y) AS m2] + FunctionCall [84-92] [min(B.y)] + PathExpression [84-87] [min] + Identifier(min) [84-87] [min] + PathExpression [88-91] [B.y] + Identifier(B) [88-89] [B] + Identifier(y) [90-91] [y] + Alias [93-98] [AS m2] + Identifier(m2) [96-98] [m2] + RowPatternOperation [111-135] [(A B | C D) E `quoted $`] + RowPatternOperation [112-121] [A B | C D] + RowPatternOperation [112-115] [A B] + RowPatternVariable [112-113] [A] + Identifier(A) [112-113] [A] + RowPatternVariable [114-115] [B] + Identifier(B) [114-115] [B] + RowPatternOperation [118-121] [C D] + RowPatternVariable [118-119] [C] + Identifier(C) [118-119] [C] + RowPatternVariable [120-121] [D] + Identifier(D) [120-121] [D] + RowPatternVariable [123-124] [E] + Identifier(E) [123-124] [E] + RowPatternVariable [125-135] [`quoted $`] + Identifier(`quoted $`) [125-135] [`quoted $`] + SelectList [146-188] [A AS x.y,...), g(z.p))] + SelectColumn [146-154] [A AS x.y] + PathExpression [151-154] [x.y] + Identifier(x) [151-152] [x] + Identifier(y) [153-154] [y] + Alias [146-150] [A AS] + Identifier(A) [146-147] [A] + SelectColumn [165-188] [B AS y IN(f(z), g(z.p))] + InExpression(IN) [170-188] [y IN(f(z), g(z.p))] + PathExpression [170-171] [y] + Identifier(y) [170-171] [y] + Location [172-174] [IN] + InList [175-187] [f(z), g(z.p)] + FunctionCall [175-179] [f(z)] + PathExpression [175-176] [f] + Identifier(f) [175-176] [f] + PathExpression [177-178] [z] + Identifier(z) [177-178] [z] + FunctionCall [181-187] [g(z.p)] + PathExpression [181-182] [g] + Identifier(g) [181-182] [g] + PathExpression [183-186] [z.p] + Identifier(z) [183-184] [z] + Identifier(p) [185-186] [p] + Alias [165-169] [B AS] + Identifier(B) [165-166] [B] +-- +SELECT + * +FROM + t MATCH_RECOGNIZE( + ORDER BY c DESC, f(d) + MEASURES + max(A.x) AS m1, + min(B.y) AS m2 + PATTERN ((A B | C D) E `quoted $`) + DEFINE A AS x.y, B AS y IN (f(z), g(z.p))) +== + +# order by is required +select * from t match_recognize( + PARTITION BY a, f.g(b) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B | C D) E `quoted $`) + DEFINE A AS x.y, + B AS y IN(f(z), g(z.p)) +) +-- +ERROR: Syntax error: Expected keyword ORDER but got keyword MEASURES [at 3:3] + MEASURES max(A.x) AS m1, min(B.y) AS m2 + ^ +== + +# measures is required +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + PATTERN ( (A B | C D) E `quoted $`) + DEFINE A AS x.y, + B AS y IN(f(z), g(z.p)) +) +-- +ERROR: Syntax error: Expected keyword MEASURES but got keyword PATTERN [at 4:3] + PATTERN ( (A B | C D) E `quoted $`) + ^ +== + +# pattern is required +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + DEFINE A AS x.y, + B AS y IN(f(z), g(z.p)) +) +-- +ERROR: Syntax error: Expected "," or keyword PATTERN but got keyword DEFINE [at 5:3] + DEFINE A AS x.y, + ^ +== + +# define is required +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B | C D) E `quoted $`) +) +-- +ERROR: Syntax error: Expected keyword DEFINE but got ")" [at 6:1] +) +^ +== + +# Measures requires all its expressions to have AS alias, and no trailing comma +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES {{| , | a | a m1 | a AS m1, }} + PATTERN ( (A B | C D) E `quoted $`) + DEFINE A AS x.y, + B AS y IN(f(z), g(z.p)) +) +-- +ALTERNATION GROUPS: + + a AS m1, +-- +ERROR: Syntax error: Expected "," but got identifier "B" [at 5:16] + PATTERN ( (A B | C D) E `quoted $`) + ^ +-- +ALTERNATION GROUP: , +-- +ERROR: Syntax error: Unexpected "," [at 4:13] + MEASURES , + ^ +-- +ALTERNATION GROUP: a +-- +ERROR: Syntax error: Expected keyword AS but got keyword PATTERN [at 5:3] + PATTERN ( (A B | C D) E `quoted $`) + ^ +-- +ALTERNATION GROUP: a m1 +-- +ERROR: Syntax error: Expected keyword AS but got identifier "m1" [at 4:15] + MEASURES a m1 + ^ +== + +# DEFINE requires AS and does not allow trailing comma +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN ( (A B | C D) E `quoted $`) + DEFINE {{ A | A true | A AS x.y, }}) +-- +ALTERNATION GROUP: A +-- +ERROR: Syntax error: Expected keyword AS but got ")" [at 6:13] + DEFINE A ) + ^ +-- +ALTERNATION GROUP: A true +-- +ERROR: Syntax error: Expected keyword AS but got keyword TRUE [at 6:13] + DEFINE A true ) + ^ +-- +ALTERNATION GROUP: A AS x.y, +-- +ERROR: Syntax error: Unexpected ")" [at 6:21] + DEFINE A AS x.y, ) + ^ +== + +# PATTERN requires parentheses around the pattern expression +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES max(A.x) AS m1, min(B.y) AS m2 + PATTERN A B + DEFINE A AS x.y +) +-- +ERROR: Syntax error: Expected "(" but got identifier "A" [at 5:11] + PATTERN A B + ^ +== + +# MATCH_RECOGNIZE() nested in a subquery inside another MATCH_RECOGNIZE() +select * from t match_recognize( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES max(A.x) + (SELECT * + FROM v MATCH_RECOGNIZE( + ORDER BY x + MEASURES + max(A.col1) - min(B.col2) + outer_a AS inner_m + PATTERN ( A B ) + DEFINE A AS x + ) + ) AS m1 + PATTERN (A B A B ) + DEFINE A AS true +) +-- +QueryStatement [0-493] [select * from...AS true )] + Query [0-493] [select * from...AS true )] + Select [0-493] [select * from...AS true )] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-493] [from t match_reco...AS true )] + TablePathExpression [14-493] [t match_recognize...AS true )] + PathExpression [14-15] [t] + Identifier(t) [14-15] [t] + MatchRecognizeClause [16-493] [match_recognize...AS true )] + PartitionBy [35-57] [PARTITION BY a, f.g(b)] + PathExpression [48-49] [a] + Identifier(a) [48-49] [a] + FunctionCall [51-57] [f.g(b)] + PathExpression [51-54] [f.g] + Identifier(f) [51-52] [f] + Identifier(g) [53-54] [g] + PathExpression [55-56] [b] + Identifier(b) [55-56] [b] + OrderBy [60-81] [ORDER BY c DESC, f(d)] + OrderingExpression(DESC) [69-75] [c DESC] + PathExpression [69-70] [c] + Identifier(c) [69-70] [c] + OrderingExpression(ASC) [77-81] [f(d)] + FunctionCall [77-81] [f(d)] + PathExpression [77-78] [f] + Identifier(f) [77-78] [f] + PathExpression [79-80] [d] + Identifier(d) [79-80] [d] + SelectList [93-451] [max(A.x) +...) AS m1] + SelectColumn [93-451] [max(A.x) +...) AS m1] + BinaryExpression(+) [93-445] [max(A.x) +...)] + FunctionCall [93-101] [max(A.x)] + PathExpression [93-96] [max] + Identifier(max) [93-96] [max] + PathExpression [97-100] [A.x] + Identifier(A) [97-98] [A] + Identifier(x) [99-100] [x] + ExpressionSubquery [104-445] [(SELECT *...)] + Query [105-421] [SELECT *...)] + Select [105-421] [SELECT *...)] + SelectList [112-113] [*] + SelectColumn [112-113] [*] + Star(*) [112-113] [*] + FromClause [139-421] [FROM v MATCH_RECO...)] + TablePathExpression [144-421] [v MATCH_RECOGNIZE...)] + PathExpression [144-145] [v] + Identifier(v) [144-145] [v] + MatchRecognizeClause [146-421] [MATCH_RECOGNIZE...)] + OrderBy [190-200] [ORDER BY x] + OrderingExpression(ASC) [199-200] [x] + PathExpression [199-200] [x] + Identifier(x) [199-200] [x] + SelectList [265-311] [max(A.col1...AS inner_m] + SelectColumn [265-311] [max(A.col1...AS inner_m] + BinaryExpression(+) [265-300] [max(A.col1...+ outer_a] + BinaryExpression(-) [265-290] [max(A.col1) - min(B.col2)] + FunctionCall [265-276] [max(A.col1)] + PathExpression [265-268] [max] + Identifier(max) [265-268] [max] + PathExpression [269-275] [A.col1] + Identifier(A) [269-270] [A] + Identifier(col1) [271-275] [col1] + FunctionCall [279-290] [min(B.col2)] + PathExpression [279-282] [min] + Identifier(min) [279-282] [min] + PathExpression [283-289] [B.col2] + Identifier(B) [283-284] [B] + Identifier(col2) [285-289] [col2] + PathExpression [293-300] [outer_a] + Identifier(outer_a) [293-300] [outer_a] + Alias [301-311] [AS inner_m] + Identifier(inner_m) [304-311] [inner_m] + RowPatternOperation [349-352] [A B] + RowPatternVariable [349-350] [A] + Identifier(A) [349-350] [A] + RowPatternVariable [351-352] [B] + Identifier(B) [351-352] [B] + SelectList [389-395] [A AS x] + SelectColumn [389-395] [A AS x] + PathExpression [394-395] [x] + Identifier(x) [394-395] [x] + Alias [389-393] [A AS] + Identifier(A) [389-390] [A] + Alias [446-451] [AS m1] + Identifier(m1) [449-451] [m1] + RowPatternOperation [463-470] [A B A B] + RowPatternVariable [463-464] [A] + Identifier(A) [463-464] [A] + RowPatternVariable [465-466] [B] + Identifier(B) [465-466] [B] + RowPatternVariable [467-468] [A] + Identifier(A) [467-468] [A] + RowPatternVariable [469-470] [B] + Identifier(B) [469-470] [B] + SelectList [482-491] [A AS true] + SelectColumn [482-491] [A AS true] + BooleanLiteral(true) [487-491] [true] + Alias [482-486] [A AS] + Identifier(A) [482-483] [A] +-- +SELECT + * +FROM + t MATCH_RECOGNIZE( + PARTITION BY a, f.g(b) + ORDER BY c DESC, f(d) + MEASURES + max(A.x) + ( + SELECT + * + FROM + v MATCH_RECOGNIZE( + ORDER BY x + MEASURES + max(A.col1) - min(B.col2) + outer_a AS inner_m + PATTERN (A B) + DEFINE A AS x) + ) AS m1 + PATTERN (A B A B) + DEFINE A AS true) +== diff --git a/zetasql/parser/testdata/orderby.test b/zetasql/parser/testdata/orderby.test index 4fea63182..d8f18b509 100644 --- a/zetasql/parser/testdata/orderby.test +++ b/zetasql/parser/testdata/orderby.test @@ -174,8 +174,8 @@ order by 1 QueryStatement [0-52] [select a from...order by 1] Query [0-52] [select a from...order by 1] SetOperation(UNION ALL) [0-41] [select a from...elect b from t] - SetOperationMetadataList [15-25] [union all] - SetOperationMetadata [15-25] [union all] + SetOperationMetadataList [16-25] [union all] + SetOperationMetadata [16-25] [union all] SetOperationType [16-21] [union] SetOperationAllOrDistinct [22-25] [all] Select [0-15] [select a from t] @@ -229,8 +229,8 @@ order by 3 QueryStatement [0-78] [(select a...order by 3] Query [0-78] [(select a...order by 3] SetOperation(UNION ALL) [0-67] [(select a...order by 2)] - SetOperationMetadataList [28-38] [UNION ALL] - SetOperationMetadata [28-38] [UNION ALL] + SetOperationMetadataList [29-38] [UNION ALL] + SetOperationMetadata [29-38] [UNION ALL] SetOperationType [29-34] [UNION] SetOperationAllOrDistinct [35-38] [ALL] Query [1-27] [select a from t order by 1] diff --git a/zetasql/parser/testdata/parenthesized_query.test b/zetasql/parser/testdata/parenthesized_query.test index 48325d9c9..f3a76b4c8 100644 --- a/zetasql/parser/testdata/parenthesized_query.test +++ b/zetasql/parser/testdata/parenthesized_query.test @@ -101,8 +101,8 @@ ORDER BY 1 DESC) QueryStatement [0-73] [((select 1...select 4)))] Query [1-72] [(select 1)...select 4))] SetOperation(UNION ALL) [1-72] [(select 1)...select 4))] - SetOperationMetadataList [11-21] [union all] - SetOperationMetadata [11-21] [union all] + SetOperationMetadataList [12-21] [union all] + SetOperationMetadata [12-21] [union all] SetOperationType [12-17] [union] SetOperationAllOrDistinct [18-21] [all] Query [2-10] [select 1] @@ -112,8 +112,8 @@ QueryStatement [0-73] [((select 1...select 4)))] IntLiteral(1) [9-10] [1] Query [23-71] [select 2 union...select 4)] SetOperation(UNION ALL) [23-71] [select 2 union...select 4)] - SetOperationMetadataList [31-41] [union all] - SetOperationMetadata [31-41] [union all] + SetOperationMetadataList [32-41] [union all] + SetOperationMetadata [32-41] [union all] SetOperationType [32-37] [union] SetOperationAllOrDistinct [38-41] [all] Select [23-31] [select 2] @@ -122,8 +122,8 @@ QueryStatement [0-73] [((select 1...select 4)))] IntLiteral(2) [30-31] [2] Query [43-70] [select 3 union all select 4] SetOperation(UNION ALL) [43-70] [select 3 union all select 4] - SetOperationMetadataList [51-61] [union all] - SetOperationMetadata [51-61] [union all] + SetOperationMetadataList [52-61] [union all] + SetOperationMetadata [52-61] [union all] SetOperationType [52-57] [union] SetOperationAllOrDistinct [58-61] [all] Select [43-51] [select 3] @@ -157,8 +157,8 @@ limit 10 QueryStatement [0-84] [(select 1...1 limit 10] Query [0-84] [(select 1...1 limit 10] SetOperation(UNION ALL) [0-64] [(select 1...order by 1)] - SetOperationMetadataList [25-35] [union all] - SetOperationMetadata [25-35] [union all] + SetOperationMetadataList [26-35] [union all] + SetOperationMetadata [26-35] [union all] SetOperationType [26-31] [union] SetOperationAllOrDistinct [32-35] [all] Query [1-24] [select 1 from x limit 1] @@ -315,8 +315,8 @@ QueryStatement [0-41] [select ((...select 2))] ExpressionSubquery [8-41] [((select 1...select 2))] Query [9-40] [(select 1)...(select 2)] SetOperation(UNION ALL) [9-40] [(select 1)...(select 2)] - SetOperationMetadataList [19-29] [union all] - SetOperationMetadata [19-29] [union all] + SetOperationMetadataList [20-29] [union all] + SetOperationMetadata [20-29] [union all] SetOperationType [20-25] [union] SetOperationAllOrDistinct [26-29] [all] Query [10-18] [select 1] @@ -349,8 +349,8 @@ QueryStatement [0-46] [select ARRAY...select 2))] ExpressionSubquery(modifier=ARRAY) [7-46] [ARRAY ((select...select 2))] Query [14-45] [(select 1)...(select 2)] SetOperation(UNION ALL) [14-45] [(select 1)...(select 2)] - SetOperationMetadataList [24-34] [union all] - SetOperationMetadata [24-34] [union all] + SetOperationMetadataList [25-34] [union all] + SetOperationMetadata [25-34] [union all] SetOperationType [25-30] [union] SetOperationAllOrDistinct [31-34] [all] Query [15-23] [select 1] @@ -391,8 +391,8 @@ QueryStatement [0-43] [select ((...select 2))] ExpressionSubquery [8-43] [(((select...select 2))] Query [9-42] [((select 1...(select 2)] SetOperation(UNION ALL) [9-42] [((select 1...(select 2)] - SetOperationMetadataList [21-31] [union all] - SetOperationMetadata [21-31] [union all] + SetOperationMetadataList [22-31] [union all] + SetOperationMetadata [22-31] [union all] SetOperationType [22-27] [union] SetOperationAllOrDistinct [28-31] [all] Query [11-19] [select 1] @@ -425,8 +425,8 @@ QueryStatement [0-48] [select ARRAY...select 2))] ExpressionSubquery(modifier=ARRAY) [7-48] [ARRAY (((select...select 2))] Query [14-47] [((select 1...(select 2)] SetOperation(UNION ALL) [14-47] [((select 1...(select 2)] - SetOperationMetadataList [26-36] [union all] - SetOperationMetadata [26-36] [union all] + SetOperationMetadataList [27-36] [union all] + SetOperationMetadata [27-36] [union all] SetOperationType [27-32] [union] SetOperationAllOrDistinct [33-36] [all] Query [16-24] [select 1] @@ -806,8 +806,8 @@ QueryStatement [0-49] [select * from...select 2))] TableSubquery [14-49] [(((select...select 2))] Query [15-48] [((select 1...(select 2)] SetOperation(UNION ALL) [15-48] [((select 1...(select 2)] - SetOperationMetadataList [27-37] [union all] - SetOperationMetadata [27-37] [union all] + SetOperationMetadataList [28-37] [union all] + SetOperationMetadata [28-37] [union all] SetOperationType [28-33] [union] SetOperationAllOrDistinct [34-37] [all] Query [17-25] [select 1] diff --git a/zetasql/parser/testdata/pipe_aggregate.test b/zetasql/parser/testdata/pipe_aggregate.test new file mode 100644 index 000000000..4b8ff0bc3 --- /dev/null +++ b/zetasql/parser/testdata/pipe_aggregate.test @@ -0,0 +1,654 @@ +[default language_features=PIPES] +select 1 x +|> AGGREGATE count(*), sum(x) as xx +-- +QueryStatement [0-46] [select 1 x...sum(x) as xx] + Query [0-46] [select 1 x...sum(x) as xx] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeAggregate [11-46] [|> AGGREGATE...sum(x) as xx] + Select [14-46] [AGGREGATE...sum(x) as xx] + SelectList [24-46] [count(*), sum(x) as xx] + SelectColumn [24-32] [count(*)] + FunctionCall [24-32] [count(*)] + PathExpression [24-29] [count] + Identifier(count) [24-29] [count] + Star(*) [30-31] [*] + SelectColumn [34-46] [sum(x) as xx] + FunctionCall [34-40] [sum(x)] + PathExpression [34-37] [sum] + Identifier(sum) [34-37] [sum] + PathExpression [38-39] [x] + Identifier(x) [38-39] [x] + Alias [41-46] [as xx] + Identifier(xx) [44-46] [xx] +-- +SELECT + 1 AS x +|> AGGREGATE + count(*), + sum(x) AS xx +== + +select 1 +|> AGGREGATE sum(z), 1+count(x+1) GROUP BY x, y+2, 3 +-- +QueryStatement [0-61] [select 1 |...x, y+2, 3] + Query [0-61] [select 1 |...x, y+2, 3] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-61] [|> AGGREGATE...x, y+2, 3] + Select [12-61] [AGGREGATE...x, y+2, 3] + SelectList [22-42] [sum(z), 1+count(x+1)] + SelectColumn [22-28] [sum(z)] + FunctionCall [22-28] [sum(z)] + PathExpression [22-25] [sum] + Identifier(sum) [22-25] [sum] + PathExpression [26-27] [z] + Identifier(z) [26-27] [z] + SelectColumn [30-42] [1+count(x+1)] + BinaryExpression(+) [30-42] [1+count(x+1)] + IntLiteral(1) [30-31] [1] + FunctionCall [32-42] [count(x+1)] + PathExpression [32-37] [count] + Identifier(count) [32-37] [count] + BinaryExpression(+) [38-41] [x+1] + PathExpression [38-39] [x] + Identifier(x) [38-39] [x] + IntLiteral(1) [40-41] [1] + GroupBy [43-61] [GROUP BY x, y+2, 3] + GroupingItem [52-53] [x] + PathExpression [52-53] [x] + Identifier(x) [52-53] [x] + GroupingItem [55-58] [y+2] + BinaryExpression(+) [55-58] [y+2] + PathExpression [55-56] [y] + Identifier(y) [55-56] [y] + IntLiteral(2) [57-58] [2] + GroupingItem [60-61] [3] + IntLiteral(3) [60-61] [3] +-- +SELECT + 1 +|> AGGREGATE + sum(z), + 1 + count(x + 1) + GROUP BY x, y + 2, 3 +== + +# GROUP BY ALL is not allowed in pipe aggregate. +select 1 +|> AGGREGATE COUNT(*) GROUP BY ALL +-- +ERROR: Syntax error: Unexpected keyword ALL [at 2:32] +|> AGGREGATE COUNT(*) GROUP BY ALL + ^ +== + +# Window function parses but should be rejected later. +select 1 +|> AGGREGATE 1, x, sum(y) OVER () +-- +QueryStatement [0-42] [select 1 |...y) OVER ()] + Query [0-42] [select 1 |...y) OVER ()] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-42] [|> AGGREGATE...y) OVER ()] + Select [12-42] [AGGREGATE 1, x, sum(y) OVER ()] + SelectList [22-42] [1, x, sum(y) OVER ()] + SelectColumn [22-23] [1] + IntLiteral(1) [22-23] [1] + SelectColumn [25-26] [x] + PathExpression [25-26] [x] + Identifier(x) [25-26] [x] + SelectColumn [28-42] [sum(y) OVER ()] + AnalyticFunctionCall [28-42] [sum(y) OVER ()] + FunctionCall [28-34] [sum(y)] + PathExpression [28-31] [sum] + Identifier(sum) [28-31] [sum] + PathExpression [32-33] [y] + Identifier(y) [32-33] [y] + WindowSpecification [40-42] [()] +-- +SELECT + 1 +|> AGGREGATE + 1, + x, + sum(y) OVER () +== + +select 1 +|> AGGREGATE y GROUP @{hint=1} BY x,2,3 +-- +QueryStatement [0-48] [select 1 |...} BY x,2,3] + Query [0-48] [select 1 |...} BY x,2,3] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-48] [|> AGGREGATE...} BY x,2,3] + Select [12-48] [AGGREGATE...} BY x,2,3] + SelectList [22-23] [y] + SelectColumn [22-23] [y] + PathExpression [22-23] [y] + Identifier(y) [22-23] [y] + GroupBy [24-48] [GROUP @{hint=1} BY x,2,3] + Hint [30-39] [@{hint=1}] + HintEntry [32-38] [hint=1] + Identifier(hint) [32-36] [hint] + IntLiteral(1) [37-38] [1] + GroupingItem [43-44] [x] + PathExpression [43-44] [x] + Identifier(x) [43-44] [x] + GroupingItem [45-46] [2] + IntLiteral(2) [45-46] [2] + GroupingItem [47-48] [3] + IntLiteral(3) [47-48] [3] +-- +SELECT + 1 +|> AGGREGATE + y + GROUP @{ hint = 1 } BY x, 2, 3 +== + +# GROUP BY () +select 1 +|> AGGREGATE sum(y) GROUP BY () +|> AGGREGATE sum(y) GROUP BY y, (), x +-- +QueryStatement [0-78] [select 1 |...BY y, (), x] + Query [0-78] [select 1 |...BY y, (), x] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-40] [|> AGGREGATE...GROUP BY ()] + Select [12-40] [AGGREGATE sum(y) GROUP BY ()] + SelectList [22-28] [sum(y)] + SelectColumn [22-28] [sum(y)] + FunctionCall [22-28] [sum(y)] + PathExpression [22-25] [sum] + Identifier(sum) [22-25] [sum] + PathExpression [26-27] [y] + Identifier(y) [26-27] [y] + GroupBy [29-40] [GROUP BY ()] + GroupingItem [38-40] [()] + PipeAggregate [41-78] [|> AGGREGATE...BY y, (), x] + Select [44-78] [AGGREGATE...BY y, (), x] + SelectList [54-60] [sum(y)] + SelectColumn [54-60] [sum(y)] + FunctionCall [54-60] [sum(y)] + PathExpression [54-57] [sum] + Identifier(sum) [54-57] [sum] + PathExpression [58-59] [y] + Identifier(y) [58-59] [y] + GroupBy [61-78] [GROUP BY y, (), x] + GroupingItem [70-71] [y] + PathExpression [70-71] [y] + Identifier(y) [70-71] [y] + GroupingItem [73-75] [()] + GroupingItem [77-78] [x] + PathExpression [77-78] [x] + Identifier(x) [77-78] [x] +-- +SELECT + 1 +|> AGGREGATE + sum(y) + GROUP BY () +|> AGGREGATE + sum(y) + GROUP BY y, (), x +== + +# AGGREGATE with empty aggregate list +select 1 +|> AGGREGATE +|> AGGREGATE GROUP BY x +-- +QueryStatement [0-45] [select 1 |...GROUP BY x] + Query [0-45] [select 1 |...GROUP BY x] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-21] [|> AGGREGATE] + Select [12-21] [AGGREGATE] + SelectList [21-21] [] + PipeAggregate [22-45] [|> AGGREGATE GROUP BY x] + Select [25-45] [AGGREGATE GROUP BY x] + SelectList [34-34] [] + GroupBy [35-45] [GROUP BY x] + GroupingItem [44-45] [x] + PathExpression [44-45] [x] + Identifier(x) [44-45] [x] +-- +SELECT + 1 +|> AGGREGATE +|> AGGREGATE + GROUP BY x +== + +select 1 +|> AGGREGATE count(*) GROUP BY ROLLUP(x,y) +|> AGGREGATE GROUP BY GROUPING SETS ((x), (y,z)) +|> AGGREGATE xyz GROUP BY GROUPING SETS(CUBE(x,y), (z,z), ROLLUP(a,b)); +-- +QueryStatement [0-171] [select 1 |...ROLLUP(a,b))] + Query [0-171] [select 1 |...ROLLUP(a,b))] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-51] [|> AGGREGATE...ROLLUP(x,y)] + Select [12-51] [AGGREGATE...ROLLUP(x,y)] + SelectList [22-30] [count(*)] + SelectColumn [22-30] [count(*)] + FunctionCall [22-30] [count(*)] + PathExpression [22-27] [count] + Identifier(count) [22-27] [count] + Star(*) [28-29] [*] + GroupBy [31-51] [GROUP BY ROLLUP(x,y)] + GroupingItem [40-51] [ROLLUP(x,y)] + Rollup [40-51] [ROLLUP(x,y)] + PathExpression [47-48] [x] + Identifier(x) [47-48] [x] + PathExpression [49-50] [y] + Identifier(y) [49-50] [y] + PipeAggregate [52-100] [|> AGGREGATE...x), (y,z))] + Select [55-100] [AGGREGATE...x), (y,z))] + SelectList [64-64] [] + GroupBy [65-100] [GROUP BY GROUPING...x), (y,z))] + GroupingItem [74-100] [GROUPING SETS ((x), (y,z))] + GroupingSetList [74-100] [GROUPING SETS ((x), (y,z))] + GroupingSet [89-92] [(x)] + PathExpression [90-92] [x)] + Identifier(x) [90-91] [x] + GroupingSet [94-99] [(y,z)] + StructConstructorWithParens [94-99] [(y,z)] + PathExpression [95-96] [y] + Identifier(y) [95-96] [y] + PathExpression [97-98] [z] + Identifier(z) [97-98] [z] + PipeAggregate [101-171] [|> AGGREGATE...ROLLUP(a,b))] + Select [104-171] [AGGREGATE...ROLLUP(a,b))] + SelectList [114-117] [xyz] + SelectColumn [114-117] [xyz] + PathExpression [114-117] [xyz] + Identifier(xyz) [114-117] [xyz] + GroupBy [118-171] [GROUP BY GROUPING...LLUP(a,b))] + GroupingItem [127-171] [GROUPING SETS...ROLLUP(a,b))] + GroupingSetList [127-171] [GROUPING SETS...ROLLUP(a,b))] + GroupingSet [141-150] [CUBE(x,y)] + Cube [141-150] [CUBE(x,y)] + PathExpression [146-147] [x] + Identifier(x) [146-147] [x] + PathExpression [148-149] [y] + Identifier(y) [148-149] [y] + GroupingSet [152-157] [(z,z)] + StructConstructorWithParens [152-157] [(z,z)] + PathExpression [153-154] [z] + Identifier(z) [153-154] [z] + PathExpression [155-156] [z] + Identifier(z) [155-156] [z] + GroupingSet [159-170] [ROLLUP(a,b)] + Rollup [159-170] [ROLLUP(a,b)] + PathExpression [166-167] [a] + Identifier(a) [166-167] [a] + PathExpression [168-169] [b] + Identifier(b) [168-169] [b] +-- +SELECT + 1 +|> AGGREGATE + count(*) + GROUP BY ROLLUP(x, y) +|> AGGREGATE + GROUP BY GROUPING SETS((x), (y, z)) +|> AGGREGATE + xyz + GROUP BY GROUPING SETS(CUBE(x, y), (z, z), ROLLUP(a, b)) +== + +# Aggregate list supports aliases with and without AS +select 1 +|> AGGREGATE count(*) as x, count(*) z +-- +QueryStatement [0-47] [select 1 |...count(*) z] + Query [0-47] [select 1 |...count(*) z] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-47] [|> AGGREGATE...count(*) z] + Select [12-47] [AGGREGATE...count(*) z] + SelectList [22-47] [count(*) as x, count(*) z] + SelectColumn [22-35] [count(*) as x] + FunctionCall [22-30] [count(*)] + PathExpression [22-27] [count] + Identifier(count) [22-27] [count] + Star(*) [28-29] [*] + Alias [31-35] [as x] + Identifier(x) [34-35] [x] + SelectColumn [37-47] [count(*) z] + FunctionCall [37-45] [count(*)] + PathExpression [37-42] [count] + Identifier(count) [37-42] [count] + Star(*) [43-44] [*] + Alias [46-47] [z] + Identifier(z) [46-47] [z] +-- +SELECT + 1 +|> AGGREGATE + count(*) AS x, + count(*) AS z +== + +# Trailing commas are allowed in aggregate list. +select 1 +|> AGGREGATE count(*), +|> AGGREGATE count(*), GROUP BY x +-- +QueryStatement [0-65] [select 1 |...GROUP BY x] + Query [0-65] [select 1 |...GROUP BY x] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-31] [|> AGGREGATE count(*),] + Select [12-31] [AGGREGATE count(*),] + SelectList [22-31] [count(*),] + SelectColumn [22-30] [count(*)] + FunctionCall [22-30] [count(*)] + PathExpression [22-27] [count] + Identifier(count) [22-27] [count] + Star(*) [28-29] [*] + PipeAggregate [32-65] [|> AGGREGATE...GROUP BY x] + Select [35-65] [AGGREGATE count(*), GROUP BY x] + SelectList [45-54] [count(*),] + SelectColumn [45-53] [count(*)] + FunctionCall [45-53] [count(*)] + PathExpression [45-50] [count] + Identifier(count) [45-50] [count] + Star(*) [51-52] [*] + GroupBy [55-65] [GROUP BY x] + GroupingItem [64-65] [x] + PathExpression [64-65] [x] + Identifier(x) [64-65] [x] +-- +SELECT + 1 +|> AGGREGATE + count(*) +|> AGGREGATE + count(*) + GROUP BY x +== + +# Trailing commas are allowed in the GROUP BY. +select 1 +|> AGGREGATE GROUP BY x, +|> AGGREGATE GROUP BY x, y, +-- +QueryStatement [0-61] [select 1 |...GROUP BY x, y,] + Query [0-61] [select 1 |...GROUP BY x, y,] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-33] [|> AGGREGATE GROUP BY x,] + Select [12-33] [AGGREGATE GROUP BY x,] + SelectList [21-21] [] + GroupBy [22-33] [GROUP BY x,] + GroupingItem [31-32] [x] + PathExpression [31-32] [x] + Identifier(x) [31-32] [x] + PipeAggregate [34-61] [|> AGGREGATE GROUP BY x, y,] + Select [37-61] [AGGREGATE GROUP BY x, y,] + SelectList [46-46] [] + GroupBy [47-61] [GROUP BY x, y,] + GroupingItem [56-57] [x] + PathExpression [56-57] [x] + Identifier(x) [56-57] [x] + GroupingItem [59-60] [y] + PathExpression [59-60] [y] + Identifier(y) [59-60] [y] +-- +SELECT + 1 +|> AGGREGATE + GROUP BY x +|> AGGREGATE + GROUP BY x, y +== + +select 1 +|> AGGREGATE {{,|,,|sum(x),,}} +-- +ALTERNATION GROUP: , +-- +ERROR: Syntax error: Expected end of input but got "," [at 2:14] +|> AGGREGATE , + ^ +-- +ALTERNATION GROUP: ,, +-- +ERROR: Syntax error: Expected end of input but got "," [at 2:14] +|> AGGREGATE ,, + ^ +-- +ALTERNATION GROUP: sum(x),, +-- +ERROR: Syntax error: Expected end of input but got "," [at 2:21] +|> AGGREGATE sum(x),, + ^ +== + +select 1 +|> AGGREGATE GROUP BY {{,|,,|x,,}} +-- +ALTERNATION GROUP: , +-- +ERROR: Syntax error: Unexpected "," [at 2:23] +|> AGGREGATE GROUP BY , + ^ +-- +ALTERNATION GROUP: ,, +-- +ERROR: Syntax error: Unexpected "," [at 2:23] +|> AGGREGATE GROUP BY ,, + ^ +-- +ALTERNATION GROUP: x,, +-- +ERROR: Syntax error: Expected end of input but got "," [at 2:25] +|> AGGREGATE GROUP BY x,, + ^ +== + +select 1 +|> AGGREGATE * +-- +ERROR: Syntax error: Expected end of input but got "*" [at 2:14] +|> AGGREGATE * + ^ +== + +select 1 +|> AGGREGATE x, s.*, f(y).*, (a+b).* +-- +QueryStatement [0-45] [select 1 |...*, (a+b).*] + Query [0-45] [select 1 |...*, (a+b).*] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-45] [|> AGGREGATE...*, (a+b).*] + Select [12-45] [AGGREGATE...*, (a+b).*] + SelectList [22-45] [x, s.*, f(y).*, (a+b).*] + SelectColumn [22-23] [x] + PathExpression [22-23] [x] + Identifier(x) [22-23] [x] + SelectColumn [25-28] [s.*] + DotStar [25-28] [s.*] + PathExpression [25-26] [s] + Identifier(s) [25-26] [s] + SelectColumn [30-36] [f(y).*] + DotStar [30-36] [f(y).*] + FunctionCall [30-34] [f(y)] + PathExpression [30-31] [f] + Identifier(f) [30-31] [f] + PathExpression [32-33] [y] + Identifier(y) [32-33] [y] + SelectColumn [38-45] [(a+b).*] + DotStar [38-45] [(a+b).*] + BinaryExpression(+) [39-42] [a+b] + PathExpression [39-40] [a] + Identifier(a) [39-40] [a] + PathExpression [41-42] [b] + Identifier(b) [41-42] [b] +-- +SELECT + 1 +|> AGGREGATE + x, + s.*, + f(y).*, + (a + b).* +== + +# Dot-star only works as the outermost suffix operator on an expression. +select 1 +|> AGGREGATE {{f(s.*)|s.*+1|1+s.*|s.*.*}} +-- +ALTERNATION GROUP: f(s.*) +-- +ERROR: Syntax error: Unexpected "*" [at 2:18] +|> AGGREGATE f(s.*) + ^ +-- +ALTERNATION GROUP: s.*+1 +-- +ERROR: Syntax error: Expected end of input but got "+" [at 2:17] +|> AGGREGATE s.*+1 + ^ +-- +ALTERNATION GROUP: 1+s.* +-- +ERROR: Syntax error: Unexpected "*" [at 2:18] +|> AGGREGATE 1+s.* + ^ +-- +ALTERNATION GROUP: s.*.* +-- +ERROR: Syntax error: Expected end of input but got "." [at 2:17] +|> AGGREGATE s.*.* + ^ +== + +# Dot-star is not allowed in the GROUP BY list. +select 1 +|> AGGREGATE GROUP BY s.* +-- +ERROR: Syntax error: Unexpected "*" [at 2:25] +|> AGGREGATE GROUP BY s.* + ^ +== + +# Star is not allowed in the GROUP BY list. +SELECT 1 +|> AGGREGATE COUNT(*) + GROUP BY * +-- +ERROR: Syntax error: Unexpected "*" [at 3:13] + GROUP BY * + ^ +== + +select 1 +|> AGGREGATE x.* except (a,b), + y.* replace (abc as def), + z.* except (a) replace (1+2 as ccc) +-- +QueryStatement [0-127] [select 1 |...+2 as ccc)] + Query [0-127] [select 1 |...+2 as ccc)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-127] [|> AGGREGATE...+2 as ccc)] + Select [12-127] [AGGREGATE...+2 as ccc)] + SelectList [22-127] [x.* except...+2 as ccc)] + SelectColumn [22-38] [x.* except (a,b)] + DotStarWithModifiers [22-38] [x.* except (a,b)] + PathExpression [22-23] [x] + Identifier(x) [22-23] [x] + StarModifiers [26-38] [except (a,b)] + StarExceptList [26-38] [except (a,b)] + Identifier(a) [34-35] [a] + Identifier(b) [36-37] [b] + SelectColumn [53-77] [y.* replace (abc as def)] + DotStarWithModifiers [53-77] [y.* replace (abc as def)] + PathExpression [53-54] [y] + Identifier(y) [53-54] [y] + StarModifiers [57-77] [replace (abc as def)] + StarReplaceItem [66-76] [abc as def] + PathExpression [66-69] [abc] + Identifier(abc) [66-69] [abc] + Identifier(def) [73-76] [def] + SelectColumn [92-127] [z.* except...+2 as ccc)] + DotStarWithModifiers [92-127] [z.* except...+2 as ccc)] + PathExpression [92-93] [z] + Identifier(z) [92-93] [z] + StarModifiers [96-127] [except (a)...+2 as ccc)] + StarExceptList [96-106] [except (a)] + Identifier(a) [104-105] [a] + StarReplaceItem [116-126] [1+2 as ccc] + BinaryExpression(+) [116-119] [1+2] + IntLiteral(1) [116-117] [1] + IntLiteral(2) [118-119] [2] + Identifier(ccc) [123-126] [ccc] +-- +SELECT + 1 +|> AGGREGATE + x.* EXCEPT (a, b), + y.* REPLACE (abc AS def), + z.* EXCEPT (a) REPLACE (1 + 2 AS ccc) +== + +select 1 +|> GROUP BY a, b +-- +ERROR: Syntax error: GROUP BY should be part of a pipe AGGREGATE operator, without a leading pipe character [at 2:4] +|> GROUP BY a, b + ^ +== + +select 1 +|> group +-- +ERROR: Syntax error: GROUP BY should be part of a pipe AGGREGATE operator, without a leading pipe character [at 2:4] +|> group + ^ +== + +select 1 +|> AGGREGATE COUNT(*) +|> GROUP BY x +-- +ERROR: Syntax error: GROUP BY should be part of a pipe AGGREGATE operator, without a leading pipe character [at 3:4] +|> GROUP BY x + ^ diff --git a/zetasql/parser/testdata/pipe_aggregate_group_by_aliases.test b/zetasql/parser/testdata/pipe_aggregate_group_by_aliases.test new file mode 100644 index 000000000..6f15c78e6 --- /dev/null +++ b/zetasql/parser/testdata/pipe_aggregate_group_by_aliases.test @@ -0,0 +1,252 @@ +# This file has tests for GROUP BY with aliases in pipe AGGREGATE. +# After the tests with pipe syntax, it has tests for GROUP BY with aliases in +# regular syntax, with FEATURE_PIPES turned on and off. + +[default language_features=PIPES] +# GROUP BY supports aliases with AS. +select 1 +|> AGGREGATE count(*) as x, count(*) z + GROUP BY x AS y, z, 1 AS one, x+y AS xy +-- +QueryStatement [37-127] [select 1 |...x+y AS xy] + Query [37-127] [select 1 |...x+y AS xy] + Select [37-45] [select 1] + SelectList [44-45] [1] + SelectColumn [44-45] [1] + IntLiteral(1) [44-45] [1] + PipeAggregate [46-127] [|> AGGREGATE...x+y AS xy] + Select [49-127] [AGGREGATE...x+y AS xy] + SelectList [59-84] [count(*) as x, count(*) z] + SelectColumn [59-72] [count(*) as x] + FunctionCall [59-67] [count(*)] + PathExpression [59-64] [count] + Identifier(count) [59-64] [count] + Star(*) [65-66] [*] + Alias [68-72] [as x] + Identifier(x) [71-72] [x] + SelectColumn [74-84] [count(*) z] + FunctionCall [74-82] [count(*)] + PathExpression [74-79] [count] + Identifier(count) [74-79] [count] + Star(*) [80-81] [*] + Alias [83-84] [z] + Identifier(z) [83-84] [z] + GroupBy [88-127] [GROUP BY x...x+y AS xy] + GroupingItem [97-103] [x AS y] + PathExpression [97-98] [x] + Identifier(x) [97-98] [x] + Alias [99-103] [AS y] + Identifier(y) [102-103] [y] + GroupingItem [105-106] [z] + PathExpression [105-106] [z] + Identifier(z) [105-106] [z] + GroupingItem [108-116] [1 AS one] + IntLiteral(1) [108-109] [1] + Alias [110-116] [AS one] + Identifier(one) [113-116] [one] + GroupingItem [118-127] [x+y AS xy] + BinaryExpression(+) [118-121] [x+y] + PathExpression [118-119] [x] + Identifier(x) [118-119] [x] + PathExpression [120-121] [y] + Identifier(y) [120-121] [y] + Alias [122-127] [AS xy] + Identifier(xy) [125-127] [xy] +-- +SELECT + 1 +|> AGGREGATE + count(*) AS x, + count(*) AS z + GROUP BY x AS y, z, 1 AS one, x + y AS xy +== + +# GROUP BY aliases without AS. +select 1 +|> AGGREGATE count(*) as x, count(*) z + GROUP BY x y, x+y xy +-- +ERROR: Syntax error: Expected end of input but got identifier "y" [at 3:15] + GROUP BY x y, x+y xy + ^ +== + +# GROUP BY doesn't support aliases without AS +select 1 +|> AGGREGATE count(*) + GROUP BY x y +-- +ERROR: Syntax error: Expected end of input but got identifier "y" [at 3:15] + GROUP BY x y + ^ +== + +# GROUPING_SETS, etc, don't support aliases. +select 1 +|> AGGREGATE count(*) GROUP BY {{ROLLUP|CUBE|GROUPING SETS}}(x,y) AS z +-- +ALTERNATION GROUP: ROLLUP +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 2:44] +|> AGGREGATE count(*) GROUP BY ROLLUP(x,y) AS z + ^ +-- +ALTERNATION GROUP: CUBE +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 2:42] +|> AGGREGATE count(*) GROUP BY CUBE(x,y) AS z + ^ +-- +ALTERNATION GROUP: GROUPING SETS +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 2:51] +|> AGGREGATE count(*) GROUP BY GROUPING SETS(x,y) AS z + ^ +== + +# GROUP BY () doesn't support aliases. +select 1 +|> AGGREGATE count(*) GROUP BY () AS z +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 2:35] +|> AGGREGATE count(*) GROUP BY () AS z + ^ +== + +# Aliases inside ROLLUP not allowed. +select 1 +|> AGGREGATE count(*) GROUP BY ROLLUP(x, x+1 AS y) +-- +ERROR: Syntax error: Expected ")" or "," but got keyword AS [at 2:46] +|> AGGREGATE count(*) GROUP BY ROLLUP(x, x+1 AS y) + ^ +== + +# Aliases inside GROUPING SETS not allowed. +select 1 +|> AGGREGATE count(*) + GROUP BY GROUPING SETS(x, x+1 AS y, ROLLUP(x AS y), CUBE(z AS zz)) +-- +ERROR: Syntax error: Expected ")" or "," but got keyword AS [at 3:34] + GROUP BY GROUPING SETS(x, x+1 AS y, ROLLUP(x AS y), CUBE(z AS zz)) + ^ +== + +# Now we replicate the same tests as above, in regular SQL syntax, +# with FEATURE_PIPES turned on or off. With the feature off, these are +# rejected immediately in the grammar production. With the feature on, +# one case makes it through the parse, but will be rejected during analysis. +# These tests are also in aggregation.test, without referencing FEATURE_PIPES. +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY x AS y +-- +ALTERNATION GROUP: PIPES +-- +QueryStatement [0-38] [select COUNT...BY x AS y] + Query [0-38] [select COUNT...BY x AS y] + Select [0-38] [select COUNT...BY x AS y] + SelectList [7-15] [COUNT(*)] + SelectColumn [7-15] [COUNT(*)] + FunctionCall [7-15] [COUNT(*)] + PathExpression [7-12] [COUNT] + Identifier(COUNT) [7-12] [COUNT] + Star(*) [13-14] [*] + FromClause [16-22] [from t] + TablePathExpression [21-22] [t] + PathExpression [21-22] [t] + Identifier(t) [21-22] [t] + GroupBy [23-38] [GROUP BY x AS y] + GroupingItem [32-38] [x AS y] + PathExpression [32-33] [x] + Identifier(x) [32-33] [x] + Alias [34-38] [AS y] + Identifier(y) [37-38] [y] +-- +SELECT + COUNT(*) +FROM + t +GROUP BY x AS y +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: GROUP BY does not support aliases [at 3:12] +GROUP BY x AS y + ^ +== + +# GROUP BY aliases without AS. +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY x y +-- +ERROR: Syntax error: Expected end of input but got identifier "y" [at 3:12] +GROUP BY x y + ^ +== + +# GROUPING_SETS, etc, don't support aliases. +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY {{ROLLUP|CUBE|GROUPING SETS}}(x,y) AS z +-- +ALTERNATION GROUPS: + PIPES,ROLLUP + ROLLUP +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 3:22] +GROUP BY ROLLUP(x,y) AS z + ^ +-- +ALTERNATION GROUPS: + PIPES,CUBE + CUBE +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 3:20] +GROUP BY CUBE(x,y) AS z + ^ +-- +ALTERNATION GROUPS: + PIPES,GROUPING SETS + GROUPING SETS +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 3:29] +GROUP BY GROUPING SETS(x,y) AS z + ^ +== + +# GROUP BY () doesn't support aliases. +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY () AS z +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 3:13] +GROUP BY () AS z + ^ +== + +# Aliases inside ROLLUP not allowed. +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY ROLLUP(x, x+1 AS y) +-- +ERROR: Syntax error: Expected ")" or "," but got keyword AS [at 3:24] +GROUP BY ROLLUP(x, x+1 AS y) + ^ +== + +# Aliases inside GROUPING SETS not allowed. +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY GROUPING SETS(x, x+1 AS y, ROLLUP(x AS y), CUBE(z AS zz)) +-- +ERROR: Syntax error: Expected ")" or "," but got keyword AS [at 3:31] +GROUP BY GROUPING SETS(x, x+1 AS y, ROLLUP(x AS y), CUBE(z AS zz)) + ^ diff --git a/zetasql/parser/testdata/pipe_aggregate_with_order.test b/zetasql/parser/testdata/pipe_aggregate_with_order.test new file mode 100644 index 000000000..cd438d752 --- /dev/null +++ b/zetasql/parser/testdata/pipe_aggregate_with_order.test @@ -0,0 +1,820 @@ +# This file has tests for GROUP BY with ordering, using AND ORDER and ASC/DESC. +# After the tests with pipe syntax, it has tests for GROUP BY with aliases in +# regular syntax, with FEATURE_PIPES turned on and off. + +[default language_features=PIPES] +select 1 +|> AGGREGATE + GROUP BY + x, + x+1 ASC, + f() DESC, + z ASC NULLS FIRST, + zz DESC NULLS LAST, + a NULLS FIRST, + aa NULLS LAST +-- +QueryStatement [0-165] [select 1 |...NULLS LAST] + Query [0-165] [select 1 |...NULLS LAST] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-165] [|> AGGREGATE...NULLS LAST] + Select [12-165] [AGGREGATE...NULLS LAST] + SelectList [21-21] [] + GroupBy [25-165] [GROUP BY...NULLS LAST] + GroupingItem [40-41] [x] + PathExpression [40-41] [x] + Identifier(x) [40-41] [x] + GroupingItem [49-56] [x+1 ASC] + BinaryExpression(+) [49-52] [x+1] + PathExpression [49-50] [x] + Identifier(x) [49-50] [x] + IntLiteral(1) [51-52] [1] + GroupingItemOrder(ASC) [53-56] [ASC] + GroupingItem [64-72] [f() DESC] + FunctionCall [64-67] [f()] + PathExpression [64-65] [f] + Identifier(f) [64-65] [f] + GroupingItemOrder(DESC) [68-72] [DESC] + GroupingItem [80-97] [z ASC NULLS FIRST] + PathExpression [80-81] [z] + Identifier(z) [80-81] [z] + GroupingItemOrder(ASC) [82-97] [ASC NULLS FIRST] + NullOrder(NULLS FIRST) [86-97] [NULLS FIRST] + GroupingItem [105-123] [zz DESC NULLS LAST] + PathExpression [105-107] [zz] + Identifier(zz) [105-107] [zz] + GroupingItemOrder(DESC) [108-123] [DESC NULLS LAST] + NullOrder(NULLS LAST) [113-123] [NULLS LAST] + GroupingItem [131-144] [a NULLS FIRST] + PathExpression [131-132] [a] + Identifier(a) [131-132] [a] + GroupingItemOrder(UNSPECIFIED) [133-144] [NULLS FIRST] + NullOrder(NULLS FIRST) [133-144] [NULLS FIRST] + GroupingItem [152-165] [aa NULLS LAST] + PathExpression [152-154] [aa] + Identifier(aa) [152-154] [aa] + GroupingItemOrder(UNSPECIFIED) [155-165] [NULLS LAST] + NullOrder(NULLS LAST) [155-165] [NULLS LAST] +-- +SELECT + 1 +|> AGGREGATE + GROUP BY x, x + 1 ASC, f() DESC, z ASC NULLS FIRST, zz DESC NULLS LAST, a NULLS FIRST, aa NULLS LAST +== + +# NULLS FIRST/LAST are allowed by the parser without ASC/DESC. +select 1 +|> AGGREGATE + GROUP BY x+1 {{NULLS|NULLS FIRST|AS alias NULLS LAST|alias NULLS FIRST}} +-- +ALTERNATION GROUP: NULLS +-- +ERROR: Syntax error: Expected keyword FIRST or keyword LAST but got end of statement [at 3:22] + GROUP BY x+1 NULLS + ^ +-- +ALTERNATION GROUP: NULLS FIRST +-- +QueryStatement [0-49] [select 1 |...NULLS FIRST] + Query [0-49] [select 1 |...NULLS FIRST] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-49] [|> AGGREGATE...NULLS FIRST] + Select [12-49] [AGGREGATE...NULLS FIRST] + SelectList [21-21] [] + GroupBy [25-49] [GROUP BY x+1 NULLS FIRST] + GroupingItem [34-49] [x+1 NULLS FIRST] + BinaryExpression(+) [34-37] [x+1] + PathExpression [34-35] [x] + Identifier(x) [34-35] [x] + IntLiteral(1) [36-37] [1] + GroupingItemOrder(UNSPECIFIED) [38-49] [NULLS FIRST] + NullOrder(NULLS FIRST) [38-49] [NULLS FIRST] +-- +SELECT + 1 +|> AGGREGATE + GROUP BY x + 1 NULLS FIRST +-- +ALTERNATION GROUP: AS alias NULLS LAST +-- +QueryStatement [0-57] [select 1 |...NULLS LAST] + Query [0-57] [select 1 |...NULLS LAST] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-57] [|> AGGREGATE...NULLS LAST] + Select [12-57] [AGGREGATE...NULLS LAST] + SelectList [21-21] [] + GroupBy [25-57] [GROUP BY x...NULLS LAST] + GroupingItem [34-57] [x+1 AS alias NULLS LAST] + BinaryExpression(+) [34-37] [x+1] + PathExpression [34-35] [x] + Identifier(x) [34-35] [x] + IntLiteral(1) [36-37] [1] + Alias [38-46] [AS alias] + Identifier(alias) [41-46] [alias] + GroupingItemOrder(UNSPECIFIED) [47-57] [NULLS LAST] + NullOrder(NULLS LAST) [47-57] [NULLS LAST] +-- +SELECT + 1 +|> AGGREGATE + GROUP BY x + 1 AS alias NULLS LAST +-- +ALTERNATION GROUP: alias NULLS FIRST +-- +ERROR: Syntax error: Expected end of input but got identifier "alias" [at 3:17] + GROUP BY x+1 alias NULLS FIRST + ^ +== + +# COLLATE is not supported in GROUP BY. +from t +|> aggregate count(*) + GROUP {{|AND ORDER}} BY k {{|ASC}} + COLLATE "abc" +-- +ERROR: Syntax error: Expected end of input but got keyword COLLATE [at 4:6] + COLLATE "abc" + ^ +== + +select 1 +|> AGGREGATE 1 GROUP AND ORDER BY x, y DESC, +-- +QueryStatement [0-53] [select 1 |...x, y DESC,] + Query [0-53] [select 1 |...x, y DESC,] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-53] [|> AGGREGATE...x, y DESC,] + Select [12-53] [AGGREGATE...x, y DESC,] + SelectList [22-23] [1] + SelectColumn [22-23] [1] + IntLiteral(1) [22-23] [1] + GroupBy(and_order_by=true) [24-53] [GROUP AND ORDER BY x, y DESC,] + GroupingItem [43-44] [x] + PathExpression [43-44] [x] + Identifier(x) [43-44] [x] + GroupingItem [46-52] [y DESC] + PathExpression [46-47] [y] + Identifier(y) [46-47] [y] + GroupingItemOrder(DESC) [48-52] [DESC] +-- +SELECT + 1 +|> AGGREGATE + 1 + GROUP AND ORDER BY x, y DESC +== + +# Ordering suffixes are not supported on non-expression items. +select 1 +|> AGGREGATE 1 + GROUP BY {{ALL|()|ROLLUP(x)|CUBE(x)|GROUPING SETS(x)}} ASC +-- +ALTERNATION GROUP: ALL +-- +ERROR: Syntax error: Unexpected keyword ALL [at 3:13] + GROUP BY ALL ASC + ^ +-- +ALTERNATION GROUP: () +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:16] + GROUP BY () ASC + ^ +-- +ALTERNATION GROUP: ROLLUP(x) +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:23] + GROUP BY ROLLUP(x) ASC + ^ +-- +ALTERNATION GROUP: CUBE(x) +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:21] + GROUP BY CUBE(x) ASC + ^ +-- +ALTERNATION GROUP: GROUPING SETS(x) +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 3:30] + GROUP BY GROUPING SETS(x) ASC + ^ +== + +# AND ORDER BY will parse for non-expression items, but won't be accepted +# in the analyzer. +select 1 +|> AGGREGATE 1 + GROUP AND ORDER BY {{ALL|(),ROLLUP(x),CUBE(x),GROUPING SETS(x)}} +-- +ALTERNATION GROUP: ALL +-- +ERROR: Syntax error: Unexpected keyword ALL [at 3:23] + GROUP AND ORDER BY ALL + ^ +-- +ALTERNATION GROUP: (),ROLLUP(x),CUBE(x),GROUPING SETS(x) +-- +QueryStatement [0-83] [select 1 |...GROUPING SETS(x)] + Query [0-83] [select 1 |...GROUPING SETS(x)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAggregate [9-83] [|> AGGREGATE...ROUPING SETS(x)] + Select [12-83] [AGGREGATE...GROUPING SETS(x)] + SelectList [22-23] [1] + SelectColumn [22-23] [1] + IntLiteral(1) [22-23] [1] + GroupBy(and_order_by=true) [27-83] [GROUP AND...GROUPING SETS(x)] + GroupingItem [46-48] [()] + GroupingItem [49-58] [ROLLUP(x)] + Rollup [49-58] [ROLLUP(x)] + PathExpression [56-57] [x] + Identifier(x) [56-57] [x] + GroupingItem [59-66] [CUBE(x)] + Cube [59-66] [CUBE(x)] + PathExpression [64-65] [x] + Identifier(x) [64-65] [x] + GroupingItem [67-83] [GROUPING SETS(x)] + GroupingSetList [67-83] [GROUPING SETS(x)] + GroupingSet [81-82] [x] + PathExpression [81-82] [x] + Identifier(x) [81-82] [x] +-- +SELECT + 1 +|> AGGREGATE + 1 + GROUP AND ORDER BY (), ROLLUP(x), CUBE(x), GROUPING SETS(x) +== + +# Ordering modifiers in combination with aliases. +from x +|> AGGREGATE + GROUP AND ORDER BY x+1 AS y ASC +-- +QueryStatement [0-54] [from x |>...1 AS y ASC] + Query [0-54] [from x |>...1 AS y ASC] + FromQuery [0-6] [from x] + FromClause [0-6] [from x] + TablePathExpression [5-6] [x] + PathExpression [5-6] [x] + Identifier(x) [5-6] [x] + PipeAggregate [7-54] [|> AGGREGATE...1 AS y ASC] + Select [10-54] [AGGREGATE...1 AS y ASC] + SelectList [19-19] [] + GroupBy(and_order_by=true) [23-54] [GROUP AND...1 AS y ASC] + GroupingItem [42-54] [x+1 AS y ASC] + BinaryExpression(+) [42-45] [x+1] + PathExpression [42-43] [x] + Identifier(x) [42-43] [x] + IntLiteral(1) [44-45] [1] + Alias [46-50] [AS y] + Identifier(y) [49-50] [y] + GroupingItemOrder(ASC) [51-54] [ASC] +-- +FROM + x +|> AGGREGATE + GROUP AND ORDER BY x + 1 AS y ASC +== + +from x +|> AGGREGATE + GROUP BY x+1 ASC AS y +-- +ERROR: Syntax error: Expected end of input but got keyword AS [at 3:21] + GROUP BY x+1 ASC AS y + ^ +== + +# Now we try using order modifiers in regular GROUP BY syntax, +# with FEATURE_PIPES turned on or off. With PIPES disabled, these are +# rejected immediately in parser. With PIPES enabled, they parse +# but will be rejected during analysis. + +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP BY x {{ASC|DESC|ASC NULLS FIRST|NULLS FIRST}} +-- +ALTERNATION GROUP: PIPES,ASC +-- +QueryStatement [0-37] [select COUNT...GROUP BY x ASC] + Query [0-37] [select COUNT...GROUP BY x ASC] + Select [0-37] [select COUNT...GROUP BY x ASC] + SelectList [7-15] [COUNT(*)] + SelectColumn [7-15] [COUNT(*)] + FunctionCall [7-15] [COUNT(*)] + PathExpression [7-12] [COUNT] + Identifier(COUNT) [7-12] [COUNT] + Star(*) [13-14] [*] + FromClause [16-22] [from t] + TablePathExpression [21-22] [t] + PathExpression [21-22] [t] + Identifier(t) [21-22] [t] + GroupBy [23-37] [GROUP BY x ASC] + GroupingItem [32-37] [x ASC] + PathExpression [32-33] [x] + Identifier(x) [32-33] [x] + GroupingItemOrder(ASC) [34-37] [ASC] +-- +SELECT + COUNT(*) +FROM + t +GROUP BY x ASC +-- +ALTERNATION GROUP: PIPES,DESC +-- +QueryStatement [0-38] [select COUNT...BY x DESC] + Query [0-38] [select COUNT...BY x DESC] + Select [0-38] [select COUNT...BY x DESC] + SelectList [7-15] [COUNT(*)] + SelectColumn [7-15] [COUNT(*)] + FunctionCall [7-15] [COUNT(*)] + PathExpression [7-12] [COUNT] + Identifier(COUNT) [7-12] [COUNT] + Star(*) [13-14] [*] + FromClause [16-22] [from t] + TablePathExpression [21-22] [t] + PathExpression [21-22] [t] + Identifier(t) [21-22] [t] + GroupBy [23-38] [GROUP BY x DESC] + GroupingItem [32-38] [x DESC] + PathExpression [32-33] [x] + Identifier(x) [32-33] [x] + GroupingItemOrder(DESC) [34-38] [DESC] +-- +SELECT + COUNT(*) +FROM + t +GROUP BY x DESC +-- +ALTERNATION GROUP: PIPES,ASC NULLS FIRST +-- +QueryStatement [0-49] [select COUNT...NULLS FIRST] + Query [0-49] [select COUNT...NULLS FIRST] + Select [0-49] [select COUNT...NULLS FIRST] + SelectList [7-15] [COUNT(*)] + SelectColumn [7-15] [COUNT(*)] + FunctionCall [7-15] [COUNT(*)] + PathExpression [7-12] [COUNT] + Identifier(COUNT) [7-12] [COUNT] + Star(*) [13-14] [*] + FromClause [16-22] [from t] + TablePathExpression [21-22] [t] + PathExpression [21-22] [t] + Identifier(t) [21-22] [t] + GroupBy [23-49] [GROUP BY x ASC NULLS FIRST] + GroupingItem [32-49] [x ASC NULLS FIRST] + PathExpression [32-33] [x] + Identifier(x) [32-33] [x] + GroupingItemOrder(ASC) [34-49] [ASC NULLS FIRST] + NullOrder(NULLS FIRST) [38-49] [NULLS FIRST] +-- +SELECT + COUNT(*) +FROM + t +GROUP BY x ASC NULLS FIRST +-- +ALTERNATION GROUP: PIPES,NULLS FIRST +-- +QueryStatement [0-45] [select COUNT...NULLS FIRST] + Query [0-45] [select COUNT...NULLS FIRST] + Select [0-45] [select COUNT...NULLS FIRST] + SelectList [7-15] [COUNT(*)] + SelectColumn [7-15] [COUNT(*)] + FunctionCall [7-15] [COUNT(*)] + PathExpression [7-12] [COUNT] + Identifier(COUNT) [7-12] [COUNT] + Star(*) [13-14] [*] + FromClause [16-22] [from t] + TablePathExpression [21-22] [t] + PathExpression [21-22] [t] + Identifier(t) [21-22] [t] + GroupBy [23-45] [GROUP BY x NULLS FIRST] + GroupingItem [32-45] [x NULLS FIRST] + PathExpression [32-33] [x] + Identifier(x) [32-33] [x] + GroupingItemOrder(UNSPECIFIED) [34-45] [NULLS FIRST] + NullOrder(NULLS FIRST) [34-45] [NULLS FIRST] +-- +SELECT + COUNT(*) +FROM + t +GROUP BY x NULLS FIRST +-- +ALTERNATION GROUP: ASC +-- +ERROR: Syntax error: Unexpected ASC [at 3:12] +GROUP BY x ASC + ^ +-- +ALTERNATION GROUP: DESC +-- +ERROR: Syntax error: Unexpected DESC [at 3:12] +GROUP BY x DESC + ^ +-- +ALTERNATION GROUP: ASC NULLS FIRST +-- +ERROR: Syntax error: Unexpected ASC [at 3:12] +GROUP BY x ASC NULLS FIRST + ^ +-- +ALTERNATION GROUP: NULLS FIRST +-- +ERROR: Syntax error: Unexpected NULLS [at 3:12] +GROUP BY x NULLS FIRST + ^ +== + +[language_features={{PIPES|}}] +select COUNT(*) +from t +GROUP AND ORDER BY x +-- +ALTERNATION GROUP: PIPES +-- +QueryStatement [0-43] [select COUNT...ORDER BY x] + Query [0-43] [select COUNT...ORDER BY x] + Select [0-43] [select COUNT...ORDER BY x] + SelectList [7-15] [COUNT(*)] + SelectColumn [7-15] [COUNT(*)] + FunctionCall [7-15] [COUNT(*)] + PathExpression [7-12] [COUNT] + Identifier(COUNT) [7-12] [COUNT] + Star(*) [13-14] [*] + FromClause [16-22] [from t] + TablePathExpression [21-22] [t] + PathExpression [21-22] [t] + Identifier(t) [21-22] [t] + GroupBy(and_order_by=true) [23-43] [GROUP AND ORDER BY x] + GroupingItem [42-43] [x] + PathExpression [42-43] [x] + Identifier(x) [42-43] [x] +-- +SELECT + COUNT(*) +FROM + t +GROUP AND ORDER BY x +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Unexpected AND [at 3:7] +GROUP AND ORDER BY x + ^ +== + +# Order suffixes in the aggregate list. +from t +|> aggregate count(*) ASC, + sum(x) alias DESC, + avg(y) AS alias ASC NULLS FIRST, + max(distinct z) DESC NULLS LAST, + nosuffix() +-- +QueryStatement [0-181] [from t |>...nosuffix()] + Query [0-181] [from t |>...nosuffix()] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAggregate [7-181] [|> aggregate...nosuffix()] + Select [10-181] [aggregate...nosuffix()] + SelectList [20-181] [count(*) ASC...nosuffix()] + SelectColumn [20-32] [count(*) ASC] + FunctionCall [20-28] [count(*)] + PathExpression [20-25] [count] + Identifier(count) [20-25] [count] + Star(*) [26-27] [*] + GroupingItemOrder(ASC) [29-32] [ASC] + SelectColumn [47-64] [sum(x) alias DESC] + FunctionCall [47-53] [sum(x)] + PathExpression [47-50] [sum] + Identifier(sum) [47-50] [sum] + PathExpression [51-52] [x] + Identifier(x) [51-52] [x] + Alias [54-59] [alias] + Identifier(alias) [54-59] [alias] + GroupingItemOrder(DESC) [60-64] [DESC] + SelectColumn [79-110] [avg(y) AS...NULLS FIRST] + FunctionCall [79-85] [avg(y)] + PathExpression [79-82] [avg] + Identifier(avg) [79-82] [avg] + PathExpression [83-84] [y] + Identifier(y) [83-84] [y] + Alias [86-94] [AS alias] + Identifier(alias) [89-94] [alias] + GroupingItemOrder(ASC) [95-110] [ASC NULLS FIRST] + NullOrder(NULLS FIRST) [99-110] [NULLS FIRST] + SelectColumn [125-156] [max(distinct...NULLS LAST] + FunctionCall(distinct=true) [125-140] [max(distinct z)] + PathExpression [125-128] [max] + Identifier(max) [125-128] [max] + PathExpression [138-139] [z] + Identifier(z) [138-139] [z] + GroupingItemOrder(DESC) [141-156] [DESC NULLS LAST] + NullOrder(NULLS LAST) [146-156] [NULLS LAST] + SelectColumn [171-181] [nosuffix()] + FunctionCall [171-181] [nosuffix()] + PathExpression [171-179] [nosuffix] + Identifier(nosuffix) [171-179] [nosuffix] +-- +FROM + t +|> AGGREGATE + count(*) ASC, + sum(x) AS alias DESC, + avg(y) AS alias ASC NULLS FIRST, + max(DISTINCT z) DESC NULLS LAST, + nosuffix() +== + +# Order suffixes are independent of ordering modifiers on the GROUP BY. +from t +|> aggregate count(*) asc + group {{|and order}} by key{{| asc}} +-- +ALTERNATION GROUP: +-- +QueryStatement [0-49] [from t |>...group by key] + Query [0-49] [from t |>...group by key] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAggregate [7-49] [|> aggregate...group by key] + Select [10-49] [aggregate...group by key] + SelectList [20-32] [count(*) asc] + SelectColumn [20-32] [count(*) asc] + FunctionCall [20-28] [count(*)] + PathExpression [20-25] [count] + Identifier(count) [20-25] [count] + Star(*) [26-27] [*] + GroupingItemOrder(ASC) [29-32] [asc] + GroupBy [36-49] [group by key] + GroupingItem [46-49] [key] + PathExpression [46-49] [key] + Identifier(key) [46-49] [key] +-- +FROM + t +|> AGGREGATE + count(*) ASC + GROUP BY key +-- +ALTERNATION GROUP: asc +-- +QueryStatement [0-53] [from t |>...by key asc] + Query [0-53] [from t |>...by key asc] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAggregate [7-53] [|> aggregate...by key asc] + Select [10-53] [aggregate...by key asc] + SelectList [20-32] [count(*) asc] + SelectColumn [20-32] [count(*) asc] + FunctionCall [20-28] [count(*)] + PathExpression [20-25] [count] + Identifier(count) [20-25] [count] + Star(*) [26-27] [*] + GroupingItemOrder(ASC) [29-32] [asc] + GroupBy [36-53] [group by key asc] + GroupingItem [46-53] [key asc] + PathExpression [46-49] [key] + Identifier(key) [46-49] [key] + GroupingItemOrder(ASC) [50-53] [asc] +-- +FROM + t +|> AGGREGATE + count(*) ASC + GROUP BY key ASC +-- +ALTERNATION GROUP: and order, +-- +QueryStatement [0-58] [from t |>...order by key] + Query [0-58] [from t |>...order by key] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAggregate [7-58] [|> aggregate...order by key] + Select [10-58] [aggregate...order by key] + SelectList [20-32] [count(*) asc] + SelectColumn [20-32] [count(*) asc] + FunctionCall [20-28] [count(*)] + PathExpression [20-25] [count] + Identifier(count) [20-25] [count] + Star(*) [26-27] [*] + GroupingItemOrder(ASC) [29-32] [asc] + GroupBy(and_order_by=true) [36-58] [group and order by key] + GroupingItem [55-58] [key] + PathExpression [55-58] [key] + Identifier(key) [55-58] [key] +-- +FROM + t +|> AGGREGATE + count(*) ASC + GROUP AND ORDER BY key +-- +ALTERNATION GROUP: and order, asc +-- +QueryStatement [0-62] [from t |>...by key asc] + Query [0-62] [from t |>...by key asc] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAggregate [7-62] [|> aggregate...by key asc] + Select [10-62] [aggregate...by key asc] + SelectList [20-32] [count(*) asc] + SelectColumn [20-32] [count(*) asc] + FunctionCall [20-28] [count(*)] + PathExpression [20-25] [count] + Identifier(count) [20-25] [count] + Star(*) [26-27] [*] + GroupingItemOrder(ASC) [29-32] [asc] + GroupBy(and_order_by=true) [36-62] [group and order by key asc] + GroupingItem [55-62] [key asc] + PathExpression [55-58] [key] + Identifier(key) [55-58] [key] + GroupingItemOrder(ASC) [59-62] [asc] +-- +FROM + t +|> AGGREGATE + count(*) ASC + GROUP AND ORDER BY key ASC +== + +# Order suffixes don't parse in the selection list for other pipe operators. +from t +|> {{select|extend|window}} xyz ASC +-- +ALTERNATION GROUP: select +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 2:15] +|> select xyz ASC + ^ +-- +ALTERNATION GROUP: extend +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 2:15] +|> extend xyz ASC + ^ +-- +ALTERNATION GROUP: window +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 2:15] +|> window xyz ASC + ^ +== + +# Order suffixes don't parse in the selection list for other pipe operators, +# with aliases. +from t +|> extend xyz {{|alias|AS alias}} ASC +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 2:16] +|> extend xyz ASC + ^ +-- +ALTERNATION GROUP: alias +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 2:21] +|> extend xyz alias ASC + ^ +-- +ALTERNATION GROUP: AS alias +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 2:24] +|> extend xyz AS alias ASC + ^ +== + +# Order suffxies don't parse in standard SELECT. +select xyz {{|alias|AS alias}} ASC +from t +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 1:13] +select xyz ASC + ^ +-- +ALTERNATION GROUP: alias +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 1:18] +select xyz alias ASC + ^ +-- +ALTERNATION GROUP: AS alias +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 1:21] +select xyz AS alias ASC + ^ +== + +# ASC or DESC is required before NULLS in the AGGREGATE list. +from t +|> aggregate count(*) {{nulls first|alias null last|AS alias nulls first|nulls}} +-- +ALTERNATION GROUP: nulls first +-- +ERROR: Syntax error: Expected end of input but got keyword NULLS [at 2:23] +|> aggregate count(*) nulls first + ^ +-- +ALTERNATION GROUP: alias null last +-- +ERROR: Syntax error: Expected end of input but got keyword NULL [at 2:29] +|> aggregate count(*) alias null last + ^ +-- +ALTERNATION GROUP: AS alias nulls first +-- +ERROR: Syntax error: Expected end of input but got keyword NULLS [at 2:32] +|> aggregate count(*) AS alias nulls first + ^ +-- +ALTERNATION GROUP: nulls +-- +ERROR: Syntax error: Expected end of input but got keyword NULLS [at 2:23] +|> aggregate count(*) nulls + ^ +== + +# COLLATE is not supported on AGGREGATE items. +from t +|> aggregate count(*) {{|ASC}} + COLLATE "abc" +-- +ERROR: Syntax error: Expected end of input but got keyword COLLATE [at 3:27] + COLLATE "abc" + ^ +== + +# ASC/DESC modifiers don't work with star or dot-star, with or +# without modifiers. +select 1 +|> AGGREGATE {{*|s.*}} + {{|EXCEPT(x)|REPLACE(x AS y)}} + {{ASC|DESC}} +-- +ALTERNATION GROUPS: + *,,ASC + *,,DESC + *,EXCEPT(x),ASC + *,EXCEPT(x),DESC + *,REPLACE(x AS y),ASC + *,REPLACE(x AS y),DESC +-- +ERROR: Syntax error: Expected end of input but got "*" [at 2:14] +|> AGGREGATE * + ^ +-- +ALTERNATION GROUPS: + s.*,,ASC + s.*,EXCEPT(x),ASC + s.*,REPLACE(x AS y),ASC +-- +ERROR: Syntax error: Expected end of input but got keyword ASC [at 4:6] + ASC + ^ +-- +ALTERNATION GROUPS: + s.*,,DESC + s.*,EXCEPT(x),DESC + s.*,REPLACE(x AS y),DESC +-- +ERROR: Syntax error: Expected end of input but got keyword DESC [at 4:6] + DESC + ^ diff --git a/zetasql/parser/testdata/pipe_as.test b/zetasql/parser/testdata/pipe_as.test new file mode 100644 index 000000000..aa4d4e382 --- /dev/null +++ b/zetasql/parser/testdata/pipe_as.test @@ -0,0 +1,155 @@ +[default language_features=PIPES] + +SELECT 1 |> AS t +-- +QueryStatement [0-16] [SELECT 1 |> AS t] + Query [0-16] [SELECT 1 |> AS t] + Select [0-8] [SELECT 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAs [9-16] [|> AS t] + Alias [15-16] [t] + Identifier(t) [15-16] [t] +-- +SELECT + 1 +|> AS t +== + +SELECT 1 x |> AS t +-- +QueryStatement [0-18] [SELECT 1 x |> AS t] + Query [0-18] [SELECT 1 x |> AS t] + Select [0-10] [SELECT 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeAs [11-18] [|> AS t] + Alias [17-18] [t] + Identifier(t) [17-18] [t] +-- +SELECT + 1 AS x +|> AS t +== + +SELECT 1 |> AS a.b +-- +ERROR: Syntax error: Expected end of input but got "." [at 1:17] +SELECT 1 |> AS a.b + ^ +== + +SELECT 1 |> AS; +-- +ERROR: Syntax error: Unexpected ";" [at 1:15] +SELECT 1 |> AS; + ^ +== + +SELECT 1 |> @{hint=1} AS t; +-- +ERROR: Syntax error: Expected keyword JOIN but got "@" [at 1:13] +SELECT 1 |> @{hint=1} AS t; + ^ +== + +SELECT 1 |> AS @{hint=1} t; +-- +ERROR: Syntax error: Unexpected "@" [at 1:16] +SELECT 1 |> AS @{hint=1} t; + ^ +== + +SELECT 1 |> AS t1 |> AS t2; +-- +QueryStatement [0-26] [SELECT 1 |> AS t1 |> AS t2] + Query [0-26] [SELECT 1 |> AS t1 |> AS t2] + Select [0-8] [SELECT 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeAs [9-17] [|> AS t1] + Alias [15-17] [t1] + Identifier(t1) [15-17] [t1] + PipeAs [18-26] [|> AS t2] + Alias [24-26] [t2] + Identifier(t2) [24-26] [t2] +-- +SELECT + 1 +|> AS t1 +|> AS t2 +== + +# This case is syntactically correct but semantically wrong, since "t" is in the +# inner scope, won't be visible to the follow up pipe select. +FROM (SELECT 1 AS a |> AS t) +|> SELECT t.a +-- +QueryStatement [0-42] [FROM (SELECT...SELECT t.a] + Query [0-42] [FROM (SELECT...SELECT t.a] + FromQuery [0-28] [FROM (SELECT 1 AS a |> AS t)] + FromClause [0-28] [FROM (SELECT 1 AS a |> AS t)] + TableSubquery [5-28] [(SELECT 1 AS a |> AS t)] + Query [6-27] [SELECT 1 AS a |> AS t] + Select [6-19] [SELECT 1 AS a] + SelectList [13-19] [1 AS a] + SelectColumn [13-19] [1 AS a] + IntLiteral(1) [13-14] [1] + Alias [15-19] [AS a] + Identifier(a) [18-19] [a] + PipeAs [20-27] [|> AS t] + Alias [26-27] [t] + Identifier(t) [26-27] [t] + PipeSelect [29-42] [|> SELECT t.a] + Select [32-42] [SELECT t.a] + SelectList [39-42] [t.a] + SelectColumn [39-42] [t.a] + PathExpression [39-42] [t.a] + Identifier(t) [39-40] [t] + Identifier(a) [41-42] [a] +-- +FROM + ( + SELECT + 1 AS a + |> AS t + ) +|> SELECT + t.a +== + +FROM t1 +|> JOIN t2 USING(key) +|> AS t3 +-- +QueryStatement [0-38] [FROM t1 |>...) |> AS t3] + Query [0-38] [FROM t1 |>...) |> AS t3] + FromQuery [0-7] [FROM t1] + FromClause [0-7] [FROM t1] + TablePathExpression [5-7] [t1] + PathExpression [5-7] [t1] + Identifier(t1) [5-7] [t1] + PipeJoin [8-29] [|> JOIN t2 USING(key)] + Join [10-29] [JOIN t2 USING(key)] + PipeJoinLhsPlaceholder [11-29] [JOIN t2 USING(key)] + Location [11-15] [JOIN] + TablePathExpression [16-18] [t2] + PathExpression [16-18] [t2] + Identifier(t2) [16-18] [t2] + UsingClause [19-29] [USING(key)] + Identifier(key) [25-28] [key] + PipeAs [30-38] [|> AS t3] + Alias [36-38] [t3] + Identifier(t3) [36-38] [t3] +-- +FROM + t1 +|> JOIN + t2 + USING(key) +|> AS t3 diff --git a/zetasql/parser/testdata/pipe_assert.test b/zetasql/parser/testdata/pipe_assert.test new file mode 100644 index 000000000..08a7647cd --- /dev/null +++ b/zetasql/parser/testdata/pipe_assert.test @@ -0,0 +1,126 @@ +[default language_features=PIPES] + +from t +|> assert x+1 = 5 +|> assert (select true) +|> assert false, 'hello' +|> assert x=y, concat(a,b), 17, "xxxx" +-- +QueryStatement [0-112] [from t |>...17, "xxxx"] + Query [0-112] [from t |>...17, "xxxx"] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAssert [7-24] [|> assert x+1 = 5] + BinaryExpression(=) [17-24] [x+1 = 5] + BinaryExpression(+) [17-20] [x+1] + PathExpression [17-18] [x] + Identifier(x) [17-18] [x] + IntLiteral(1) [19-20] [1] + IntLiteral(5) [23-24] [5] + PipeAssert [25-48] [|> assert (select true)] + ExpressionSubquery [35-48] [(select true)] + Query [36-47] [select true] + Select [36-47] [select true] + SelectList [43-47] [true] + SelectColumn [43-47] [true] + BooleanLiteral(true) [43-47] [true] + PipeAssert [49-73] [|> assert false, 'hello'] + BooleanLiteral(false) [59-64] [false] + StringLiteral [66-73] ['hello'] + StringLiteralComponent('hello') [66-73] ['hello'] + PipeAssert [74-112] [|> assert...17, "xxxx"] + BinaryExpression(=) [84-87] [x=y] + PathExpression [84-85] [x] + Identifier(x) [84-85] [x] + PathExpression [86-87] [y] + Identifier(y) [86-87] [y] + FunctionCall [89-100] [concat(a,b)] + PathExpression [89-95] [concat] + Identifier(concat) [89-95] [concat] + PathExpression [96-97] [a] + Identifier(a) [96-97] [a] + PathExpression [98-99] [b] + Identifier(b) [98-99] [b] + IntLiteral(17) [102-104] [17] + StringLiteral [106-112] ["xxxx"] + StringLiteralComponent("xxxx") [106-112] ["xxxx"] +-- +FROM + t +|> ASSERT x + 1 = 5 +|> ASSERT( + SELECT + true + ) +|> ASSERT false, 'hello' +|> ASSERT x = y, concat(a, b), 17, "xxxx" +== + +from t +|> @{hint=1} assert true +-- +ERROR: Syntax error: Expected keyword JOIN but got "@" [at 2:4] +|> @{hint=1} assert true + ^ +== + +from t +|> assert +-- +ERROR: Syntax error: Unexpected end of statement [at 2:10] +|> assert + ^ +== + +from t +|> assert x, +|> assert x,y, +|> assert x,y,z, +-- +QueryStatement [0-51] [from t |>...assert x,y,z,] + Query [0-51] [from t |>...assert x,y,z,] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeAssert [7-19] [|> assert x,] + PathExpression [17-18] [x] + Identifier(x) [17-18] [x] + PipeAssert [20-34] [|> assert x,y,] + PathExpression [30-31] [x] + Identifier(x) [30-31] [x] + PathExpression [32-33] [y] + Identifier(y) [32-33] [y] + PipeAssert [35-51] [|> assert x,y,z,] + PathExpression [45-46] [x] + Identifier(x) [45-46] [x] + PathExpression [47-48] [y] + Identifier(y) [47-48] [y] + PathExpression [49-50] [z] + Identifier(z) [49-50] [z] +-- +FROM + t +|> ASSERT x +|> ASSERT x, y +|> ASSERT x, y, z +== + +from t +|> assert , +-- +ERROR: Syntax error: Unexpected "," [at 2:11] +|> assert , + ^ +== + +from t +|> assert true,, +-- +ERROR: Syntax error: Expected end of input but got "," [at 2:16] +|> assert true,, + ^ diff --git a/zetasql/parser/testdata/pipe_call_tvf.test b/zetasql/parser/testdata/pipe_call_tvf.test new file mode 100644 index 000000000..5c1e2b764 --- /dev/null +++ b/zetasql/parser/testdata/pipe_call_tvf.test @@ -0,0 +1,295 @@ +# All supported ASTTVF argument forms work. +select 1 +|> call f() +|> call a.b.c(1, x.y, g(), named=>5) +|> call f(TABLE t, (select 1)) +|> call f(DESCRIPTOR(col)) +|> call f(MODEL m, CONNECTION c) +-- +QueryStatement [0-148] [select 1 |...CONNECTION c)] + Query [0-148] [select 1 |...CONNECTION c)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeCall [9-20] [|> call f()] + TVF [17-20] [f()] + PathExpression [17-18] [f] + Identifier(f) [17-18] [f] + PipeCall [21-57] [|> call a....named=>5)] + TVF [29-57] [a.b.c(1, x.y, g(), named=>5)] + PathExpression [29-34] [a.b.c] + Identifier(a) [29-30] [a] + Identifier(b) [31-32] [b] + Identifier(c) [33-34] [c] + TVFArgument [35-36] [1] + IntLiteral(1) [35-36] [1] + TVFArgument [38-41] [x.y] + PathExpression [38-41] [x.y] + Identifier(x) [38-39] [x] + Identifier(y) [40-41] [y] + TVFArgument [43-46] [g()] + FunctionCall [43-46] [g()] + PathExpression [43-44] [g] + Identifier(g) [43-44] [g] + TVFArgument [48-56] [named=>5] + NamedArgument [48-56] [named=>5] + Identifier(named) [48-53] [named] + IntLiteral(5) [55-56] [5] + PipeCall [58-88] [|> call f(TABLE t, (select 1))] + TVF [66-88] [f(TABLE t, (select 1))] + PathExpression [66-67] [f] + Identifier(f) [66-67] [f] + TVFArgument [68-75] [TABLE t] + TableClause [68-75] [TABLE t] + PathExpression [74-75] [t] + Identifier(t) [74-75] [t] + TVFArgument [77-87] [(select 1)] + ExpressionSubquery [77-87] [(select 1)] + Query [78-86] [select 1] + Select [78-86] [select 1] + SelectList [85-86] [1] + SelectColumn [85-86] [1] + IntLiteral(1) [85-86] [1] + PipeCall [89-115] [|> call f(DESCRIPTOR(col))] + TVF [97-115] [f(DESCRIPTOR(col))] + PathExpression [97-98] [f] + Identifier(f) [97-98] [f] + TVFArgument [99-114] [DESCRIPTOR(col)] + Descriptor [99-114] [DESCRIPTOR(col)] + DescriptorColumnList [110-113] [col] + DescriptorColumn [110-113] [col] + Identifier(col) [110-113] [col] + PipeCall [116-148] [|> call f(...CONNECTION c)] + TVF [124-148] [f(MODEL m, CONNECTION c)] + PathExpression [124-125] [f] + Identifier(f) [124-125] [f] + TVFArgument [126-133] [MODEL m] + ModelClause [126-133] [MODEL m] + PathExpression [132-133] [m] + Identifier(m) [132-133] [m] + TVFArgument [135-147] [CONNECTION c] + ConnectionClause [135-147] [CONNECTION c] + PathExpression [146-147] [c] + Identifier(c) [146-147] [c] +-- +SELECT + 1 +|> CALL f() +|> CALL a.b.c(1, x.y, g(), named => 5) +|> CALL f(TABLE t, ( + SELECT + 1 + )) +|> CALL f(DESCRIPTOR(col)) +|> CALL f(MODEL m, CONNECTION c) +== + +select 1 +|> call f() +|> call f(MODEL m, CONNECTION DEFAULT) +-- +QueryStatement [0-59] [select 1 |...NNECTION DEFAULT)] + Query [0-59] [select 1 |...NNECTION DEFAULT)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeCall [9-20] [|> call f()] + TVF [17-20] [f()] + PathExpression [17-18] [f] + Identifier(f) [17-18] [f] + PipeCall [21-59] [|> call f(...NNECTION DEFAULT)] + TVF [29-59] [f(MODEL m, CONNECTION DEFAULT)] + PathExpression [29-30] [f] + Identifier(f) [29-30] [f] + TVFArgument [31-38] [MODEL m] + ModelClause [31-38] [MODEL m] + PathExpression [37-38] [m] + Identifier(m) [37-38] [m] + TVFArgument [40-58] [CONNECTION DEFAULT] + ConnectionClause [40-58] [CONNECTION DEFAULT] + DefaultLiteral [51-58] [DEFAULT] +-- +SELECT + 1 +|> CALL f() +|> CALL f(MODEL m, CONNECTION DEFAULT) +== + +# TVF hints work. +select 1 +|> call f() @{hint=1} +-- +QueryStatement [0-30] [select 1 |> call f() @{hint=1}] + Query [0-30] [select 1 |> call f() @{hint=1}] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeCall [9-30] [|> call f() @{hint=1}] + TVF [17-30] [f() @{hint=1}] + PathExpression [17-18] [f] + Identifier(f) [17-18] [f] + Hint [21-30] [@{hint=1}] + HintEntry [23-29] [hint=1] + Identifier(hint) [23-27] [hint] + IntLiteral(1) [28-29] [1] +-- +SELECT + 1 +|> CALL f() @{ hint = 1 } +== + +select 1 +|> call @{hint=1} f() +-- +ERROR: Syntax error: Unexpected "@" [at 2:9] +|> call @{hint=1} f() + ^ +== + +# Helper errors from TVFs work here too. +select 1 +|> call f(select 5) +-- +ERROR: Syntax error: Each subquery argument for table-valued function calls must be enclosed in parentheses. To fix this, replace SELECT... with (SELECT...) [at 2:11] +|> call f(select 5) + ^ +== + +select 1 +|> call(*) +-- +ERROR: Syntax error: Unexpected "(" [at 2:8] +|> call(*) + ^ +== + +select 1 +|> call +-- +ERROR: Syntax error: Unexpected end of statement [at 2:8] +|> call + ^ +== + +select 1 +|> call f(), g() +-- +ERROR: Syntax error: Unexpected "," [at 2:12] +|> call f(), g() + ^ +== + +select 1 +|> call f(), +-- +ERROR: Syntax error: Unexpected "," [at 2:12] +|> call f(), + ^ +== + +select 1 +|> call f()+1 +-- +ERROR: Syntax error: Unexpected "+" [at 2:12] +|> call f()+1 + ^ +== + +# Aliases, with or without hints. +select 1 +|> call f() AS x +|> call g() y +|> call h() @{hint=1} AS z +|> call i() @{hint=2} zz +-- +QueryStatement [0-91] [select 1 |...hint=2} zz] + Query [0-91] [select 1 |...hint=2} zz] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeCall [9-25] [|> call f() AS x] + TVF [17-25] [f() AS x] + PathExpression [17-18] [f] + Identifier(f) [17-18] [f] + Alias [21-25] [AS x] + Identifier(x) [24-25] [x] + PipeCall [26-39] [|> call g() y] + TVF [34-39] [g() y] + PathExpression [34-35] [g] + Identifier(g) [34-35] [g] + Alias [38-39] [y] + Identifier(y) [38-39] [y] + PipeCall [40-66] [|> call h() @{hint=1} AS z] + TVF [48-66] [h() @{hint=1} AS z] + PathExpression [48-49] [h] + Identifier(h) [48-49] [h] + Hint [52-61] [@{hint=1}] + HintEntry [54-60] [hint=1] + Identifier(hint) [54-58] [hint] + IntLiteral(1) [59-60] [1] + Alias [62-66] [AS z] + Identifier(z) [65-66] [z] + PipeCall [67-91] [|> call i() @{hint=2} zz] + TVF [75-91] [i() @{hint=2} zz] + PathExpression [75-76] [i] + Identifier(i) [75-76] [i] + Hint [79-88] [@{hint=2}] + HintEntry [81-87] [hint=2] + Identifier(hint) [81-85] [hint] + IntLiteral(2) [86-87] [2] + Alias [89-91] [zz] + Identifier(zz) [89-91] [zz] +-- +SELECT + 1 +|> CALL f() AS x +|> CALL g() AS y +|> CALL h() @{ hint = 1 } AS z +|> CALL i() @{ hint = 2 } AS zz +== + +select 1 +|> call f() OVER () +-- +ERROR: Syntax error: OVER keyword must follow a function call [at 2:13] +|> call f() OVER () + ^ +== + +# TVF TABLESAMPLE suffix is allowed in FROM but not here. +select 1 +|> call f() TABLESAMPLE bernoulli(5 rows) +-- +ERROR: Syntax error: Unexpected keyword TABLESAMPLE [at 2:13] +|> call f() TABLESAMPLE bernoulli(5 rows) + ^ +== + +# TVF PIVOT suffix is allowed in FROM but not here. +select 1 +|> call f() PIVOT(x FOR y IN (5)) +-- +ERROR: Syntax error: Expected end of input but got "(" [at 2:18] +|> call f() PIVOT(x FOR y IN (5)) + ^ +== + +# Extra modifiers from non-TVF function calls don't work. +select 1 +|> call f(x LIMIT 5) +-- +ERROR: Syntax error: Expected ")" or "," but got keyword LIMIT [at 2:13] +|> call f(x LIMIT 5) + ^ +== + +select 1 +|> call f(x ORDER BY y) +-- +ERROR: Syntax error: Expected ")" or "," but got keyword ORDER [at 2:13] +|> call f(x ORDER BY y) + ^ diff --git a/zetasql/parser/testdata/pipe_distinct.test b/zetasql/parser/testdata/pipe_distinct.test new file mode 100644 index 000000000..2db024d5f --- /dev/null +++ b/zetasql/parser/testdata/pipe_distinct.test @@ -0,0 +1,97 @@ +SELECT 1 x +|> DISTINCT +-- +QueryStatement [0-22] [SELECT 1 x |> DISTINCT] + Query [0-22] [SELECT 1 x |> DISTINCT] + Select [0-10] [SELECT 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeDistinct [11-22] [|> DISTINCT] +-- +SELECT + 1 AS x +|> DISTINCT +== + +SELECT 1 x +|> DISTINCT * +-- +ERROR: Syntax error: Expected end of input but got "*" [at 2:13] +|> DISTINCT * + ^ +== + +# "DISTINCT" as the start of a query is not allowed. +distinct +|> select *; +-- +ERROR: Syntax error: Unexpected keyword DISTINCT [at 1:1] +distinct +^ +== + +CREATE TEMP TABLE t +AS (SELECT 1 AS a |> DISTINCT); +-- +CreateTableStatement(is_temp) [0-50] [CREATE TEMP...DISTINCT)] + PathExpression [18-19] [t] + Identifier(t) [18-19] [t] + Query [24-49] [SELECT 1 AS a |> DISTINCT] + Select [24-37] [SELECT 1 AS a] + SelectList [31-37] [1 AS a] + SelectColumn [31-37] [1 AS a] + IntLiteral(1) [31-32] [1] + Alias [33-37] [AS a] + Identifier(a) [36-37] [a] + PipeDistinct [38-49] [|> DISTINCT] +-- +CREATE TEMP TABLE t AS +( +SELECT + 1 AS a +|> DISTINCT) +== + +SELECT (SELECT 1 AS a |> DISTINCT); +-- +QueryStatement [0-34] [SELECT (SELECT...DISTINCT)] + Query [0-34] [SELECT (SELECT...DISTINCT)] + Select [0-34] [SELECT (SELECT...DISTINCT)] + SelectList [7-34] [(SELECT 1 AS a |> DISTINCT)] + SelectColumn [7-34] [(SELECT 1 AS a |> DISTINCT)] + ExpressionSubquery [7-34] [(SELECT 1 AS a |> DISTINCT)] + Query [8-33] [SELECT 1 AS a |> DISTINCT] + Select [8-21] [SELECT 1 AS a] + SelectList [15-21] [1 AS a] + SelectColumn [15-21] [1 AS a] + IntLiteral(1) [15-16] [1] + Alias [17-21] [AS a] + Identifier(a) [20-21] [a] + PipeDistinct [22-33] [|> DISTINCT] +-- +SELECT + ( + SELECT + 1 AS a + |> DISTINCT) +== + +SELECT 1 +|> @{hint=1} DISTINCT +-- +ERROR: Syntax error: Expected keyword JOIN but got "@" [at 2:4] +|> @{hint=1} DISTINCT + ^ + +== + +SELECT 1 +|> DISTINCT @{hint=1} +-- +ERROR: Syntax error: Expected end of input but got "@" [at 2:13] +|> DISTINCT @{hint=1} + ^ + diff --git a/zetasql/parser/testdata/pipe_drop.test b/zetasql/parser/testdata/pipe_drop.test new file mode 100644 index 000000000..cfbbb95ff --- /dev/null +++ b/zetasql/parser/testdata/pipe_drop.test @@ -0,0 +1,76 @@ +select x +|> drop a +|> drop a, +|> drop a,b,c +|> drop a,b,c, +-- +QueryStatement [0-58] [select x |...drop a,b,c,] + Query [0-58] [select x |...drop a,b,c,] + Select [0-8] [select x] + SelectList [7-8] [x] + SelectColumn [7-8] [x] + PathExpression [7-8] [x] + Identifier(x) [7-8] [x] + PipeDrop [9-18] [|> drop a] + IdentifierList [17-18] [a] + Identifier(a) [17-18] [a] + PipeDrop [19-29] [|> drop a,] + IdentifierList [27-28] [a] + Identifier(a) [27-28] [a] + PipeDrop [30-43] [|> drop a,b,c] + IdentifierList [38-43] [a,b,c] + Identifier(a) [38-39] [a] + Identifier(b) [40-41] [b] + Identifier(c) [42-43] [c] + PipeDrop [44-58] [|> drop a,b,c,] + IdentifierList [52-57] [a,b,c] + Identifier(a) [52-53] [a] + Identifier(b) [54-55] [b] + Identifier(c) [56-57] [c] +-- +SELECT + x +|> DROP a +|> DROP a +|> DROP a, b, c +|> DROP a, b, c +== + +select x +|> drop +-- +ERROR: Syntax error: Unexpected end of statement [at 2:8] +|> drop + ^ +== + +select x +|> drop , +-- +ERROR: Syntax error: Unexpected "," [at 2:9] +|> drop , + ^ +== + +select x +|> drop t.x +-- +ERROR: Syntax error: Pipe DROP can only drop columns by name alone; Dropping columns under table aliases or fields under paths is not supported [at 2:9] +|> drop t.x + ^ +== + +select x +|> drop a, b, c.d.e +-- +ERROR: Syntax error: Pipe DROP can only drop columns by name alone; Dropping columns under table aliases or fields under paths is not supported [at 2:15] +|> drop a, b, c.d.e + ^ +== + +select x +|> drop a, b, f(c) +-- +ERROR: Syntax error: Expected end of input but got "(" [at 2:16] +|> drop a, b, f(c) + ^ diff --git a/zetasql/parser/testdata/pipe_from_query.test b/zetasql/parser/testdata/pipe_from_query.test new file mode 100644 index 000000000..f09a7f7ac --- /dev/null +++ b/zetasql/parser/testdata/pipe_from_query.test @@ -0,0 +1,666 @@ +# We give parse errors as early as possible for queries +# that start with FROM, rather than waiting for the analyzer. +from t +-- +ERROR: Syntax error: Unexpected FROM [at 1:1] +from t +^ +== + +select (from TestTable) +-- +ERROR: Syntax error: Unexpected FROM [at 1:9] +select (from TestTable) + ^ +== + +select EXISTS(from TestTable) +-- +ERROR: Syntax error: Unexpected FROM [at 1:15] +select EXISTS(from TestTable) + ^ +== + +select ARRAY(from TestTable) +-- +ERROR: Syntax error: Unexpected FROM [at 1:14] +select ARRAY(from TestTable) + ^ +== + +WITH q1 AS (select * from TestTable) +FROM q1 +-- +ERROR: Syntax error: Unexpected FROM [at 2:1] +FROM q1 +^ +== + +# These two queries show a leakage where we get an error about bad syntax +# inside the FROM before we get the error about FROM not being allowed. +# This is hard to avoid. +from 123 +-- +ERROR: Syntax error: Unexpected integer literal "123" [at 1:6] +from 123 + ^ +== + +from t1, (select bad syntax here) +-- +ERROR: Syntax error: Expected ")" but got identifier "here" [at 1:29] +from t1, (select bad syntax here) + ^ +== + +[default language_features=PIPES] +from t +-- +QueryStatement [0-6] [from t] + Query [0-6] [from t] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] +-- +FROM + t +== + +from unnest([1]) x +-- +QueryStatement [0-18] [from unnest([1]) x] + Query [0-18] [from unnest([1]) x] + FromQuery [0-18] [from unnest([1]) x] + FromClause [0-18] [from unnest([1]) x] + TablePathExpression [5-18] [unnest([1]) x] + UnnestExpression [5-16] [unnest([1])] + ExpressionWithOptAlias [12-15] [[1]] + ArrayConstructor [12-15] [[1]] + IntLiteral(1) [13-14] [1] + Alias [17-18] [x] + Identifier(x) [17-18] [x] +-- +FROM + UNNEST(ARRAY[1]) AS x +== + +from t1, t2 JOIN t3 using (x), unnest(4) with offset o +-- +QueryStatement [0-54] [from t1, t2...with offset o] + Query [0-54] [from t1, t2...with offset o] + FromQuery [0-54] [from t1, t2...with offset o] + FromClause [0-54] [from t1, t2...with offset o] + Join(COMMA) [5-54] [t1, t2 JOIN...with offset o] + Join [5-29] [t1, t2 JOIN t3 using (x)] + Join(COMMA) [5-11] [t1, t2] + TablePathExpression [5-7] [t1] + PathExpression [5-7] [t1] + Identifier(t1) [5-7] [t1] + Location [7-8] [,] + TablePathExpression [9-11] [t2] + PathExpression [9-11] [t2] + Identifier(t2) [9-11] [t2] + Location [12-16] [JOIN] + TablePathExpression [17-19] [t3] + PathExpression [17-19] [t3] + Identifier(t3) [17-19] [t3] + UsingClause [20-29] [using (x)] + Identifier(x) [27-28] [x] + Location [29-30] [,] + TablePathExpression [31-54] [unnest(4) with offset o] + UnnestExpression [31-40] [unnest(4)] + ExpressionWithOptAlias [38-39] [4] + IntLiteral(4) [38-39] [4] + WithOffset [41-54] [with offset o] + Alias [53-54] [o] + Identifier(o) [53-54] [o] +-- +FROM + t1, + t2 + JOIN + t3 + USING(x), + UNNEST(4) WITH OFFSET AS o +== + +from t +select 1 +-- +ERROR: Syntax error: SELECT not supported after FROM query; Consider using pipe operator `|> SELECT` [at 2:1] +select 1 +^ +== + +# Test the errors for other operators not allowed after FROM queries. +# Common ones give a suggestion to use the pipe operator. +[language_features={{|PIPES}}] +from t +{{where true|group by 1|having x|qualify y|offset 10}} +-- +ALTERNATION GROUPS: + where true + group by 1 + having x + qualify y + offset 10 +-- +ERROR: Syntax error: Unexpected FROM [at 1:1] +from t +^ +-- +ALTERNATION GROUP: PIPES,where true +-- +ERROR: Syntax error: WHERE not supported after FROM query; Consider using pipe operator `|> WHERE` [at 2:1] +where true +^ +-- +ALTERNATION GROUP: PIPES,group by 1 +-- +ERROR: Syntax error: GROUP BY not supported after FROM query; Consider using pipe operator `|> AGGREGATE` [at 2:1] +group by 1 +^ +-- +ALTERNATION GROUP: PIPES,having x +-- +ERROR: Syntax error: Expected end of input but got keyword HAVING [at 2:1] +having x +^ +-- +ALTERNATION GROUP: PIPES,qualify y +-- +ERROR: Syntax error: Expected end of input but got keyword QUALIFY [at 2:1] +qualify y +^ +-- +ALTERNATION GROUP: PIPES,offset 10 +-- +ERROR: Syntax error: Expected end of input but got integer literal "10" [at 2:8] +offset 10 + ^ +== + +# LIMIT, ORDER BY and set operations are not allowed when starting from a +# standalone FROM. The FROM clause is a whole query, not a query_primary. +# These suggest using the pipe operator, or parenthesizing, which also works. +[language_features={{|PIPES}}] +from t +{{limit 5|order by x|union all from t2|intersect|except distinct abc}} +-- +ALTERNATION GROUPS: + limit 5 + order by x + union all from t2 + intersect + except distinct abc +-- +ERROR: Syntax error: Unexpected FROM [at 1:1] +from t +^ +-- +ALTERNATION GROUP: PIPES,limit 5 +-- +ERROR: Syntax error: LIMIT not supported after FROM query; Consider using pipe operator `|> LIMIT` or parentheses around the FROM query [at 2:1] +limit 5 +^ +-- +ALTERNATION GROUP: PIPES,order by x +-- +ERROR: Syntax error: ORDER BY not supported after FROM query; Consider using pipe operator `|> ORDER BY` or parentheses around the FROM query [at 2:1] +order by x +^ +-- +ALTERNATION GROUP: PIPES,union all from t2 +-- +ERROR: Syntax error: UNION not supported after FROM query; Consider using pipe operator `|> UNION` or parentheses around the FROM query [at 2:1] +union all from t2 +^ +-- +ALTERNATION GROUP: PIPES,intersect +-- +ERROR: Syntax error: INTERSECT not supported after FROM query; Consider using pipe operator `|> INTERSECT` or parentheses around the FROM query [at 2:1] +intersect +^ +-- +ALTERNATION GROUP: PIPES,except distinct abc +-- +ERROR: Syntax error: EXCEPT not supported after FROM query; Consider using pipe operator `|> EXCEPT` or parentheses around the FROM query [at 2:1] +except distinct abc +^ +== + +# EXCEPT has some special case handling that makes it +# behave slightly differently from UNION on some cases. +from t +{{except|union}} +from t2 +-- +ALTERNATION GROUP: except +-- +ERROR: EXCEPT must be followed by ALL, DISTINCT, or "(" [at 2:1] +except +^ +-- +ALTERNATION GROUP: union +-- +ERROR: Syntax error: UNION not supported after FROM query; Consider using pipe operator `|> UNION` or parentheses around the FROM query [at 2:1] +union +^ +== + +# Helpful error for FROM after a set op. +[language_features={{|PIPES}}] +select 1 +union all +from t2 +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Unexpected FROM [at 3:1] +from t2 +^ +-- +ALTERNATION GROUP: PIPES +-- +ERROR: Syntax error: Unexpected FROM; FROM queries following a set operation must be parenthesized [at 3:1] +from t2 +^ +== + +# Helpful error for FROM after a set op. Separate parser rule for second +# vs third or later input query. +[language_features={{|PIPES}}] +select 1 +intersect all +select 2 +except all +from t3 +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Unexpected FROM [at 5:1] +from t3 +^ +-- +ALTERNATION GROUP: PIPES +-- +ERROR: Syntax error: Unexpected FROM; FROM queries following a set operation must be parenthesized [at 5:1] +from t3 +^ +== + +(from t) union all (from t intersect all from t2) +-- +ERROR: Syntax error: INTERSECT not supported after FROM query; Consider using pipe operator `|> INTERSECT` or parentheses around the FROM query [at 1:28] +(from t) union all (from t intersect all from t2) + ^ +== + +(from t) join (from t2) using (x) +-- +ERROR: Syntax error: Expected end of input but got keyword JOIN [at 1:10] +(from t) join (from t2) using (x) + ^ +== + +select * +from + (from t) join (from t2) using (x) +-- +QueryStatement [0-49] [select * from...using (x)] + Query [0-49] [select * from...using (x)] + Select [0-49] [select * from...using (x)] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-49] [from (from...using (x)] + Join [16-49] [(from t) join...using (x)] + TableSubquery [16-24] [(from t)] + Query [17-23] [from t] + FromQuery [17-23] [from t] + FromClause [17-23] [from t] + TablePathExpression [22-23] [t] + PathExpression [22-23] [t] + Identifier(t) [22-23] [t] + Location [25-29] [join] + TableSubquery [30-39] [(from t2)] + Query [31-38] [from t2] + FromQuery [31-38] [from t2] + FromClause [31-38] [from t2] + TablePathExpression [36-38] [t2] + PathExpression [36-38] [t2] + Identifier(t2) [36-38] [t2] + UsingClause [40-49] [using (x)] + Identifier(x) [47-48] [x] +-- +SELECT + * +FROM + ( + FROM + t + ) + JOIN + ( + FROM + t2 + ) + USING(x) +== + +from (select * from t) +-- +QueryStatement [0-22] [from (select * from t)] + Query [0-22] [from (select * from t)] + FromQuery [0-22] [from (select * from t)] + FromClause [0-22] [from (select * from t)] + TableSubquery [5-22] [(select * from t)] + Query [6-21] [select * from t] + Select [6-21] [select * from t] + SelectList [13-14] [*] + SelectColumn [13-14] [*] + Star(*) [13-14] [*] + FromClause [15-21] [from t] + TablePathExpression [20-21] [t] + PathExpression [20-21] [t] + Identifier(t) [20-21] [t] +-- +FROM + ( + SELECT + * + FROM + t + ) +== + +select (from t), EXISTS(from t1), ARRAY(from t2) +from (from t) +-- +QueryStatement [0-62] [select (from...from (from t)] + Query [0-62] [select (from...from (from t)] + Select [0-62] [select (from...from (from t)] + SelectList [7-48] [(from t),...ARRAY(from t2)] + SelectColumn [7-15] [(from t)] + ExpressionSubquery [7-15] [(from t)] + Query [8-14] [from t] + FromQuery [8-14] [from t] + FromClause [8-14] [from t] + TablePathExpression [13-14] [t] + PathExpression [13-14] [t] + Identifier(t) [13-14] [t] + SelectColumn [17-32] [EXISTS(from t1)] + ExpressionSubquery(modifier=EXISTS) [17-32] [EXISTS(from t1)] + Query [24-31] [from t1] + FromQuery [24-31] [from t1] + FromClause [24-31] [from t1] + TablePathExpression [29-31] [t1] + PathExpression [29-31] [t1] + Identifier(t1) [29-31] [t1] + SelectColumn [34-48] [ARRAY(from t2)] + ExpressionSubquery(modifier=ARRAY) [34-48] [ARRAY(from t2)] + Query [40-47] [from t2] + FromQuery [40-47] [from t2] + FromClause [40-47] [from t2] + TablePathExpression [45-47] [t2] + PathExpression [45-47] [t2] + Identifier(t2) [45-47] [t2] + FromClause [49-62] [from (from t)] + TableSubquery [54-62] [(from t)] + Query [55-61] [from t] + FromQuery [55-61] [from t] + FromClause [55-61] [from t] + TablePathExpression [60-61] [t] + PathExpression [60-61] [t] + Identifier(t) [60-61] [t] +-- +SELECT + ( + FROM + t), + EXISTS( + FROM + t1), + ARRAY( + FROM + t2) +FROM + ( + FROM + t + ) +== + +from tvf(1,2) +-- +QueryStatement [0-13] [from tvf(1,2)] + Query [0-13] [from tvf(1,2)] + FromQuery [0-13] [from tvf(1,2)] + FromClause [0-13] [from tvf(1,2)] + TVF [5-13] [tvf(1,2)] + PathExpression [5-8] [tvf] + Identifier(tvf) [5-8] [tvf] + TVFArgument [9-10] [1] + IntLiteral(1) [9-10] [1] + TVFArgument [11-12] [2] + IntLiteral(2) [11-12] [2] +-- +FROM + tvf(1, 2) +== + +select * from tvf((from t)) +-- +QueryStatement [0-27] [select * from tvf((from t))] + Query [0-27] [select * from tvf((from t))] + Select [0-27] [select * from tvf((from t))] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-27] [from tvf((from t))] + TVF [14-27] [tvf((from t))] + PathExpression [14-17] [tvf] + Identifier(tvf) [14-17] [tvf] + TVFArgument [18-26] [(from t)] + ExpressionSubquery [18-26] [(from t)] + Query [19-25] [from t] + FromQuery [19-25] [from t] + FromClause [19-25] [from t] + TablePathExpression [24-25] [t] + PathExpression [24-25] [t] + Identifier(t) [24-25] [t] +-- +SELECT + * +FROM + tvf(( + FROM + t)) +== + +select * from tvf(from t) +-- +ERROR: Syntax error: Unexpected keyword FROM [at 1:19] +select * from tvf(from t) + ^ +== + +WITH q1 AS (from t) +from t2 +-- +QueryStatement [0-27] [WITH q1 AS (from t) from t2] + Query [0-27] [WITH q1 AS (from t) from t2] + WithClause [0-19] [WITH q1 AS (from t)] + AliasedQuery [5-19] [q1 AS (from t)] + Identifier(q1) [5-7] [q1] + Query [12-18] [from t] + FromQuery [12-18] [from t] + FromClause [12-18] [from t] + TablePathExpression [17-18] [t] + PathExpression [17-18] [t] + Identifier(t) [17-18] [t] + FromQuery [20-27] [from t2] + FromClause [20-27] [from t2] + TablePathExpression [25-27] [t2] + PathExpression [25-27] [t2] + Identifier(t2) [25-27] [t2] +-- +WITH + q1 AS ( + FROM + t + ) +FROM + t2 +== + +WITH q1 AS (from t1,t2) +from t3,t4 +limit 10 +-- +ERROR: Syntax error: LIMIT not supported after FROM query; Consider using pipe operator `|> LIMIT` or parentheses around the FROM query [at 3:1] +limit 10 +^ +== + +# Test the special case error for trailing comma in the WITH clause. +WITH q1 AS (from t), +{{from t2|select 5}} +-- +ALTERNATION GROUP: from t2 +-- +ERROR: Syntax error: Trailing comma after the WITH clause before the main query is not allowed [at 2:1] +from t2 +^ +-- +ALTERNATION GROUP: select 5 +-- +ERROR: Syntax error: Trailing comma after the WITH clause before the main query is not allowed [at 2:1] +select 5 +^ +== + +with q1 as (from t |> where true) +from q1 +|> where false +-- +QueryStatement [0-56] [with q1 as...where false] + Query [0-56] [with q1 as...where false] + WithClause [0-33] [with q1 as...where true)] + AliasedQuery [5-33] [q1 as (from t |> where true)] + Identifier(q1) [5-7] [q1] + Query [12-32] [from t |> where true] + FromQuery [12-18] [from t] + FromClause [12-18] [from t] + TablePathExpression [17-18] [t] + PathExpression [17-18] [t] + Identifier(t) [17-18] [t] + PipeWhere [19-32] [|> where true] + WhereClause [22-32] [where true] + BooleanLiteral(true) [28-32] [true] + FromQuery [34-41] [from q1] + FromClause [34-41] [from q1] + TablePathExpression [39-41] [q1] + PathExpression [39-41] [q1] + Identifier(q1) [39-41] [q1] + PipeWhere [42-56] [|> where false] + WhereClause [45-56] [where false] + BooleanLiteral(false) [51-56] [false] +-- +WITH + q1 AS ( + FROM + t + |> WHERE + true + ) +FROM + q1 +|> WHERE + false +== + +# Test other postfix table operators allowed in the FROM clause, +# mostly from `table_path_expression`. +from + t1 @{hint=1}, + t2 PIVOT(SUM(a) FOR b IN (0, 1)), + t3 UNPIVOT (a FOR c IN (x, y)), + t4 WITH OFFSET AS o + FOR SYSTEM TIME AS OF y + TABLESAMPLE abc(5 rows) +-- +QueryStatement [0-170] [from t1...abc(5 rows)] + Query [0-170] [from t1...abc(5 rows)] + FromQuery [0-170] [from t1...abc(5 rows)] + FromClause [0-170] [from t1...abc(5 rows)] + Join(COMMA) [7-170] [t1 @{hint=...abc(5 rows)] + Join(COMMA) [7-89] [t1 @{hint=...IN (x, y))] + Join(COMMA) [7-55] [t1 @{hint=...IN (0, 1))] + TablePathExpression [7-19] [t1 @{hint=1}] + PathExpression [7-9] [t1] + Identifier(t1) [7-9] [t1] + Hint [10-19] [@{hint=1}] + HintEntry [12-18] [hint=1] + Identifier(hint) [12-16] [hint] + IntLiteral(1) [17-18] [1] + Location [19-20] [,] + TablePathExpression [23-55] [t2 PIVOT(SUM...IN (0, 1))] + PathExpression [23-25] [t2] + Identifier(t2) [23-25] [t2] + PivotClause [26-55] [PIVOT(SUM(a) FOR b IN (0, 1))] + PivotExpressionList [32-38] [SUM(a)] + PivotExpression [32-38] [SUM(a)] + FunctionCall [32-38] [SUM(a)] + PathExpression [32-35] [SUM] + Identifier(SUM) [32-35] [SUM] + PathExpression [36-37] [a] + Identifier(a) [36-37] [a] + PathExpression [43-44] [b] + Identifier(b) [43-44] [b] + PivotValueList [49-53] [0, 1] + PivotValue [49-50] [0] + IntLiteral(0) [49-50] [0] + PivotValue [52-53] [1] + IntLiteral(1) [52-53] [1] + Location [55-56] [,] + TablePathExpression [59-89] [t3 UNPIVOT (a FOR c IN (x, y))] + PathExpression [59-61] [t3] + Identifier(t3) [59-61] [t3] + UnpivotClause [62-89] [UNPIVOT (a FOR c IN (x, y))] + PathExpressionList [71-72] [a] + PathExpression [71-72] [a] + Identifier(a) [71-72] [a] + PathExpression [77-78] [c] + Identifier(c) [77-78] [c] + UnpivotInItemList [82-88] [(x, y)] + UnpivotInItem [83-84] [x] + PathExpressionList [83-84] [x] + PathExpression [83-84] [x] + Identifier(x) [83-84] [x] + UnpivotInItem [86-87] [y] + PathExpressionList [86-87] [y] + PathExpression [86-87] [y] + Identifier(y) [86-87] [y] + Location [89-90] [,] + TablePathExpression [93-170] [t4 WITH OFFSET...abc(5 rows)] + PathExpression [93-95] [t4] + Identifier(t4) [93-95] [t4] + WithOffset [96-112] [WITH OFFSET AS o] + Alias [108-112] [AS o] + Identifier(o) [111-112] [o] + ForSystemTime [118-141] [FOR SYSTEM TIME AS OF y] + PathExpression [140-141] [y] + Identifier(y) [140-141] [y] + SampleClause [147-170] [TABLESAMPLE abc(5 rows)] + Identifier(abc) [159-162] [abc] + SampleSize [163-169] [5 rows] + IntLiteral(5) [163-164] [5] +-- +FROM + t1 @{ hint = 1 }, + t2 PIVOT(SUM(a) FOR b IN (0, 1)), + t3 UNPIVOT(a FOR c IN ((x), (y))), + t4 WITH OFFSET AS o FOR SYSTEM_TIME AS OF y TABLESAMPLE abc(5 ROWS) diff --git a/zetasql/parser/testdata/pipe_join.test b/zetasql/parser/testdata/pipe_join.test new file mode 100644 index 000000000..abfd5208f --- /dev/null +++ b/zetasql/parser/testdata/pipe_join.test @@ -0,0 +1,504 @@ +# The first few tests here are trying to make sure all the possible keyword +# prefixes around JOIN work for pipe JOIN too, and that the various +# forms of RHS table_expressions also work. +select 1 x +|> join t2.x.y +|> join t3 using (x) +|> left join @{hint=1} t4 on true +|> inner join (select zzz from zzz) +|> cross join t6 +|> natural join t7 +|> hash join t7 +|> lookup join t7 +-- +QueryStatement [0-186] [select 1 x...lookup join t7] + Query [0-186] [select 1 x...lookup join t7] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-25] [|> join t2.x.y] + Join [13-25] [join t2.x.y] + PipeJoinLhsPlaceholder [14-25] [join t2.x.y] + Location [14-18] [join] + TablePathExpression [19-25] [t2.x.y] + PathExpression [19-25] [t2.x.y] + Identifier(t2) [19-21] [t2] + Identifier(x) [22-23] [x] + Identifier(y) [24-25] [y] + PipeJoin [26-46] [|> join t3 using (x)] + Join [28-46] [join t3 using (x)] + PipeJoinLhsPlaceholder [29-46] [join t3 using (x)] + Location [29-33] [join] + TablePathExpression [34-36] [t3] + PathExpression [34-36] [t3] + Identifier(t3) [34-36] [t3] + UsingClause [37-46] [using (x)] + Identifier(x) [44-45] [x] + PipeJoin [47-80] [|> left join...t4 on true] + Join(LEFT) [49-80] [left join @{hint=1} t4 on true] + PipeJoinLhsPlaceholder [50-80] [left join @{hint=1} t4 on true] + Hint [60-69] [@{hint=1}] + HintEntry [62-68] [hint=1] + Identifier(hint) [62-66] [hint] + IntLiteral(1) [67-68] [1] + Location [50-59] [left join] + TablePathExpression [70-72] [t4] + PathExpression [70-72] [t4] + Identifier(t4) [70-72] [t4] + OnClause [73-80] [on true] + BooleanLiteral(true) [76-80] [true] + PipeJoin [81-116] [|> inner join...from zzz)] + Join(INNER) [83-116] [inner join...from zzz)] + PipeJoinLhsPlaceholder [84-116] [inner join...from zzz)] + Location [84-94] [inner join] + TableSubquery [95-116] [(select zzz from zzz)] + Query [96-115] [select zzz from zzz] + Select [96-115] [select zzz from zzz] + SelectList [103-106] [zzz] + SelectColumn [103-106] [zzz] + PathExpression [103-106] [zzz] + Identifier(zzz) [103-106] [zzz] + FromClause [107-115] [from zzz] + TablePathExpression [112-115] [zzz] + PathExpression [112-115] [zzz] + Identifier(zzz) [112-115] [zzz] + PipeJoin [117-133] [|> cross join t6] + Join(CROSS) [119-133] [cross join t6] + PipeJoinLhsPlaceholder [120-133] [cross join t6] + Location [120-130] [cross join] + TablePathExpression [131-133] [t6] + PathExpression [131-133] [t6] + Identifier(t6) [131-133] [t6] + PipeJoin [134-152] [|> natural join t7] + Join(NATURAL) [137-152] [natural join t7] + PipeJoinLhsPlaceholder [137-152] [natural join t7] + Location [137-149] [natural join] + TablePathExpression [150-152] [t7] + PathExpression [150-152] [t7] + Identifier(t7) [150-152] [t7] + PipeJoin [153-168] [|> hash join t7] + Join(HASH) [155-168] [hash join t7] + PipeJoinLhsPlaceholder [156-168] [hash join t7] + Location [156-165] [hash join] + TablePathExpression [166-168] [t7] + PathExpression [166-168] [t7] + Identifier(t7) [166-168] [t7] + PipeJoin [169-186] [|> lookup join t7] + Join(LOOKUP) [171-186] [lookup join t7] + PipeJoinLhsPlaceholder [172-186] [lookup join t7] + Location [172-183] [lookup join] + TablePathExpression [184-186] [t7] + PathExpression [184-186] [t7] + Identifier(t7) [184-186] [t7] +-- +SELECT + 1 AS x +|> JOIN + t2.x.y +|> JOIN + t3 + USING(x) +|> LEFT JOIN + @{ hint = 1 } t4 + ON true +|> INNER JOIN + ( + SELECT + zzz + FROM + zzz + ) +|> CROSS JOIN + t6 +|> NATURAL JOIN + t7 +|> HASH JOIN + t7 +|> LOOKUP JOIN + t7 +== + +select 1 x +|> join tvf((select 1)) +|> join unnest(y) +|> join unnest(y.z) i WITH OFFSET o +-- +QueryStatement [0-88] [select 1 x...WITH OFFSET o] + Query [0-88] [select 1 x...WITH OFFSET o] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-34] [|> join tvf((select 1))] + Join [13-34] [join tvf((select 1))] + PipeJoinLhsPlaceholder [14-34] [join tvf((select 1))] + Location [14-18] [join] + TVF [19-34] [tvf((select 1))] + PathExpression [19-22] [tvf] + Identifier(tvf) [19-22] [tvf] + TVFArgument [23-33] [(select 1)] + ExpressionSubquery [23-33] [(select 1)] + Query [24-32] [select 1] + Select [24-32] [select 1] + SelectList [31-32] [1] + SelectColumn [31-32] [1] + IntLiteral(1) [31-32] [1] + PipeJoin [35-52] [|> join unnest(y)] + Join [37-52] [join unnest(y)] + PipeJoinLhsPlaceholder [38-52] [join unnest(y)] + Location [38-42] [join] + TablePathExpression [43-52] [unnest(y)] + UnnestExpression [43-52] [unnest(y)] + ExpressionWithOptAlias [50-51] [y] + PathExpression [50-51] [y] + Identifier(y) [50-51] [y] + PipeJoin [53-88] [|> join unnest...WITH OFFSET o] + Join [55-88] [join unnest...WITH OFFSET o] + PipeJoinLhsPlaceholder [56-88] [join unnest...WITH OFFSET o] + Location [56-60] [join] + TablePathExpression [61-88] [unnest(y.z) i WITH OFFSET o] + UnnestExpression [61-72] [unnest(y.z)] + ExpressionWithOptAlias [68-71] [y.z] + PathExpression [68-71] [y.z] + Identifier(y) [68-69] [y] + Identifier(z) [70-71] [z] + Alias [73-74] [i] + Identifier(i) [73-74] [i] + WithOffset [75-88] [WITH OFFSET o] + Alias [87-88] [o] + Identifier(o) [87-88] [o] +-- +SELECT + 1 AS x +|> JOIN + tvf(( + SELECT + 1 + )) +|> JOIN + UNNEST(y) +|> JOIN + UNNEST(y.z) AS i WITH OFFSET AS o +== + +# Variations of LEFT/RIGHT/FULL [OUTER] JOIN. +select 1 x +|> {{left|right|full}} {{|outer}} join tr +-- +ALTERNATION GROUP: left, +-- +QueryStatement [0-27] [select 1 x |> left join tr] + Query [0-27] [select 1 x |> left join tr] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-27] [|> left join tr] + Join(LEFT) [13-27] [left join tr] + PipeJoinLhsPlaceholder [14-27] [left join tr] + Location [14-24] [left join] + TablePathExpression [25-27] [tr] + PathExpression [25-27] [tr] + Identifier(tr) [25-27] [tr] +-- +SELECT + 1 AS x +|> LEFT JOIN + tr +-- +ALTERNATION GROUP: left,outer +-- +QueryStatement [0-32] [select 1 x...outer join tr] + Query [0-32] [select 1 x...outer join tr] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-32] [|> left outer join tr] + Join(LEFT) [13-32] [left outer join tr] + PipeJoinLhsPlaceholder [14-32] [left outer join tr] + Location [14-29] [left outer join] + TablePathExpression [30-32] [tr] + PathExpression [30-32] [tr] + Identifier(tr) [30-32] [tr] +-- +SELECT + 1 AS x +|> LEFT JOIN + tr +-- +ALTERNATION GROUP: right, +-- +QueryStatement [0-28] [select 1 x |> right join tr] + Query [0-28] [select 1 x |> right join tr] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-28] [|> right join tr] + Join(RIGHT) [13-28] [right join tr] + PipeJoinLhsPlaceholder [14-28] [right join tr] + Location [14-25] [right join] + TablePathExpression [26-28] [tr] + PathExpression [26-28] [tr] + Identifier(tr) [26-28] [tr] +-- +SELECT + 1 AS x +|> RIGHT JOIN + tr +-- +ALTERNATION GROUP: right,outer +-- +QueryStatement [0-33] [select 1 x...outer join tr] + Query [0-33] [select 1 x...outer join tr] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-33] [|> right outer join tr] + Join(RIGHT) [13-33] [right outer join tr] + PipeJoinLhsPlaceholder [14-33] [right outer join tr] + Location [14-30] [right outer join] + TablePathExpression [31-33] [tr] + PathExpression [31-33] [tr] + Identifier(tr) [31-33] [tr] +-- +SELECT + 1 AS x +|> RIGHT JOIN + tr +-- +ALTERNATION GROUP: full, +-- +QueryStatement [0-27] [select 1 x |> full join tr] + Query [0-27] [select 1 x |> full join tr] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-27] [|> full join tr] + Join(FULL) [13-27] [full join tr] + PipeJoinLhsPlaceholder [14-27] [full join tr] + Location [14-24] [full join] + TablePathExpression [25-27] [tr] + PathExpression [25-27] [tr] + Identifier(tr) [25-27] [tr] +-- +SELECT + 1 AS x +|> FULL JOIN + tr +-- +ALTERNATION GROUP: full,outer +-- +QueryStatement [0-32] [select 1 x...outer join tr] + Query [0-32] [select 1 x...outer join tr] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-32] [|> full outer join tr] + Join(FULL) [13-32] [full outer join tr] + PipeJoinLhsPlaceholder [14-32] [full outer join tr] + Location [14-29] [full outer join] + TablePathExpression [30-32] [tr] + PathExpression [30-32] [tr] + Identifier(tr) [30-32] [tr] +-- +SELECT + 1 AS x +|> FULL JOIN + tr +== + +# The normal join has productions for multiple ON/USING clauses, but +# that isn't allowed here. +select 1 x +|> join t2 USING(x) USING(y) +-- +ERROR: Syntax error: Expected end of input but got keyword USING [at 2:21] +|> join t2 USING(x) USING(y) + ^ +== + +select 1 x +|> join t2 ON true ON false +-- +ERROR: Syntax error: Expected end of input but got keyword ON [at 2:20] +|> join t2 ON true ON false + ^ +== + +select 1 x +|> join +-- +ERROR: Syntax error: Unexpected end of statement [at 2:8] +|> join + ^ +== + +select 1 x +|> join t2, t3 +-- +ERROR: Syntax error: Expected end of input but got "," [at 2:11] +|> join t2, t3 + ^ +== + +select 1 x +|> join t2 join t3 +-- +ERROR: Syntax error: Expected end of input but got keyword JOIN [at 2:12] +|> join t2 join t3 + ^ +== + +# Parenthesized joins on the rhs, with arbitrary nesting. +select 1 x +|> join (t2 join t3 using (x)) +|> join (t2 join (t3 join t4)) +-- +QueryStatement [0-72] [select 1 x...join t4))] + Query [0-72] [select 1 x...join t4))] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-41] [|> join (t2 join t3 using (x))] + Join [13-41] [join (t2 join t3 using (x))] + PipeJoinLhsPlaceholder [14-41] [join (t2 join t3 using (x))] + Location [14-18] [join] + ParenthesizedJoin [19-41] [(t2 join t3 using (x))] + Join [20-40] [t2 join t3 using (x)] + TablePathExpression [20-22] [t2] + PathExpression [20-22] [t2] + Identifier(t2) [20-22] [t2] + Location [23-27] [join] + TablePathExpression [28-30] [t3] + PathExpression [28-30] [t3] + Identifier(t3) [28-30] [t3] + UsingClause [31-40] [using (x)] + Identifier(x) [38-39] [x] + PipeJoin [42-72] [|> join (t2 join (t3 join t4))] + Join [44-72] [join (t2 join (t3 join t4))] + PipeJoinLhsPlaceholder [45-72] [join (t2 join (t3 join t4))] + Location [45-49] [join] + ParenthesizedJoin [50-72] [(t2 join (t3 join t4))] + Join [51-71] [t2 join (t3 join t4)] + TablePathExpression [51-53] [t2] + PathExpression [51-53] [t2] + Identifier(t2) [51-53] [t2] + Location [54-58] [join] + ParenthesizedJoin [59-71] [(t3 join t4)] + Join [60-70] [t3 join t4] + TablePathExpression [60-62] [t3] + PathExpression [60-62] [t3] + Identifier(t3) [60-62] [t3] + Location [63-67] [join] + TablePathExpression [68-70] [t4] + PathExpression [68-70] [t4] + Identifier(t4) [68-70] [t4] +-- +SELECT + 1 AS x +|> JOIN + ( + t2 + JOIN + t3 + USING(x) + ) +|> JOIN + ( + t2 + JOIN + ( + t3 + JOIN + t4 + ) + ) +== + +# Parenthesizing also allows sequences of joins. +select 1 x +|> join (t2 join t3 join t4) using (x) +-- +QueryStatement [0-49] [select 1 x...using (x)] + Query [0-49] [select 1 x...using (x)] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeJoin [11-49] [|> join (t2...using (x)] + Join [13-49] [join (t2 join...using (x)] + PipeJoinLhsPlaceholder [14-49] [join (t2 join...using (x)] + Location [14-18] [join] + ParenthesizedJoin [19-39] [(t2 join t3 join t4)] + Join [20-38] [t2 join t3 join t4] + Join [20-30] [t2 join t3] + TablePathExpression [20-22] [t2] + PathExpression [20-22] [t2] + Identifier(t2) [20-22] [t2] + Location [23-27] [join] + TablePathExpression [28-30] [t3] + PathExpression [28-30] [t3] + Identifier(t3) [28-30] [t3] + Location [31-35] [join] + TablePathExpression [36-38] [t4] + PathExpression [36-38] [t4] + Identifier(t4) [36-38] [t4] + UsingClause [40-49] [using (x)] + Identifier(x) [47-48] [x] +-- +SELECT + 1 AS x +|> JOIN + ( + t2 + JOIN + t3 + JOIN + t4 + ) + USING(x) +== + +# The weird JoinProcessor case with consecutive conditions +# doesn't work inside a parenthesized rhs. +select 1 +|> join (t1 join t2 join t3 using (x) using (y)) +-- +ERROR: Syntax error: Expected end of input but got keyword USING [at 2:39] +|> join (t1 join t2 join t3 using (x) using (y)) + ^ +== + +select 1 +|> join (t1 join t2 join t3 on cond1 on cond2) +-- +ERROR: Syntax error: Expected end of input but got keyword ON [at 2:38] +|> join (t1 join t2 join t3 on cond1 on cond2) + ^ diff --git a/zetasql/parser/testdata/pipe_order_by.test b/zetasql/parser/testdata/pipe_order_by.test new file mode 100644 index 000000000..5c3a593f2 --- /dev/null +++ b/zetasql/parser/testdata/pipe_order_by.test @@ -0,0 +1,144 @@ +select 1 +|> order by x +|> order by 1, x, y, x+1, sum(x), x() OVER () +|> order @{hint=1} by y +|> order by x ASC, y DESC +|> order by x COLLATE "abc" +|> order by x NULLS FIRST +|> order by x COLLATE "abc" ASC NULLS LAST +-- +QueryStatement [0-215] [select 1 |...NULLS LAST] + Query [0-215] [select 1 |...NULLS LAST] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeOrderBy [9-22] [|> order by x] + OrderBy [12-22] [order by x] + OrderingExpression(ASC) [21-22] [x] + PathExpression [21-22] [x] + Identifier(x) [21-22] [x] + PipeOrderBy [23-68] [|> order by...() OVER ()] + OrderBy [26-68] [order by 1...() OVER ()] + OrderingExpression(ASC) [35-36] [1] + IntLiteral(1) [35-36] [1] + OrderingExpression(ASC) [38-39] [x] + PathExpression [38-39] [x] + Identifier(x) [38-39] [x] + OrderingExpression(ASC) [41-42] [y] + PathExpression [41-42] [y] + Identifier(y) [41-42] [y] + OrderingExpression(ASC) [44-47] [x+1] + BinaryExpression(+) [44-47] [x+1] + PathExpression [44-45] [x] + Identifier(x) [44-45] [x] + IntLiteral(1) [46-47] [1] + OrderingExpression(ASC) [49-55] [sum(x)] + FunctionCall [49-55] [sum(x)] + PathExpression [49-52] [sum] + Identifier(sum) [49-52] [sum] + PathExpression [53-54] [x] + Identifier(x) [53-54] [x] + OrderingExpression(ASC) [57-68] [x() OVER ()] + AnalyticFunctionCall [57-68] [x() OVER ()] + FunctionCall [57-60] [x()] + PathExpression [57-58] [x] + Identifier(x) [57-58] [x] + WindowSpecification [66-68] [()] + PipeOrderBy [69-92] [|> order @{hint=1} by y] + OrderBy [72-92] [order @{hint=1} by y] + Hint [78-87] [@{hint=1}] + HintEntry [80-86] [hint=1] + Identifier(hint) [80-84] [hint] + IntLiteral(1) [85-86] [1] + OrderingExpression(ASC) [91-92] [y] + PathExpression [91-92] [y] + Identifier(y) [91-92] [y] + PipeOrderBy [93-118] [|> order by x ASC, y DESC] + OrderBy [96-118] [order by x ASC, y DESC] + OrderingExpression(ASC EXPLICITLY) [105-110] [x ASC] + PathExpression [105-106] [x] + Identifier(x) [105-106] [x] + OrderingExpression(DESC) [112-118] [y DESC] + PathExpression [112-113] [y] + Identifier(y) [112-113] [y] + PipeOrderBy [119-146] [|> order by x COLLATE "abc"] + OrderBy [122-146] [order by x COLLATE "abc"] + OrderingExpression(ASC) [131-146] [x COLLATE "abc"] + PathExpression [131-132] [x] + Identifier(x) [131-132] [x] + Collate [133-146] [COLLATE "abc"] + StringLiteral [141-146] ["abc"] + StringLiteralComponent("abc") [141-146] ["abc"] + PipeOrderBy [147-172] [|> order by x NULLS FIRST] + OrderBy [150-172] [order by x NULLS FIRST] + OrderingExpression(ASC) [159-172] [x NULLS FIRST] + PathExpression [159-160] [x] + Identifier(x) [159-160] [x] + NullOrder(NULLS FIRST) [161-172] [NULLS FIRST] + PipeOrderBy [173-215] [|> order by...NULLS LAST] + OrderBy [176-215] [order by x...NULLS LAST] + OrderingExpression(ASC EXPLICITLY) [185-215] [x COLLATE "abc" ASC NULLS LAST] + PathExpression [185-186] [x] + Identifier(x) [185-186] [x] + Collate [187-200] [COLLATE "abc"] + StringLiteral [195-200] ["abc"] + StringLiteralComponent("abc") [195-200] ["abc"] + NullOrder(NULLS LAST) [205-215] [NULLS LAST] +-- +SELECT + 1 +|> ORDER BY x +|> ORDER BY 1, x, y, x + 1, sum(x), x() OVER () +|> ORDER @{ hint = 1 } BY y +|> ORDER BY x ASC, y DESC +|> ORDER BY x COLLATE "abc" +|> ORDER BY x NULLS FIRST +|> ORDER BY x COLLATE "abc" ASC NULLS LAST +== + +select 1 +|> order by +-- +ERROR: Syntax error: Unexpected end of statement [at 2:12] +|> order by + ^ +== + +# Trailing commas. +select 1 +|> order by x, +|> order by x, y, +-- +QueryStatement [0-41] [select 1 |...order by x, y,] + Query [0-41] [select 1 |...order by x, y,] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeOrderBy [9-23] [|> order by x,] + OrderBy [12-23] [order by x,] + OrderingExpression(ASC) [21-22] [x] + PathExpression [21-22] [x] + Identifier(x) [21-22] [x] + PipeOrderBy [24-41] [|> order by x, y,] + OrderBy [27-41] [order by x, y,] + OrderingExpression(ASC) [36-37] [x] + PathExpression [36-37] [x] + Identifier(x) [36-37] [x] + OrderingExpression(ASC) [39-40] [y] + PathExpression [39-40] [y] + Identifier(y) [39-40] [y] +-- +SELECT + 1 +|> ORDER BY x +|> ORDER BY x, y +== + +select 1 x, 2 y +|> order by , +-- +ERROR: Syntax error: Unexpected "," [at 2:13] +|> order by , + ^ diff --git a/zetasql/parser/testdata/pipe_parenthesized_query_alias.test b/zetasql/parser/testdata/pipe_parenthesized_query_alias.test new file mode 100644 index 000000000..3d3fa6bd9 --- /dev/null +++ b/zetasql/parser/testdata/pipe_parenthesized_query_alias.test @@ -0,0 +1,512 @@ +[language_{{features=PIPES|features=}}] +(SELECT 1) {{AS q1|q1|}} +-- +ALTERNATION GROUP: features=PIPES,AS q1 +-- +QueryStatement [0-16] [(SELECT 1) AS q1] + Query [0-16] [(SELECT 1) AS q1] + AliasedQueryExpression [0-16] [(SELECT 1) AS q1] + Query [1-9] [SELECT 1] + Select [1-9] [SELECT 1] + SelectList [8-9] [1] + SelectColumn [8-9] [1] + IntLiteral(1) [8-9] [1] + Alias [11-16] [AS q1] + Identifier(q1) [14-16] [q1] +-- +( + SELECT + 1 +) AS q1 +-- +ALTERNATION GROUPS: + features=PIPES,q1 + features=,q1 +-- +ERROR: Syntax error: Expected end of input but got identifier "q1" [at 1:12] +(SELECT 1) q1 + ^ +-- +ALTERNATION GROUPS: + features=PIPES, + features=, +-- +QueryStatement [0-10] [(SELECT 1)] + Query [1-9] [SELECT 1] + Select [1-9] [SELECT 1] + SelectList [8-9] [1] + SelectColumn [8-9] [1] + IntLiteral(1) [8-9] [1] +-- +( +SELECT + 1 +) +-- +ALTERNATION GROUP: features=,AS q1 +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:12] +(SELECT 1) AS q1 + ^ +== + +[language_{{features=PIPES|features=}}] +(FROM t1) {{AS q1|q1|}} +-- +ALTERNATION GROUP: features=PIPES,AS q1 +-- +QueryStatement [0-15] [(FROM t1) AS q1] + Query [0-15] [(FROM t1) AS q1] + AliasedQueryExpression [0-15] [(FROM t1) AS q1] + Query [1-8] [FROM t1] + FromQuery [1-8] [FROM t1] + FromClause [1-8] [FROM t1] + TablePathExpression [6-8] [t1] + PathExpression [6-8] [t1] + Identifier(t1) [6-8] [t1] + Alias [10-15] [AS q1] + Identifier(q1) [13-15] [q1] +-- +( + FROM + t1 +) AS q1 +-- +ALTERNATION GROUP: features=PIPES,q1 +-- +ERROR: Syntax error: Expected end of input but got identifier "q1" [at 1:11] +(FROM t1) q1 + ^ +-- +ALTERNATION GROUP: features=PIPES, +-- +QueryStatement [0-9] [(FROM t1)] + Query [1-8] [FROM t1] + FromQuery [1-8] [FROM t1] + FromClause [1-8] [FROM t1] + TablePathExpression [6-8] [t1] + PathExpression [6-8] [t1] + Identifier(t1) [6-8] [t1] +-- +( +FROM + t1) +-- +ALTERNATION GROUP: features=,AS q1 +-- +ERROR: Syntax error: Unexpected FROM [at 1:2] +(FROM t1) AS q1 + ^ +-- +ALTERNATION GROUP: features=,q1 +-- +ERROR: Syntax error: Unexpected FROM [at 1:2] +(FROM t1) q1 + ^ +-- +ALTERNATION GROUP: features=, +-- +ERROR: Syntax error: Unexpected FROM [at 1:2] +(FROM t1) + ^ +== + +[language_features=PIPES] +(FROM t1) {{AS|}} q1 +|> WHERE true +-- +ALTERNATION GROUP: AS +-- +QueryStatement [0-29] [(FROM t1) AS q1 |> WHERE true] + Query [0-29] [(FROM t1) AS q1 |> WHERE true] + AliasedQueryExpression [0-15] [(FROM t1) AS q1] + Query [1-8] [FROM t1] + FromQuery [1-8] [FROM t1] + FromClause [1-8] [FROM t1] + TablePathExpression [6-8] [t1] + PathExpression [6-8] [t1] + Identifier(t1) [6-8] [t1] + Alias [10-15] [AS q1] + Identifier(q1) [13-15] [q1] + PipeWhere [16-29] [|> WHERE true] + WhereClause [19-29] [WHERE true] + BooleanLiteral(true) [25-29] [true] +-- +( + FROM + t1 +) AS q1 +|> WHERE + true +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected end of input but got identifier "q1" [at 1:12] +(FROM t1) q1 + ^ +== + +[language_{{features=PIPES|features=}}] +(SELECT * FROM t1) AS q1 +UNION ALL +(SELECT * FROM t2) AS q2 +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [0-59] [(SELECT *...t2) AS q2] + Query [0-59] [(SELECT *...t2) AS q2] + SetOperation(UNION ALL) [0-59] [(SELECT *...t2) AS q2] + SetOperationMetadataList [25-34] [UNION ALL] + SetOperationMetadata [25-34] [UNION ALL] + SetOperationType [25-30] [UNION] + SetOperationAllOrDistinct [31-34] [ALL] + AliasedQueryExpression [0-24] [(SELECT * FROM t1) AS q1] + Query [1-17] [SELECT * FROM t1] + Select [1-17] [SELECT * FROM t1] + SelectList [8-9] [*] + SelectColumn [8-9] [*] + Star(*) [8-9] [*] + FromClause [10-17] [FROM t1] + TablePathExpression [15-17] [t1] + PathExpression [15-17] [t1] + Identifier(t1) [15-17] [t1] + Alias [19-24] [AS q1] + Identifier(q1) [22-24] [q1] + AliasedQueryExpression [35-59] [(SELECT * FROM t2) AS q2] + Query [36-52] [SELECT * FROM t2] + Select [36-52] [SELECT * FROM t2] + SelectList [43-44] [*] + SelectColumn [43-44] [*] + Star(*) [43-44] [*] + FromClause [45-52] [FROM t2] + TablePathExpression [50-52] [t2] + PathExpression [50-52] [t2] + Identifier(t2) [50-52] [t2] + Alias [54-59] [AS q2] + Identifier(q2) [57-59] [q2] +-- +( + SELECT + * + FROM + t1 +) AS q1 UNION ALL +( + SELECT + * + FROM + t2 +) AS q2 +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:20] +(SELECT * FROM t1) AS q1 + ^ +== + +[language_{{features=PIPES|features=}}] +(SELECT * FROM t1) AS q1 +ORDER BY x +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [0-35] [(SELECT *...ORDER BY x] + Query [0-35] [(SELECT *...ORDER BY x] + AliasedQueryExpression [0-24] [(SELECT * FROM t1) AS q1] + Query [1-17] [SELECT * FROM t1] + Select [1-17] [SELECT * FROM t1] + SelectList [8-9] [*] + SelectColumn [8-9] [*] + Star(*) [8-9] [*] + FromClause [10-17] [FROM t1] + TablePathExpression [15-17] [t1] + PathExpression [15-17] [t1] + Identifier(t1) [15-17] [t1] + Alias [19-24] [AS q1] + Identifier(q1) [22-24] [q1] + OrderBy [25-35] [ORDER BY x] + OrderingExpression(ASC) [34-35] [x] + PathExpression [34-35] [x] + Identifier(x) [34-35] [x] +-- +( + SELECT + * + FROM + t1 +) AS q1 +ORDER BY x +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:20] +(SELECT * FROM t1) AS q1 + ^ +== + +[language_{{features=PIPES|features=}}] +(SELECT * FROM t1) AS q1 +LIMIT 10 +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [0-33] [(SELECT *...q1 LIMIT 10] + Query [0-33] [(SELECT *...q1 LIMIT 10] + AliasedQueryExpression [0-24] [(SELECT * FROM t1) AS q1] + Query [1-17] [SELECT * FROM t1] + Select [1-17] [SELECT * FROM t1] + SelectList [8-9] [*] + SelectColumn [8-9] [*] + Star(*) [8-9] [*] + FromClause [10-17] [FROM t1] + TablePathExpression [15-17] [t1] + PathExpression [15-17] [t1] + Identifier(t1) [15-17] [t1] + Alias [19-24] [AS q1] + Identifier(q1) [22-24] [q1] + LimitOffset [25-33] [LIMIT 10] + IntLiteral(10) [31-33] [10] +-- +( + SELECT + * + FROM + t1 +) AS q1 +LIMIT 10 +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:20] +(SELECT * FROM t1) AS q1 + ^ +== + +[language_{{features=PIPES|features=}}] +(SELECT * FROM t1) {{AS q1|q1}} +JOIN +(SELECT * FROM t2) {{AS q2|q2}} +-- +ALTERNATION GROUPS: + features=PIPES,AS q1,AS q2 + features=PIPES,AS q1,q2 +-- +ERROR: Syntax error: Expected end of input but got keyword JOIN [at 2:1] +JOIN +^ +-- +ALTERNATION GROUPS: + features=PIPES,q1,AS q2 + features=PIPES,q1,q2 + features=,q1,AS q2 + features=,q1,q2 +-- +ERROR: Syntax error: Expected end of input but got identifier "q1" [at 1:20] +(SELECT * FROM t1) q1 + ^ +-- +ALTERNATION GROUPS: + features=,AS q1,AS q2 + features=,AS q1,q2 +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:20] +(SELECT * FROM t1) AS q1 + ^ +== + +[language_{{features=PIPES|features=}}] +# This compares aliased query output inside and outside FROM. +{{FROM|}} (SELECT 1) AS q +-- +ALTERNATION GROUP: features=PIPES,FROM +-- +QueryStatement [62-82] [FROM (SELECT 1) AS q] + Query [62-82] [FROM (SELECT 1) AS q] + FromQuery [62-82] [FROM (SELECT 1) AS q] + FromClause [62-82] [FROM (SELECT 1) AS q] + TableSubquery [67-82] [(SELECT 1) AS q] + Query [68-76] [SELECT 1] + Select [68-76] [SELECT 1] + SelectList [75-76] [1] + SelectColumn [75-76] [1] + IntLiteral(1) [75-76] [1] + Alias [78-82] [AS q] + Identifier(q) [81-82] [q] +-- +FROM + ( + SELECT + 1 + ) AS q +-- +ALTERNATION GROUP: features=PIPES, +-- +QueryStatement [63-78] [(SELECT 1) AS q] + Query [63-78] [(SELECT 1) AS q] + AliasedQueryExpression [63-78] [(SELECT 1) AS q] + Query [64-72] [SELECT 1] + Select [64-72] [SELECT 1] + SelectList [71-72] [1] + SelectColumn [71-72] [1] + IntLiteral(1) [71-72] [1] + Alias [74-78] [AS q] + Identifier(q) [77-78] [q] +-- +( + SELECT + 1 +) AS q +-- +ALTERNATION GROUP: features=,FROM +-- +ERROR: Syntax error: Unexpected FROM [at 2:1] +FROM (SELECT 1) AS q +^ +-- +ALTERNATION GROUP: features=, +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 2:13] + (SELECT 1) AS q + ^ +== + +[language_{{features=PIPES|features=}}] +(((SELECT 1))) AS q1 +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [0-20] [(((SELECT 1))) AS q1] + Query [0-20] [(((SELECT 1))) AS q1] + AliasedQueryExpression [0-20] [(((SELECT 1))) AS q1] + Query [3-11] [SELECT 1] + Select [3-11] [SELECT 1] + SelectList [10-11] [1] + SelectColumn [10-11] [1] + IntLiteral(1) [10-11] [1] + Alias [15-20] [AS q1] + Identifier(q1) [18-20] [q1] +-- +(( + SELECT + 1 + ) +) AS q1 +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:16] +(((SELECT 1))) AS q1 + ^ +== + +[language_{{features=PIPES|features=}}] +(((SELECT 1) AS q1)) +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [0-20] [(((SELECT 1) AS q1))] + Query [2-18] [(SELECT 1) AS q1] + AliasedQueryExpression [2-18] [(SELECT 1) AS q1] + Query [3-11] [SELECT 1] + Select [3-11] [SELECT 1] + SelectList [10-11] [1] + SelectColumn [10-11] [1] + IntLiteral(1) [10-11] [1] + Alias [13-18] [AS q1] + Identifier(q1) [16-18] [q1] +-- +( +( + SELECT + 1 +) AS q1) +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:14] +(((SELECT 1) AS q1)) + ^ +== + +[language_{{features=PIPES|features=}}] +(((SELECT 1) AS q1) AS q2) AS q3 +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [0-32] [(((SELECT...q2) AS q3] + Query [0-32] [(((SELECT...q2) AS q3] + AliasedQueryExpression [0-32] [(((SELECT...q2) AS q3] + Query [1-25] [((SELECT 1) AS q1) AS q2] + AliasedQueryExpression [1-25] [((SELECT 1) AS q1) AS q2] + Query [2-18] [(SELECT 1) AS q1] + AliasedQueryExpression [2-18] [(SELECT 1) AS q1] + Query [3-11] [SELECT 1] + Select [3-11] [SELECT 1] + SelectList [10-11] [1] + SelectColumn [10-11] [1] + IntLiteral(1) [10-11] [1] + Alias [13-18] [AS q1] + Identifier(q1) [16-18] [q1] + Alias [20-25] [AS q2] + Identifier(q2) [23-25] [q2] + Alias [27-32] [AS q3] + Identifier(q3) [30-32] [q3] +-- +( + ( + ( + SELECT + 1 + ) AS q1 + ) AS q2 +) AS q3 +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 1:14] +(((SELECT 1) AS q1) AS q2) AS q3 + ^ +== + +[language_{{features=PIPES|features=}}] +# Test another kind of QueryExpression with an alias. +(select 1 + union all + select 2) AS q +-- +ALTERNATION GROUP: features=PIPES +-- +QueryStatement [54-90] [(select 1...select 2) AS q] + Query [54-90] [(select 1...select 2) AS q] + AliasedQueryExpression [54-90] [(select 1...select 2) AS q] + Query [55-84] [select 1 union all select 2] + SetOperation(UNION ALL) [55-84] [select 1 union all select 2] + SetOperationMetadataList [65-74] [union all] + SetOperationMetadata [65-74] [union all] + SetOperationType [65-70] [union] + SetOperationAllOrDistinct [71-74] [all] + Select [55-63] [select 1] + SelectList [62-63] [1] + SelectColumn [62-63] [1] + IntLiteral(1) [62-63] [1] + Select [76-84] [select 2] + SelectList [83-84] [2] + SelectColumn [83-84] [2] + IntLiteral(2) [83-84] [2] + Alias [86-90] [AS q] + Identifier(q) [89-90] [q] +-- +( + SELECT + 1 + UNION ALL + SELECT + 2 +) AS q +-- +ALTERNATION GROUP: features= +-- +ERROR: Syntax error: Alias not allowed on parenthesized outer query [at 4:12] + select 2) AS q + ^ diff --git a/zetasql/parser/testdata/pipe_pivot.test b/zetasql/parser/testdata/pipe_pivot.test new file mode 100644 index 000000000..151125fe6 --- /dev/null +++ b/zetasql/parser/testdata/pipe_pivot.test @@ -0,0 +1,252 @@ +# Most of these tests came from pivot.test, with cases involving combinations +# with JOIN, TVFs, subqueries, etc, removed as irrelevant. +# After we remove copybara stripping, we might combine these back into the +# main test file, with alternations on whether to add "|>". +[default language_features=PIPES] + +FROM t +|> PIVOT(SUM(a) FOR b IN (0, 1)) +-- +QueryStatement [0-39] [FROM t |>...IN (0, 1))] + Query [0-39] [FROM t |>...IN (0, 1))] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipePivot [7-39] [|> PIVOT(SUM...IN (0, 1))] + PivotClause [10-39] [PIVOT(SUM(a) FOR b IN (0, 1))] + PivotExpressionList [16-22] [SUM(a)] + PivotExpression [16-22] [SUM(a)] + FunctionCall [16-22] [SUM(a)] + PathExpression [16-19] [SUM] + Identifier(SUM) [16-19] [SUM] + PathExpression [20-21] [a] + Identifier(a) [20-21] [a] + PathExpression [27-28] [b] + Identifier(b) [27-28] [b] + PivotValueList [33-37] [0, 1] + PivotValue [33-34] [0] + IntLiteral(0) [33-34] [0] + PivotValue [36-37] [1] + IntLiteral(1) [36-37] [1] +-- +FROM + t +|> PIVOT(SUM(a) FOR b IN (0, 1)) +== + +# Multiple pivot exprs, single IN list item. +# Pivot output table has alias without AS here. +FROM t +|> PIVOT(SUM(a), SUM(b) FOR b IN (0)) pivot_table +-- +QueryStatement [0-56] [FROM t |>...pivot_table] + Query [0-56] [FROM t |>...pivot_table] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipePivot [7-56] [|> PIVOT(SUM...pivot_table] + PivotClause [10-56] [PIVOT(SUM(...pivot_table] + PivotExpressionList [16-30] [SUM(a), SUM(b)] + PivotExpression [16-22] [SUM(a)] + FunctionCall [16-22] [SUM(a)] + PathExpression [16-19] [SUM] + Identifier(SUM) [16-19] [SUM] + PathExpression [20-21] [a] + Identifier(a) [20-21] [a] + PivotExpression [24-30] [SUM(b)] + FunctionCall [24-30] [SUM(b)] + PathExpression [24-27] [SUM] + Identifier(SUM) [24-27] [SUM] + PathExpression [28-29] [b] + Identifier(b) [28-29] [b] + PathExpression [35-36] [b] + Identifier(b) [35-36] [b] + PivotValueList [41-42] [0] + PivotValue [41-42] [0] + IntLiteral(0) [41-42] [0] + Alias [45-56] [pivot_table] + Identifier(pivot_table) [45-56] [pivot_table] +-- +FROM + t +|> PIVOT(SUM(a), SUM(b) FOR b IN (0)) AS pivot_table +== + +# Pivot expressions can have aliases, with optional AS. +# Value expressions can have aliases, with optional AS. +# PIVOT output has alias, with AS here. +# Contained expressions can be complex expressions. +FROM t +|> PIVOT(SUM(a) AS sum_a, SUM(aa) sum_aa, 2+COUNT(b+3) + FOR t.x IN (z, x+y xpy, 1 AS one)) AS p +-- +QueryStatement [0-110] [FROM t |>...one)) AS p] + Query [0-110] [FROM t |>...one)) AS p] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipePivot [7-110] [|> PIVOT(SUM...one)) AS p] + PivotClause [10-110] [PIVOT(SUM(...one)) AS p] + PivotExpressionList [16-61] [SUM(a) AS...COUNT(b+3)] + PivotExpression [16-31] [SUM(a) AS sum_a] + FunctionCall [16-22] [SUM(a)] + PathExpression [16-19] [SUM] + Identifier(SUM) [16-19] [SUM] + PathExpression [20-21] [a] + Identifier(a) [20-21] [a] + Alias [23-31] [AS sum_a] + Identifier(sum_a) [26-31] [sum_a] + PivotExpression [33-47] [SUM(aa) sum_aa] + FunctionCall [33-40] [SUM(aa)] + PathExpression [33-36] [SUM] + Identifier(SUM) [33-36] [SUM] + PathExpression [37-39] [aa] + Identifier(aa) [37-39] [aa] + Alias [41-47] [sum_aa] + Identifier(sum_aa) [41-47] [sum_aa] + PivotExpression [49-61] [2+COUNT(b+3)] + BinaryExpression(+) [49-61] [2+COUNT(b+3)] + IntLiteral(2) [49-50] [2] + FunctionCall [51-61] [COUNT(b+3)] + PathExpression [51-56] [COUNT] + Identifier(COUNT) [51-56] [COUNT] + BinaryExpression(+) [57-60] [b+3] + PathExpression [57-58] [b] + Identifier(b) [57-58] [b] + IntLiteral(3) [59-60] [3] + PathExpression [75-78] [t.x] + Identifier(t) [75-76] [t] + Identifier(x) [77-78] [x] + PivotValueList [83-103] [z, x+y xpy, 1 AS one] + PivotValue [83-84] [z] + PathExpression [83-84] [z] + Identifier(z) [83-84] [z] + PivotValue [86-93] [x+y xpy] + BinaryExpression(+) [86-89] [x+y] + PathExpression [86-87] [x] + Identifier(x) [86-87] [x] + PathExpression [88-89] [y] + Identifier(y) [88-89] [y] + Alias [90-93] [xpy] + Identifier(xpy) [90-93] [xpy] + PivotValue [95-103] [1 AS one] + IntLiteral(1) [95-96] [1] + Alias [97-103] [AS one] + Identifier(one) [100-103] [one] + Alias [106-110] [AS p] + Identifier(p) [109-110] [p] +-- +FROM + t +|> PIVOT(SUM(a) AS sum_a, SUM(aa) AS sum_aa, 2 + COUNT(b + 3) FOR t.x IN (z, x + y AS xpy, 1 AS one)) AS p +== + +FROM t +|> PIVOT +-- +ERROR: Syntax error: Expected "(" but got end of statement [at 2:9] +|> PIVOT + ^ +== + +FROM t +|> PIVOT() +-- +ERROR: Syntax error: Unexpected ")" [at 2:10] +|> PIVOT() + ^ +== + +FROM t +|> PIVOT(x) + + +# ERROR: Pivot clause with only pivot expr and FOR expr +FROM t +|> PIVOT(x FOR y) +-- +ERROR: Syntax error: Unexpected ")" [at 2:11] +|> PIVOT(x) + ^ +== + +# ERROR: Empty IN-list +FROM t +|> PIVOT(x FOR y IN ()) +-- +ERROR: Syntax error: Unexpected ")" [at 2:22] +|> PIVOT(x FOR y IN ()) + ^ +== + +# ERROR: Trailing comma in IN-list +FROM t +|> PIVOT(x FOR y IN (0,)) +-- +ERROR: Syntax error: Unexpected ")" [at 2:24] +|> PIVOT(x FOR y IN (0,)) + ^ +== + +# ERROR: Missing right-parenthesis +FROM t +|> PIVOT(x FOR y IN (0) +-- +ERROR: Syntax error: Expected ")" but got end of statement [at 2:24] +|> PIVOT(x FOR y IN (0) + ^ +== + +# ERROR: Missing pivot expression +FROM t +|> PIVOT(FOR y IN (0)) +-- +ERROR: Syntax error: Unexpected keyword FOR [at 2:10] +|> PIVOT(FOR y IN (0)) + ^ +== + +# ERROR: Trailing comma after last pivot expression +FROM t +|> PIVOT(SUM(x), SUM(y), FOR y IN (0)) +-- +ERROR: Syntax error: Unexpected keyword FOR [at 2:26] +|> PIVOT(SUM(x), SUM(y), FOR y IN (0)) + ^ +== + +# PIVOT as alias of table produced by a PIVOT clause. +FROM t +|> PIVOT(a FOR b IN (c)) AS PIVOT +-- +QueryStatement [0-40] [FROM t |>...) AS PIVOT] + Query [0-40] [FROM t |>...) AS PIVOT] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipePivot [7-40] [|> PIVOT(a...) AS PIVOT] + PivotClause [10-40] [PIVOT(a FOR b IN (c)) AS PIVOT] + PivotExpressionList [16-17] [a] + PivotExpression [16-17] [a] + PathExpression [16-17] [a] + Identifier(a) [16-17] [a] + PathExpression [22-23] [b] + Identifier(b) [22-23] [b] + PivotValueList [28-29] [c] + PivotValue [28-29] [c] + PathExpression [28-29] [c] + Identifier(c) [28-29] [c] + Alias [32-40] [AS PIVOT] + Identifier(PIVOT) [35-40] [PIVOT] +-- +FROM + t +|> PIVOT(a FOR b IN (c)) AS PIVOT diff --git a/zetasql/parser/testdata/pipe_query.test b/zetasql/parser/testdata/pipe_query.test new file mode 100644 index 000000000..74fa85a62 --- /dev/null +++ b/zetasql/parser/testdata/pipe_query.test @@ -0,0 +1,1158 @@ +[default language_features=PIPES] + +select 1,2,3 +|> where true; +-- +QueryStatement [0-26] [select 1,2,3 |> where true] + Query [0-26] [select 1,2,3 |> where true] + Select [0-12] [select 1,2,3] + SelectList [7-12] [1,2,3] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + SelectColumn [9-10] [2] + IntLiteral(2) [9-10] [2] + SelectColumn [11-12] [3] + IntLiteral(3) [11-12] [3] + PipeWhere [13-26] [|> where true] + WhereClause [16-26] [where true] + BooleanLiteral(true) [22-26] [true] +-- +SELECT + 1, + 2, + 3 +|> WHERE + true +== + +select * from t where x=1 +|> where y=1 +|> where y=2 +; +-- +QueryStatement [0-51] [select * from...where y=2] + Query [0-51] [select * from...where y=2] + Select [0-25] [select * from t where x=1] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-15] [from t] + TablePathExpression [14-15] [t] + PathExpression [14-15] [t] + Identifier(t) [14-15] [t] + WhereClause [16-25] [where x=1] + BinaryExpression(=) [22-25] [x=1] + PathExpression [22-23] [x] + Identifier(x) [22-23] [x] + IntLiteral(1) [24-25] [1] + PipeWhere [26-38] [|> where y=1] + WhereClause [29-38] [where y=1] + BinaryExpression(=) [35-38] [y=1] + PathExpression [35-36] [y] + Identifier(y) [35-36] [y] + IntLiteral(1) [37-38] [1] + PipeWhere [39-51] [|> where y=2] + WhereClause [42-51] [where y=2] + BinaryExpression(=) [48-51] [y=2] + PathExpression [48-49] [y] + Identifier(y) [48-49] [y] + IntLiteral(2) [50-51] [2] +-- +SELECT + * +FROM + t +WHERE + x = 1 +|> WHERE + y = 1 +|> WHERE + y = 2 +== + +select 5 +from ( + select 1 + |> where true +) +-- +QueryStatement [0-44] [select 5 from...where true )] + Query [0-44] [select 5 from...where true )] + Select [0-44] [select 5 from...where true )] + SelectList [7-8] [5] + SelectColumn [7-8] [5] + IntLiteral(5) [7-8] [5] + FromClause [9-44] [from ( select...where true )] + TableSubquery [14-44] [( select 1 |> where true )] + Query [18-42] [select 1 |> where true] + Select [18-26] [select 1] + SelectList [25-26] [1] + SelectColumn [25-26] [1] + IntLiteral(1) [25-26] [1] + PipeWhere [29-42] [|> where true] + WhereClause [32-42] [where true] + BooleanLiteral(true) [38-42] [true] +-- +SELECT + 5 +FROM + ( + SELECT + 1 + |> WHERE + true + ) +== + +select (select 1 |> where true) +-- +QueryStatement [0-31] [select (select...where true)] + Query [0-31] [select (select...where true)] + Select [0-31] [select (select...where true)] + SelectList [7-31] [(select 1 |> where true)] + SelectColumn [7-31] [(select 1 |> where true)] + ExpressionSubquery [7-31] [(select 1 |> where true)] + Query [8-30] [select 1 |> where true] + Select [8-16] [select 1] + SelectList [15-16] [1] + SelectColumn [15-16] [1] + IntLiteral(1) [15-16] [1] + PipeWhere [17-30] [|> where true] + WhereClause [20-30] [where true] + BooleanLiteral(true) [26-30] [true] +-- +SELECT + ( + SELECT + 1 + |> WHERE + true) +== + +# Query before set op cannot have pipe operators. +select 1 +|> where true +union all +select 2 +-- +ERROR: Syntax error: Expected end of input but got keyword UNION [at 3:1] +union all +^ +== + +select 1 +union all +select 2 +|> where true +-- +QueryStatement [0-41] [select 1 union...where true] + Query [0-41] [select 1 union...where true] + SetOperation(UNION ALL) [0-27] [select 1 union all select 2] + SetOperationMetadataList [9-18] [union all] + SetOperationMetadata [9-18] [union all] + SetOperationType [9-14] [union] + SetOperationAllOrDistinct [15-18] [all] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + Select [19-27] [select 2] + SelectList [26-27] [2] + SelectColumn [26-27] [2] + IntLiteral(2) [26-27] [2] + PipeWhere [28-41] [|> where true] + WhereClause [31-41] [where true] + BooleanLiteral(true) [37-41] [true] +-- +SELECT + 1 +UNION ALL +SELECT + 2 +|> WHERE + true +== + +select 1 +union all +select 2 +order by x +limit 5 +|> where false +-- +QueryStatement [0-61] [select 1 union...where false] + Query [0-61] [select 1 union...where false] + SetOperation(UNION ALL) [0-27] [select 1 union all select 2] + SetOperationMetadataList [9-18] [union all] + SetOperationMetadata [9-18] [union all] + SetOperationType [9-14] [union] + SetOperationAllOrDistinct [15-18] [all] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + Select [19-27] [select 2] + SelectList [26-27] [2] + SelectColumn [26-27] [2] + IntLiteral(2) [26-27] [2] + OrderBy [28-38] [order by x] + OrderingExpression(ASC) [37-38] [x] + PathExpression [37-38] [x] + Identifier(x) [37-38] [x] + LimitOffset [39-46] [limit 5] + IntLiteral(5) [45-46] [5] + PipeWhere [47-61] [|> where false] + WhereClause [50-61] [where false] + BooleanLiteral(false) [56-61] [false] +-- +SELECT + 1 +UNION ALL +SELECT + 2 +ORDER BY x +LIMIT 5 +|> WHERE + false +== + +WITH q1 AS (select 1 x |> where true) +select * from q1 +|> where false +-- +QueryStatement [0-69] [WITH q1 AS...where false] + Query [0-69] [WITH q1 AS...where false] + WithClause [0-37] [WITH q1 AS...where true)] + AliasedQuery [5-37] [q1 AS (select...where true)] + Identifier(q1) [5-7] [q1] + Query [12-36] [select 1 x |> where true] + Select [12-22] [select 1 x] + SelectList [19-22] [1 x] + SelectColumn [19-22] [1 x] + IntLiteral(1) [19-20] [1] + Alias [21-22] [x] + Identifier(x) [21-22] [x] + PipeWhere [23-36] [|> where true] + WhereClause [26-36] [where true] + BooleanLiteral(true) [32-36] [true] + Select [38-54] [select * from q1] + SelectList [45-46] [*] + SelectColumn [45-46] [*] + Star(*) [45-46] [*] + FromClause [47-54] [from q1] + TablePathExpression [52-54] [q1] + PathExpression [52-54] [q1] + Identifier(q1) [52-54] [q1] + PipeWhere [55-69] [|> where false] + WhereClause [58-69] [where false] + BooleanLiteral(false) [64-69] [false] +-- +WITH + q1 AS ( + SELECT + 1 AS x + |> WHERE + true + ) +SELECT + * +FROM + q1 +|> WHERE + false +== + +# This shows where the pipe operators are binding around the WITH structure. +# One pipe is attached to the CTE query, one to the final WITH +# query, and one following the WITH query outside the parentheses. +( + WITH q1 AS (select 1 x |> where 1) + select * from q1 + |> where 2 +) +|> where 3 +-- +QueryStatement [0-83] [( WITH q1...|> where 3] + Query [0-83] [( WITH q1...|> where 3] + Query [4-70] [WITH q1 AS...|> where 2] + WithClause [4-38] [WITH q1 AS...> where 1)] + AliasedQuery [9-38] [q1 AS (select 1 x |> where 1)] + Identifier(q1) [9-11] [q1] + Query [16-37] [select 1 x |> where 1] + Select [16-26] [select 1 x] + SelectList [23-26] [1 x] + SelectColumn [23-26] [1 x] + IntLiteral(1) [23-24] [1] + Alias [25-26] [x] + Identifier(x) [25-26] [x] + PipeWhere [27-37] [|> where 1] + WhereClause [30-37] [where 1] + IntLiteral(1) [36-37] [1] + Select [41-57] [select * from q1] + SelectList [48-49] [*] + SelectColumn [48-49] [*] + Star(*) [48-49] [*] + FromClause [50-57] [from q1] + TablePathExpression [55-57] [q1] + PathExpression [55-57] [q1] + Identifier(q1) [55-57] [q1] + PipeWhere [60-70] [|> where 2] + WhereClause [63-70] [where 2] + IntLiteral(2) [69-70] [2] + PipeWhere [73-83] [|> where 3] + WhereClause [76-83] [where 3] + IntLiteral(3) [82-83] [3] +-- +(WITH + q1 AS ( + SELECT + 1 AS x + |> WHERE + 1 + ) + SELECT + * + FROM + q1 + |> WHERE + 2 +) +|> WHERE + 3 +== + +# Pipe after a parenthesized query attaches at an outer level, not just +# as a trailing pipe operator. +( + select 1 + UNION ALL + select 2 + |> where 2 +) +|> where 3 +-- +QueryStatement [0-61] [( select...|> where 3] + Query [0-61] [( select...|> where 3] + Query [4-48] [select 1...|> where 2] + SetOperation(UNION ALL) [4-35] [select 1...select 2] + SetOperationMetadataList [15-24] [UNION ALL] + SetOperationMetadata [15-24] [UNION ALL] + SetOperationType [15-20] [UNION] + SetOperationAllOrDistinct [21-24] [ALL] + Select [4-12] [select 1] + SelectList [11-12] [1] + SelectColumn [11-12] [1] + IntLiteral(1) [11-12] [1] + Select [27-35] [select 2] + SelectList [34-35] [2] + SelectColumn [34-35] [2] + IntLiteral(2) [34-35] [2] + PipeWhere [38-48] [|> where 2] + WhereClause [41-48] [where 2] + IntLiteral(2) [47-48] [2] + PipeWhere [51-61] [|> where 3] + WhereClause [54-61] [where 3] + IntLiteral(3) [60-61] [3] +-- +( + SELECT + 1 + UNION ALL + SELECT + 2 + |> WHERE + 2 +) +|> WHERE + 3 +== + +select 1 x +limit 10 +|> where 1 +-- +QueryStatement [0-30] [select 1 x limit 10 |> where 1] + Query [0-30] [select 1 x limit 10 |> where 1] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + LimitOffset [11-19] [limit 10] + IntLiteral(10) [17-19] [10] + PipeWhere [20-30] [|> where 1] + WhereClause [23-30] [where 1] + IntLiteral(1) [29-30] [1] +-- +SELECT + 1 AS x +LIMIT 10 +|> WHERE + 1 +== + +select 1 x +order by 1 +|> where 1 +-- +QueryStatement [0-32] [select 1 x...|> where 1] + Query [0-32] [select 1 x...|> where 1] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + OrderBy [11-21] [order by 1] + OrderingExpression(ASC) [20-21] [1] + IntLiteral(1) [20-21] [1] + PipeWhere [22-32] [|> where 1] + WhereClause [25-32] [where 1] + IntLiteral(1) [31-32] [1] +-- +SELECT + 1 AS x +ORDER BY 1 +|> WHERE + 1 +== + +# Various forms and syntaxes for SELECT allowed during parsing. +# The full grammar of the normal SELECT clause is allowed. +# Some of these SELECT clauses are disallowed by the analyzer. +select 1 +|> select 1,2,"abc" +|> select distinct x +|> select as struct x,y +|> select as value z +|> select as c.TypeName z +|> select *, * except(abc), * replace(abc as def) +|> select @{hint=1} *, * except(abc), * replace(abc as def) +|> select abc.def.*, abc.* except(x) +|> select with anonymization 1,2 +-- +QueryStatement [0-300] [select 1 |...anonymization 1,2] + Query [0-300] [select 1 |...anonymization 1,2] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSelect [9-28] [|> select 1,2,"abc"] + Select [12-28] [select 1,2,"abc"] + SelectList [19-28] [1,2,"abc"] + SelectColumn [19-20] [1] + IntLiteral(1) [19-20] [1] + SelectColumn [21-22] [2] + IntLiteral(2) [21-22] [2] + SelectColumn [23-28] ["abc"] + StringLiteral [23-28] ["abc"] + StringLiteralComponent("abc") [23-28] ["abc"] + PipeSelect [29-49] [|> select distinct x] + Select(distinct=true) [32-49] [select distinct x] + SelectList [48-49] [x] + SelectColumn [48-49] [x] + PathExpression [48-49] [x] + Identifier(x) [48-49] [x] + PipeSelect [50-73] [|> select as struct x,y] + Select [53-73] [select as struct x,y] + SelectAs(as_mode=STRUCT) [60-69] [as struct] + SelectList [70-73] [x,y] + SelectColumn [70-71] [x] + PathExpression [70-71] [x] + Identifier(x) [70-71] [x] + SelectColumn [72-73] [y] + PathExpression [72-73] [y] + Identifier(y) [72-73] [y] + PipeSelect [74-94] [|> select as value z] + Select [77-94] [select as value z] + SelectAs(as_mode=VALUE) [84-92] [as value] + SelectList [93-94] [z] + SelectColumn [93-94] [z] + PathExpression [93-94] [z] + Identifier(z) [93-94] [z] + PipeSelect [95-120] [|> select as c.TypeName z] + Select [98-120] [select as c.TypeName z] + SelectAs [105-118] [as c.TypeName] + PathExpression [108-118] [c.TypeName] + Identifier(c) [108-109] [c] + Identifier(TypeName) [110-118] [TypeName] + SelectList [119-120] [z] + SelectColumn [119-120] [z] + PathExpression [119-120] [z] + Identifier(z) [119-120] [z] + PipeSelect [121-170] [|> select...abc as def)] + Select [124-170] [select *,...abc as def)] + SelectList [131-170] [*, * except...abc as def)] + SelectColumn [131-132] [*] + Star(*) [131-132] [*] + SelectColumn [134-147] [* except(abc)] + StarWithModifiers [134-147] [* except(abc)] + StarModifiers [136-147] [except(abc)] + StarExceptList [136-147] [except(abc)] + Identifier(abc) [143-146] [abc] + SelectColumn [149-170] [* replace(abc as def)] + StarWithModifiers [149-170] [* replace(abc as def)] + StarModifiers [151-170] [replace(abc as def)] + StarReplaceItem [159-169] [abc as def] + PathExpression [159-162] [abc] + Identifier(abc) [159-162] [abc] + Identifier(def) [166-169] [def] + PipeSelect [171-230] [|> select...abc as def)] + Select [174-230] [select @{hint...abc as def)] + Hint [181-190] [@{hint=1}] + HintEntry [183-189] [hint=1] + Identifier(hint) [183-187] [hint] + IntLiteral(1) [188-189] [1] + SelectList [191-230] [*, * except...abc as def)] + SelectColumn [191-192] [*] + Star(*) [191-192] [*] + SelectColumn [194-207] [* except(abc)] + StarWithModifiers [194-207] [* except(abc)] + StarModifiers [196-207] [except(abc)] + StarExceptList [196-207] [except(abc)] + Identifier(abc) [203-206] [abc] + SelectColumn [209-230] [* replace(abc as def)] + StarWithModifiers [209-230] [* replace(abc as def)] + StarModifiers [211-230] [replace(abc as def)] + StarReplaceItem [219-229] [abc as def] + PathExpression [219-222] [abc] + Identifier(abc) [219-222] [abc] + Identifier(def) [226-229] [def] + PipeSelect [231-267] [|> select...except(x)] + Select [234-267] [select abc...except(x)] + SelectList [241-267] [abc.def.*, abc.* except(x)] + SelectColumn [241-250] [abc.def.*] + DotStar [241-250] [abc.def.*] + PathExpression [241-248] [abc.def] + Identifier(abc) [241-244] [abc] + Identifier(def) [245-248] [def] + SelectColumn [252-267] [abc.* except(x)] + DotStarWithModifiers [252-267] [abc.* except(x)] + PathExpression [252-255] [abc] + Identifier(abc) [252-255] [abc] + StarModifiers [258-267] [except(x)] + StarExceptList [258-267] [except(x)] + Identifier(x) [265-266] [x] + PipeSelect [268-300] [|> select...anonymization 1,2] + Select [271-300] [select with anonymization 1,2] + SelectWith [278-296] [with anonymization] + Identifier(anonymization) [283-296] [anonymization] + SelectList [297-300] [1,2] + SelectColumn [297-298] [1] + IntLiteral(1) [297-298] [1] + SelectColumn [299-300] [2] + IntLiteral(2) [299-300] [2] +-- +SELECT + 1 +|> SELECT + 1, + 2, + "abc" +|> SELECT DISTINCT + x +|> SELECT AS STRUCT + x, + y +|> SELECT AS VALUE + z +|> SELECT AS c.TypeName + z +|> SELECT + *, + * EXCEPT (abc), + * REPLACE (abc AS def) +|> SELECT @{ hint = 1 } + *, + * EXCEPT (abc), + * REPLACE (abc AS def) +|> SELECT + abc.def.*, + abc.* EXCEPT (x) +|> SELECT WITH anonymization ALL + 1, + 2 +== + +# Trailing commas are allowed. +select 1, +|> select 2, +|> select 3,4, +-- +QueryStatement [0-37] [select 1,...select 3,4,] + Query [0-37] [select 1,...select 3,4,] + Select [0-9] [select 1,] + SelectList [7-9] [1,] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSelect [10-22] [|> select 2,] + Select [13-22] [select 2,] + SelectList [20-22] [2,] + SelectColumn [20-21] [2] + IntLiteral(2) [20-21] [2] + PipeSelect [23-37] [|> select 3,4,] + Select [26-37] [select 3,4,] + SelectList [33-37] [3,4,] + SelectColumn [33-34] [3] + IntLiteral(3) [33-34] [3] + SelectColumn [35-36] [4] + IntLiteral(4) [35-36] [4] +-- +SELECT + 1 +|> SELECT + 2 +|> SELECT + 3, + 4 +== + +select 1 +|> select , +-- +ERROR: Syntax error: Unexpected "," [at 2:11] +|> select , + ^ +== + +select 1 +|> select +-- +ERROR: Syntax error: Unexpected end of statement [at 2:10] +|> select + ^ +== + +# Can't have a FROM clause on pipe SELECT. +select 1 +|> select 1,2 from t1 +-- +ERROR: Syntax error: Expected end of input but got keyword FROM [at 2:15] +|> select 1,2 from t1 + ^ +== + +# Can't have a WHERE clause on pipe SELECT. +select 1 +|> select 1,2 where true +-- +ERROR: Syntax error: Expected end of input but got keyword WHERE [at 2:15] +|> select 1,2 where true + ^ +== + +select 1 x +|> extend 2 y, 3 AS z +|> extend y+z AS yz, pb.f1.f2[3] f3, sqrt(y) +|> where true +|> extend 1,2,3 as `three`, 4 four +-- +QueryStatement [0-126] [select 1 x...three`, 4 four] + Query [0-126] [select 1 x...three`, 4 four] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeExtend [11-32] [|> extend 2 y, 3 AS z] + Select [14-32] [extend 2 y, 3 AS z] + SelectList [21-32] [2 y, 3 AS z] + SelectColumn [21-24] [2 y] + IntLiteral(2) [21-22] [2] + Alias [23-24] [y] + Identifier(y) [23-24] [y] + SelectColumn [26-32] [3 AS z] + IntLiteral(3) [26-27] [3] + Alias [28-32] [AS z] + Identifier(z) [31-32] [z] + PipeExtend [33-77] [|> extend...f3, sqrt(y)] + Select [36-77] [extend y+z...f3, sqrt(y)] + SelectList [43-77] [y+z AS yz,...f3, sqrt(y)] + SelectColumn [43-52] [y+z AS yz] + BinaryExpression(+) [43-46] [y+z] + PathExpression [43-44] [y] + Identifier(y) [43-44] [y] + PathExpression [45-46] [z] + Identifier(z) [45-46] [z] + Alias [47-52] [AS yz] + Identifier(yz) [50-52] [yz] + SelectColumn [54-68] [pb.f1.f2[3] f3] + ArrayElement [54-65] [pb.f1.f2[3]] + PathExpression [54-62] [pb.f1.f2] + Identifier(pb) [54-56] [pb] + Identifier(f1) [57-59] [f1] + Identifier(f2) [60-62] [f2] + Location [62-63] [[] + IntLiteral(3) [63-64] [3] + Alias [66-68] [f3] + Identifier(f3) [66-68] [f3] + SelectColumn [70-77] [sqrt(y)] + FunctionCall [70-77] [sqrt(y)] + PathExpression [70-74] [sqrt] + Identifier(sqrt) [70-74] [sqrt] + PathExpression [75-76] [y] + Identifier(y) [75-76] [y] + PipeWhere [78-91] [|> where true] + WhereClause [81-91] [where true] + BooleanLiteral(true) [87-91] [true] + PipeExtend [92-126] [|> extend...three`, 4 four] + Select [95-126] [extend 1,2...three`, 4 four] + SelectList [102-126] [1,2,3 as `three`, 4 four] + SelectColumn [102-103] [1] + IntLiteral(1) [102-103] [1] + SelectColumn [104-105] [2] + IntLiteral(2) [104-105] [2] + SelectColumn [106-118] [3 as `three`] + IntLiteral(3) [106-107] [3] + Alias [108-118] [as `three`] + Identifier(three) [111-118] [`three`] + SelectColumn [120-126] [4 four] + IntLiteral(4) [120-121] [4] + Alias [122-126] [four] + Identifier(four) [122-126] [four] +-- +SELECT + 1 AS x +|> EXTEND + 2 AS y, + 3 AS z +|> EXTEND + y + z AS yz, + pb.f1.f2[3] AS f3, + sqrt(y) +|> WHERE + true +|> EXTEND + 1, + 2, + 3 AS three, + 4 AS four +== + +select 1 x +|> extend +-- +ERROR: Syntax error: Unexpected end of statement [at 2:10] +|> extend + ^ +== + +# Trailing commas are allowed. +select 1 x, +|> extend 2, +|> extend 3, 4, +-- +QueryStatement [0-40] [select 1 x...extend 3, 4,] + Query [0-40] [select 1 x...extend 3, 4,] + Select [0-11] [select 1 x,] + SelectList [7-11] [1 x,] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeExtend [12-24] [|> extend 2,] + Select [15-24] [extend 2,] + SelectList [22-24] [2,] + SelectColumn [22-23] [2] + IntLiteral(2) [22-23] [2] + PipeExtend [25-40] [|> extend 3, 4,] + Select [28-40] [extend 3, 4,] + SelectList [35-40] [3, 4,] + SelectColumn [35-36] [3] + IntLiteral(3) [35-36] [3] + SelectColumn [38-39] [4] + IntLiteral(4) [38-39] [4] +-- +SELECT + 1 AS x +|> EXTEND + 2 +|> EXTEND + 3, + 4 +== + +select 1 x +|> extend , +-- +ERROR: Syntax error: Unexpected "," [at 2:11] +|> extend , + ^ +== + +select 1 x +|> extend * +-- +ERROR: Syntax error: Unexpected "*" [at 2:11] +|> extend * + ^ +== + +select 1 x +|> extend * except (x) +-- +ERROR: Syntax error: Unexpected "*" [at 2:11] +|> extend * except (x) + ^ +== + +# Dot-star is allowed on EXTEND, with the optional modifiers, including +# on the output of window functions. +select 1 +|> EXTEND x.*, + f(x).* except(a), + (1+x).* replace(a as b), + (sum(x) OVER ()).* +-- +QueryStatement [0-115] [select 1 |...OVER ()).*] + Query [0-115] [select 1 |...OVER ()).*] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeExtend [9-115] [|> EXTEND...OVER ()).*] + Select [12-115] [EXTEND x.*...OVER ()).*] + SelectList [19-115] [x.*,...OVER ()).*] + SelectColumn [19-22] [x.*] + DotStar [19-22] [x.*] + PathExpression [19-20] [x] + Identifier(x) [19-20] [x] + SelectColumn [34-50] [f(x).* except(a)] + DotStarWithModifiers [34-50] [f(x).* except(a)] + FunctionCall [34-38] [f(x)] + PathExpression [34-35] [f] + Identifier(f) [34-35] [f] + PathExpression [36-37] [x] + Identifier(x) [36-37] [x] + StarModifiers [41-50] [except(a)] + StarExceptList [41-50] [except(a)] + Identifier(a) [48-49] [a] + SelectColumn [62-85] [(1+x).* replace(a as b)] + DotStarWithModifiers [62-85] [(1+x).* replace(a as b)] + BinaryExpression(+) [63-66] [1+x] + IntLiteral(1) [63-64] [1] + PathExpression [65-66] [x] + Identifier(x) [65-66] [x] + StarModifiers [70-85] [replace(a as b)] + StarReplaceItem [78-84] [a as b] + PathExpression [78-79] [a] + Identifier(a) [78-79] [a] + Identifier(b) [83-84] [b] + SelectColumn [97-115] [(sum(x) OVER ()).*] + DotStar [97-115] [(sum(x) OVER ()).*] + AnalyticFunctionCall [98-112] [sum(x) OVER ()] + FunctionCall [98-104] [sum(x)] + PathExpression [98-101] [sum] + Identifier(sum) [98-101] [sum] + PathExpression [102-103] [x] + Identifier(x) [102-103] [x] + WindowSpecification [110-112] [()] +-- +SELECT + 1 +|> EXTEND + x.*, + f(x).* EXCEPT (a), + (1 + x).* REPLACE (a AS b), + (sum(x) OVER ()).* +== + +# The precedence allows binding .* here. +select 1 +|> EXTEND sum(x) OVER ().* +-- +QueryStatement [0-35] [select 1 |...OVER ().*] + Query [0-35] [select 1 |...OVER ().*] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeExtend [9-35] [|> EXTEND sum(x) OVER ().*] + Select [12-35] [EXTEND sum(x) OVER ().*] + SelectList [19-35] [sum(x) OVER ().*] + SelectColumn [19-35] [sum(x) OVER ().*] + DotStar [19-35] [sum(x) OVER ().*] + AnalyticFunctionCall [19-33] [sum(x) OVER ()] + FunctionCall [19-25] [sum(x)] + PathExpression [19-22] [sum] + Identifier(sum) [19-22] [sum] + PathExpression [23-24] [x] + Identifier(x) [23-24] [x] + WindowSpecification [31-33] [()] +-- +SELECT + 1 +|> EXTEND + sum(x) OVER ().* +== + +# Precedence makes this illegal. +select 1 +|> EXTEND 1+sum(x) OVER ().* +-- +ERROR: Syntax error: Unexpected "*" [at 2:28] +|> EXTEND 1+sum(x) OVER ().* + ^ +== + +select 1 +|> EXTEND sum(x).* OVER () +-- +ERROR: Syntax error: OVER keyword must follow a function call [at 2:20] +|> EXTEND sum(x).* OVER () + ^ +== + +select 1 +|> EXTEND sum(x.*) OVER () +-- +ERROR: Syntax error: Unexpected "*" [at 2:17] +|> EXTEND sum(x.*) OVER () + ^ +== + +# These are disallowed in the analyzer. +select 1 x +|> extend count(*), sum(x) over () +-- +QueryStatement [0-45] [select 1 x...x) over ()] + Query [0-45] [select 1 x...x) over ()] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeExtend [11-45] [|> extend...x) over ()] + Select [14-45] [extend count...x) over ()] + SelectList [21-45] [count(*), sum(x) over ()] + SelectColumn [21-29] [count(*)] + FunctionCall [21-29] [count(*)] + PathExpression [21-26] [count] + Identifier(count) [21-26] [count] + Star(*) [27-28] [*] + SelectColumn [31-45] [sum(x) over ()] + AnalyticFunctionCall [31-45] [sum(x) over ()] + FunctionCall [31-37] [sum(x)] + PathExpression [31-34] [sum] + Identifier(sum) [31-34] [sum] + PathExpression [35-36] [x] + Identifier(x) [35-36] [x] + WindowSpecification [43-45] [()] +-- +SELECT + 1 AS x +|> EXTEND + count(*), + sum(x) OVER () +== + +select 1 x +|> extend as struct x,x +-- +ERROR: Syntax error: Unexpected keyword AS [at 2:11] +|> extend as struct x,x + ^ +== + +select 1 x +|> limit 10 +|> limit 12 offset 22 +|> limit @x offset @y +|> limit cast(@z as int64) +-- +QueryStatement [0-93] [select 1 x...as int64)] + Query [0-93] [select 1 x...as int64)] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeLimitOffset [11-22] [|> limit 10] + LimitOffset [14-22] [limit 10] + IntLiteral(10) [20-22] [10] + PipeLimitOffset [23-44] [|> limit 12 offset 22] + LimitOffset [26-44] [limit 12 offset 22] + IntLiteral(12) [32-34] [12] + IntLiteral(22) [42-44] [22] + PipeLimitOffset [45-66] [|> limit @x offset @y] + LimitOffset [48-66] [limit @x offset @y] + ParameterExpr [54-56] [@x] + Identifier(x) [55-56] [x] + ParameterExpr [64-66] [@y] + Identifier(y) [65-66] [y] + PipeLimitOffset [67-93] [|> limit cast(@z as int64)] + LimitOffset [70-93] [limit cast(@z as int64)] + CastExpression [76-93] [cast(@z as int64)] + ParameterExpr [81-83] [@z] + Identifier(z) [82-83] [z] + SimpleType [87-92] [int64] + PathExpression [87-92] [int64] + Identifier(int64) [87-92] [int64] +-- +SELECT + 1 AS x +|> LIMIT 10 +|> LIMIT 12 OFFSET 22 +|> LIMIT @x OFFSET @y +|> LIMIT CAST(@z AS int64) +== + +select 1 x +|> limit +-- +ERROR: Syntax error: Unexpected end of statement [at 2:9] +|> limit + ^ +== + +select 1 x +|> offset 20 +-- +ERROR: Syntax error: Expected keyword JOIN but got keyword OFFSET [at 2:4] +|> offset 20 + ^ +== + +# LIMIT takes a constant expression. +select 1 x +|> limit {{x|1+2|f(1)}} +-- +ALTERNATION GROUP: x +-- +QueryStatement [0-21] [select 1 x |> limit x] + Query [0-21] [select 1 x |> limit x] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeLimitOffset [11-21] [|> limit x] + LimitOffset [14-21] [limit x] + PathExpression [20-21] [x] + Identifier(x) [20-21] [x] +-- +SELECT + 1 AS x +|> LIMIT x +-- +ALTERNATION GROUP: 1+2 +-- +QueryStatement [0-23] [select 1 x |> limit 1+2] + Query [0-23] [select 1 x |> limit 1+2] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeLimitOffset [11-23] [|> limit 1+2] + LimitOffset [14-23] [limit 1+2] + BinaryExpression(+) [20-23] [1+2] + IntLiteral(1) [20-21] [1] + IntLiteral(2) [22-23] [2] +-- +SELECT + 1 AS x +|> LIMIT 1 + 2 +-- +ALTERNATION GROUP: f(1) +-- +QueryStatement [0-24] [select 1 x |> limit f(1)] + Query [0-24] [select 1 x |> limit f(1)] + Select [0-10] [select 1 x] + SelectList [7-10] [1 x] + SelectColumn [7-10] [1 x] + IntLiteral(1) [7-8] [1] + Alias [9-10] [x] + Identifier(x) [9-10] [x] + PipeLimitOffset [11-24] [|> limit f(1)] + LimitOffset [14-24] [limit f(1)] + FunctionCall [20-24] [f(1)] + PathExpression [20-21] [f] + Identifier(f) [20-21] [f] + IntLiteral(1) [22-23] [1] +-- +SELECT + 1 AS x +|> LIMIT f(1) +== + +from t +|> static_describe +|> static_describe +|> where true +-- +QueryStatement [0-58] [from t |>...where true] + Query [0-58] [from t |>...where true] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeStaticDescribe [7-25] [|> static_describe] + PipeStaticDescribe [26-44] [|> static_describe] + PipeWhere [45-58] [|> where true] + WhereClause [48-58] [where true] + BooleanLiteral(true) [54-58] [true] +-- +FROM + t +|> STATIC_DESCRIBE +|> STATIC_DESCRIBE +|> WHERE + true +== + +from t +|> @{hint=1} static_describe +-- +ERROR: Syntax error: Expected keyword JOIN but got "@" [at 2:4] +|> @{hint=1} static_describe + ^ +== + +# It's a common mistake to try writing a pipe operator directly after the WITH, +# without a query. +WITH q AS (select 1 x) +|> where x = 5 +-- +ERROR: Syntax error: A pipe operator cannot follow the WITH clause before the main query; The main query usually starts with SELECT or FROM here [at 2:1] +|> where x = 5 +^ +== + +WITH q AS (select 1 x) +|> from q +-- +ERROR: Syntax error: A pipe operator cannot follow the WITH clause before the main query; The main query usually starts with SELECT or FROM here [at 2:1] +|> from q +^ +== + +[default language_features={{|PIPES}}] +WITH q AS (select 1 x) +|> select * from q +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Unexpected | [at 2:1] +|> select * from q +^ +-- +ALTERNATION GROUP: PIPES +-- +ERROR: Syntax error: A pipe operator cannot follow the WITH clause before the main query; The main query usually starts with SELECT or FROM here [at 2:1] +|> select * from q +^ +== + +WITH q AS (select 1 x) |> +-- +ERROR: Syntax error: A pipe operator cannot follow the WITH clause before the main query; The main query usually starts with SELECT or FROM here [at 1:24] +WITH q AS (select 1 x) |> + ^ diff --git a/zetasql/parser/testdata/pipe_rename.test b/zetasql/parser/testdata/pipe_rename.test new file mode 100644 index 000000000..32b8e44ba --- /dev/null +++ b/zetasql/parser/testdata/pipe_rename.test @@ -0,0 +1,114 @@ +select 1 +|> RENAME x AS y +|> RENAME `a b` AS `d e`, x AS y, aaa bbb +-- +QueryStatement [0-67] [select 1 |...y, aaa bbb] + Query [0-67] [select 1 |...y, aaa bbb] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeRename [9-25] [|> RENAME x AS y] + PipeRenameItem [19-25] [x AS y] + Identifier(x) [19-20] [x] + Identifier(y) [24-25] [y] + PipeRename [26-67] [|> RENAME...y, aaa bbb] + PipeRenameItem [36-50] [`a b` AS `d e`] + Identifier(`a b`) [36-41] [`a b`] + Identifier(`d e`) [45-50] [`d e`] + PipeRenameItem [52-58] [x AS y] + Identifier(x) [52-53] [x] + Identifier(y) [57-58] [y] + PipeRenameItem [60-67] [aaa bbb] + Identifier(aaa) [60-63] [aaa] + Identifier(bbb) [64-67] [bbb] +-- +SELECT + 1 +|> RENAME x AS y +|> RENAME `a b` AS `d e`, x AS y, aaa AS bbb +== + +# Trailing commas +select 1 +|> RENAME a b, +|> RENAME a b, c AS d, +-- +QueryStatement [0-46] [select 1 |...b, c AS d,] + Query [0-45] [select 1 |...b, c AS d] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeRename [9-22] [|> RENAME a b] + PipeRenameItem [19-22] [a b] + Identifier(a) [19-20] [a] + Identifier(b) [21-22] [b] + PipeRename [24-45] [|> RENAME a b, c AS d] + PipeRenameItem [34-37] [a b] + Identifier(a) [34-35] [a] + Identifier(b) [36-37] [b] + PipeRenameItem [39-45] [c AS d] + Identifier(c) [39-40] [c] + Identifier(d) [44-45] [d] +-- +SELECT + 1 +|> RENAME a AS b +|> RENAME a AS b, c AS d +== + +select 1 +|> RENAME +-- +ERROR: Syntax error: Unexpected end of statement [at 2:10] +|> RENAME + ^ +== + +select 1 +|> RENAME , +-- +ERROR: Syntax error: Unexpected "," [at 2:11] +|> RENAME , + ^ +== + +select 1 +|> RENAME abc +-- +ERROR: Syntax error: Unexpected end of statement [at 2:14] +|> RENAME abc + ^ +== + +select 1 +|> RENAME abc AS +-- +ERROR: Syntax error: Unexpected end of statement [at 2:17] +|> RENAME abc AS + ^ +== + +select 1 +|> RENAME x.abc AS y +-- +ERROR: Syntax error: Pipe RENAME can only rename columns by name alone; Renaming columns under table aliases or fields under paths is not supported [at 2:11] +|> RENAME x.abc AS y + ^ +== + +select 1 +|> RENAME f() AS y +-- +ERROR: Syntax error: Unexpected "(" [at 2:12] +|> RENAME f() AS y + ^ +== + +select 1 +|> RENAME x AS y.z +-- +ERROR: Syntax error: Expected end of input but got "." [at 2:17] +|> RENAME x AS y.z + ^ diff --git a/zetasql/parser/testdata/pipe_set.test b/zetasql/parser/testdata/pipe_set.test new file mode 100644 index 000000000..ff93f945a --- /dev/null +++ b/zetasql/parser/testdata/pipe_set.test @@ -0,0 +1,184 @@ +select x +|> SET x = 1+2 {{|,}} +-- +ALTERNATION GROUP: +-- +QueryStatement [0-23] [select x |> SET x = 1+2] + Query [0-23] [select x |> SET x = 1+2] + Select [0-8] [select x] + SelectList [7-8] [x] + SelectColumn [7-8] [x] + PathExpression [7-8] [x] + Identifier(x) [7-8] [x] + PipeSet [9-23] [|> SET x = 1+2] + PipeSetItem [16-23] [x = 1+2] + Identifier(x) [16-17] [x] + BinaryExpression(+) [20-23] [1+2] + IntLiteral(1) [20-21] [1] + IntLiteral(2) [22-23] [2] +-- +SELECT + x +|> SET x = 1 + 2 +-- +ALTERNATION GROUP: , +-- +QueryStatement [0-25] [select x |> SET x = 1+2 ,] + Query [0-25] [select x |> SET x = 1+2 ,] + Select [0-8] [select x] + SelectList [7-8] [x] + SelectColumn [7-8] [x] + PathExpression [7-8] [x] + Identifier(x) [7-8] [x] + PipeSet [9-25] [|> SET x = 1+2 ,] + PipeSetItem [16-23] [x = 1+2] + Identifier(x) [16-17] [x] + BinaryExpression(+) [20-23] [1+2] + IntLiteral(1) [20-21] [1] + IntLiteral(2) [22-23] [2] +-- +SELECT + x +|> SET x = 1 + 2 +== + +select x +|> set x = 1+2, y = 3, z = z {{|,}} +-- +ALTERNATION GROUP: +-- +QueryStatement [0-37] [select x |...= 3, z = z] + Query [0-37] [select x |...= 3, z = z] + Select [0-8] [select x] + SelectList [7-8] [x] + SelectColumn [7-8] [x] + PathExpression [7-8] [x] + Identifier(x) [7-8] [x] + PipeSet [9-37] [|> set x = 1+2, y = 3, z = z] + PipeSetItem [16-23] [x = 1+2] + Identifier(x) [16-17] [x] + BinaryExpression(+) [20-23] [1+2] + IntLiteral(1) [20-21] [1] + IntLiteral(2) [22-23] [2] + PipeSetItem [25-30] [y = 3] + Identifier(y) [25-26] [y] + IntLiteral(3) [29-30] [3] + PipeSetItem [32-37] [z = z] + Identifier(z) [32-33] [z] + PathExpression [36-37] [z] + Identifier(z) [36-37] [z] +-- +SELECT + x +|> SET x = 1 + 2, y = 3, z = z +-- +ALTERNATION GROUP: , +-- +QueryStatement [0-39] [select x |...3, z = z ,] + Query [0-39] [select x |...3, z = z ,] + Select [0-8] [select x] + SelectList [7-8] [x] + SelectColumn [7-8] [x] + PathExpression [7-8] [x] + Identifier(x) [7-8] [x] + PipeSet [9-39] [|> set x = 1+2, y = 3, z = z ,] + PipeSetItem [16-23] [x = 1+2] + Identifier(x) [16-17] [x] + BinaryExpression(+) [20-23] [1+2] + IntLiteral(1) [20-21] [1] + IntLiteral(2) [22-23] [2] + PipeSetItem [25-30] [y = 3] + Identifier(y) [25-26] [y] + IntLiteral(3) [29-30] [3] + PipeSetItem [32-37] [z = z] + Identifier(z) [32-33] [z] + PathExpression [36-37] [z] + Identifier(z) [36-37] [z] +-- +SELECT + x +|> SET x = 1 + 2, y = 3, z = z +== + +select x +|>{{| set}}{{|,}} +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected keyword JOIN but got end of statement [at 2:3] +|> + ^ +-- +ALTERNATION GROUP: , +-- +ERROR: Syntax error: Expected keyword JOIN but got "," [at 2:3] +|>, + ^ +-- +ALTERNATION GROUP: set, +-- +ERROR: Syntax error: Unexpected end of statement [at 2:7] +|> set + ^ +-- +ALTERNATION GROUP: set,, +-- +ERROR: Syntax error: Unexpected "," [at 2:7] +|> set, + ^ +== + +select x +|> {{|set}} x +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected keyword JOIN but got identifier "x" [at 2:5] +|> x + ^ +-- +ALTERNATION GROUP: set +-- +ERROR: Syntax error: Expected "." or "=" but got end of statement [at 2:9] +|> set x + ^ +== + +select x +|> {{|set}} x y +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected keyword JOIN but got identifier "x" [at 2:5] +|> x y + ^ +-- +ALTERNATION GROUP: set +-- +ERROR: Syntax error: Expected "." or "=" but got identifier "y" [at 2:10] +|> set x y + ^ +== + +select x +|> {{|set}} x AS y +-- +ALTERNATION GROUP: +-- +ERROR: Syntax error: Expected keyword JOIN but got identifier "x" [at 2:5] +|> x AS y + ^ +-- +ALTERNATION GROUP: set +-- +ERROR: Syntax error: Expected "." or "=" but got keyword AS [at 2:10] +|> set x AS y + ^ +== + +select x +|> set t.y = x +-- +ERROR: Syntax error: Pipe SET can only update columns by column name alone; Setting columns under table aliases or fields under paths is not supported [at 2:8] +|> set t.y = x + ^ diff --git a/zetasql/parser/testdata/pipe_set_operation.test b/zetasql/parser/testdata/pipe_set_operation.test new file mode 100644 index 000000000..435140aec --- /dev/null +++ b/zetasql/parser/testdata/pipe_set_operation.test @@ -0,0 +1,497 @@ +select 1 +|> union all (select 2) +-- +QueryStatement [0-32] [select 1 |...(select 2)] + Query [0-32] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-32] [|> union all (select 2)] + SetOperationMetadata [12-21] [union all] + SetOperationType [12-17] [union] + SetOperationAllOrDistinct [18-21] [all] + Query [23-31] [select 2] + Select [23-31] [select 2] + SelectList [30-31] [2] + SelectColumn [30-31] [2] + IntLiteral(2) [30-31] [2] +-- +SELECT + 1 +|> UNION ALL + ( + SELECT + 2 + ) +== + +# Hints, and multiple rhs tables. +select 1 +|> union @{hint=1} all (select 2), (select 3) +-- +QueryStatement [0-54] [select 1 |...(select 3)] + Query [0-53] [select 1 |...(select 3] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-53] [|> union @...(select 3] + SetOperationMetadata [12-31] [union @{hint=1} all] + SetOperationType [12-17] [union] + SetOperationAllOrDistinct [28-31] [all] + Hint [18-27] [@{hint=1}] + HintEntry [20-26] [hint=1] + Identifier(hint) [20-24] [hint] + IntLiteral(1) [25-26] [1] + Query [33-41] [select 2] + Select [33-41] [select 2] + SelectList [40-41] [2] + SelectColumn [40-41] [2] + IntLiteral(2) [40-41] [2] + Query [45-53] [select 3] + Select [45-53] [select 3] + SelectList [52-53] [3] + SelectColumn [52-53] [3] + IntLiteral(3) [52-53] [3] +-- +SELECT + 1 +|> UNION @{ hint = 1 } ALL + ( + SELECT + 2 + ), + ( + SELECT + 3 + ) +== + +# Complex queries on the rhs, with pipes or without. +select 1 +|> union all +(select * from t where x), +(select 1 |> where y) +-- +QueryStatement [0-70] [select 1 |...> where y)] + Query [0-69] [select 1 |...|> where y] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-69] [|> union all...|> where y] + SetOperationMetadata [12-21] [union all] + SetOperationType [12-17] [union] + SetOperationAllOrDistinct [18-21] [all] + Query [23-46] [select * from t where x] + Select [23-46] [select * from t where x] + SelectList [30-31] [*] + SelectColumn [30-31] [*] + Star(*) [30-31] [*] + FromClause [32-38] [from t] + TablePathExpression [37-38] [t] + PathExpression [37-38] [t] + Identifier(t) [37-38] [t] + WhereClause [39-46] [where x] + PathExpression [45-46] [x] + Identifier(x) [45-46] [x] + Query [50-69] [select 1 |> where y] + Select [50-58] [select 1] + SelectList [57-58] [1] + SelectColumn [57-58] [1] + IntLiteral(1) [57-58] [1] + PipeWhere [59-69] [|> where y] + WhereClause [62-69] [where y] + PathExpression [68-69] [y] + Identifier(y) [68-69] [y] +-- +SELECT + 1 +|> UNION ALL + ( + SELECT + * + FROM + t + WHERE + x + ), + ( + SELECT + 1 + |> WHERE + y) +== + +select 1 +|> union (select 2) +-- +ERROR: Syntax error: Expected keyword ALL or keyword DISTINCT but got "(" [at 2:10] +|> union (select 2) + ^ +== + +select 1 +|> union all (select 2) union all (select 3) +-- +ERROR: Syntax error: Expected end of input but got keyword UNION [at 2:25] +|> union all (select 2) union all (select 3) + ^ +== + +select 1 +|> union all select 2 +-- +ERROR: Syntax error: Expected "(" but got keyword SELECT [at 2:14] +|> union all select 2 + ^ +== + +select 1 +|> union all other_table +-- +ERROR: Syntax error: Expected "(" but got identifier "other_table" [at 2:14] +|> union all other_table + ^ +== + +# All combinations of simple set ops work as pipe operator keywords. +select 1 +|> {{union|intersect|except}} {{all|distinct}} (select 2) +-- +ALTERNATION GROUP: union,all +-- +QueryStatement [0-32] [select 1 |...(select 2)] + Query [0-32] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-32] [|> union all (select 2)] + SetOperationMetadata [12-21] [union all] + SetOperationType [12-17] [union] + SetOperationAllOrDistinct [18-21] [all] + Query [23-31] [select 2] + Select [23-31] [select 2] + SelectList [30-31] [2] + SelectColumn [30-31] [2] + IntLiteral(2) [30-31] [2] +-- +SELECT + 1 +|> UNION ALL + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: union,distinct +-- +QueryStatement [0-37] [select 1 |...(select 2)] + Query [0-37] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-37] [|> union distinct (select 2)] + SetOperationMetadata [12-26] [union distinct] + SetOperationType [12-17] [union] + SetOperationAllOrDistinct [18-26] [distinct] + Query [28-36] [select 2] + Select [28-36] [select 2] + SelectList [35-36] [2] + SelectColumn [35-36] [2] + IntLiteral(2) [35-36] [2] +-- +SELECT + 1 +|> UNION DISTINCT + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: intersect,all +-- +QueryStatement [0-36] [select 1 |...(select 2)] + Query [0-36] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-36] [|> intersect all (select 2)] + SetOperationMetadata [12-25] [intersect all] + SetOperationType [12-21] [intersect] + SetOperationAllOrDistinct [22-25] [all] + Query [27-35] [select 2] + Select [27-35] [select 2] + SelectList [34-35] [2] + SelectColumn [34-35] [2] + IntLiteral(2) [34-35] [2] +-- +SELECT + 1 +|> INTERSECT ALL + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: intersect,distinct +-- +QueryStatement [0-41] [select 1 |...(select 2)] + Query [0-41] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-41] [|> intersect...(select 2)] + SetOperationMetadata [12-30] [intersect distinct] + SetOperationType [12-21] [intersect] + SetOperationAllOrDistinct [22-30] [distinct] + Query [32-40] [select 2] + Select [32-40] [select 2] + SelectList [39-40] [2] + SelectColumn [39-40] [2] + IntLiteral(2) [39-40] [2] +-- +SELECT + 1 +|> INTERSECT DISTINCT + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: except,all +-- +QueryStatement [0-33] [select 1 |...(select 2)] + Query [0-33] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-33] [|> except all (select 2)] + SetOperationMetadata [12-22] [except all] + SetOperationType [12-18] [except] + SetOperationAllOrDistinct [19-22] [all] + Query [24-32] [select 2] + Select [24-32] [select 2] + SelectList [31-32] [2] + SelectColumn [31-32] [2] + IntLiteral(2) [31-32] [2] +-- +SELECT + 1 +|> EXCEPT ALL + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: except,distinct +-- +QueryStatement [0-38] [select 1 |...(select 2)] + Query [0-38] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-38] [|> except distinct (select 2)] + SetOperationMetadata [12-27] [except distinct] + SetOperationType [12-18] [except] + SetOperationAllOrDistinct [19-27] [distinct] + Query [29-37] [select 2] + Select [29-37] [select 2] + SelectList [36-37] [2] + SelectColumn [36-37] [2] + IntLiteral(2) [36-37] [2] +-- +SELECT + 1 +|> EXCEPT DISTINCT + ( + SELECT + 2 + ) +== + +# CORRESPONDING modifiers work. +select 1 +|> UNION ALL CORRESPONDING (select 2) +|> INTERSECT DISTINCT CORRESPONDING BY (x) (select 3) +|> EXCEPT ALL STRICT CORRESPONDING BY (x) (select 4) +-- +QueryStatement [0-153] [select 1 |...(select 4)] + Query [0-153] [select 1 |...(select 4)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-46] [|> UNION ALL...(select 2)] + SetOperationMetadata [12-35] [UNION ALL CORRESPONDING] + SetOperationType [12-17] [UNION] + SetOperationAllOrDistinct [18-21] [ALL] + SetOperationColumnMatchMode [22-35] [CORRESPONDING] + Query [37-45] [select 2] + Select [37-45] [select 2] + SelectList [44-45] [2] + SelectColumn [44-45] [2] + IntLiteral(2) [44-45] [2] + PipeSetOperation [47-100] [|> INTERSECT...(select 3)] + SetOperationMetadata [50-89] [INTERSECT...RRESPONDING BY (x)] + SetOperationType [50-59] [INTERSECT] + SetOperationAllOrDistinct [60-68] [DISTINCT] + SetOperationColumnMatchMode [69-85] [CORRESPONDING BY] + ColumnList [86-89] [(x)] + Identifier(x) [87-88] [x] + Query [91-99] [select 3] + Select [91-99] [select 3] + SelectList [98-99] [3] + SelectColumn [98-99] [3] + IntLiteral(3) [98-99] [3] + PipeSetOperation [101-153] [|> EXCEPT...(select 4)] + SetOperationMetadata [104-142] [EXCEPT ALL...RESPONDING BY (x)] + SetOperationType [104-110] [EXCEPT] + SetOperationAllOrDistinct [111-114] [ALL] + SetOperationColumnMatchMode [122-138] [CORRESPONDING BY] + SetOperationColumnPropagationMode [115-121] [STRICT] + ColumnList [139-142] [(x)] + Identifier(x) [140-141] [x] + Query [144-152] [select 4] + Select [144-152] [select 4] + SelectList [151-152] [4] + SelectColumn [151-152] [4] + IntLiteral(4) [151-152] [4] +-- +SELECT + 1 +|> UNION ALL CORRESPONDING + ( + SELECT + 2 + ) +|> INTERSECT DISTINCT CORRESPONDING BY (x) + ( + SELECT + 3 + ) +|> EXCEPT ALL STRICT CORRESPONDING BY (x) + ( + SELECT + 4 + ) +== + +# FULL/LEFT modifiers work at the start of the pipe operator +select 1 +|> {{FULL|FULL OUTER|LEFT|LEFT OUTER}} UNION ALL (select 2) +-- +ALTERNATION GROUP: FULL +-- +QueryStatement [0-37] [select 1 |...(select 2)] + Query [0-37] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-37] [|> FULL UNION ALL (select 2)] + SetOperationMetadata [12-26] [FULL UNION ALL] + SetOperationType [17-22] [UNION] + SetOperationAllOrDistinct [23-26] [ALL] + SetOperationColumnPropagationMode [12-16] [FULL] + Query [28-36] [select 2] + Select [28-36] [select 2] + SelectList [35-36] [2] + SelectColumn [35-36] [2] + IntLiteral(2) [35-36] [2] +-- +SELECT + 1 +|> FULL UNION ALL + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: FULL OUTER +-- +QueryStatement [0-43] [select 1 |...(select 2)] + Query [0-43] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-43] [|> FULL OUTER...(select 2)] + SetOperationMetadata [12-32] [FULL OUTER UNION ALL] + SetOperationType [23-28] [UNION] + SetOperationAllOrDistinct [29-32] [ALL] + SetOperationColumnPropagationMode [12-22] [FULL OUTER] + Query [34-42] [select 2] + Select [34-42] [select 2] + SelectList [41-42] [2] + SelectColumn [41-42] [2] + IntLiteral(2) [41-42] [2] +-- +SELECT + 1 +|> FULL UNION ALL + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: LEFT +-- +QueryStatement [0-37] [select 1 |...(select 2)] + Query [0-37] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-37] [|> LEFT UNION ALL (select 2)] + SetOperationMetadata [12-26] [LEFT UNION ALL] + SetOperationType [17-22] [UNION] + SetOperationAllOrDistinct [23-26] [ALL] + SetOperationColumnPropagationMode [12-16] [LEFT] + Query [28-36] [select 2] + Select [28-36] [select 2] + SelectList [35-36] [2] + SelectColumn [35-36] [2] + IntLiteral(2) [35-36] [2] +-- +SELECT + 1 +|> LEFT UNION ALL + ( + SELECT + 2 + ) +-- +ALTERNATION GROUP: LEFT OUTER +-- +QueryStatement [0-43] [select 1 |...(select 2)] + Query [0-43] [select 1 |...(select 2)] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeSetOperation [9-43] [|> LEFT OUTER...(select 2)] + SetOperationMetadata [12-32] [LEFT OUTER UNION ALL] + SetOperationType [23-28] [UNION] + SetOperationAllOrDistinct [29-32] [ALL] + SetOperationColumnPropagationMode [12-22] [LEFT OUTER] + Query [34-42] [select 2] + Select [34-42] [select 2] + SelectList [41-42] [2] + SelectColumn [41-42] [2] + IntLiteral(2) [41-42] [2] +-- +SELECT + 1 +|> LEFT UNION ALL + ( + SELECT + 2 + ) diff --git a/zetasql/parser/testdata/pipe_tablesample.test b/zetasql/parser/testdata/pipe_tablesample.test new file mode 100644 index 000000000..12aebbd8d --- /dev/null +++ b/zetasql/parser/testdata/pipe_tablesample.test @@ -0,0 +1,283 @@ +[default language_features=PIPES] +from t +|> TABLESAMPLE RESERVOIR (100 ROWS) REPEATABLE(10) +|> TABLESAMPLE RESERVOIR (cast(100 as int32) ROWS) REPEATABLE(cast(10 as int32)) +|> TABLESAMPLE RESERVOIR (@param1 ROWS) REPEATABLE(@param2) +|> TABLESAMPLE bernoulli (cast(@param1 as int32) ROWS) + REPEATABLE(cast(@param2 as string)) +-- +QueryStatement [0-295] [from t |>...as string))] + Query [0-295] [from t |>...as string))] + FromQuery [0-6] [from t] + FromClause [0-6] [from t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeTablesample [7-57] [|> TABLESAMPLE...EPEATABLE(10)] + SampleClause [10-57] [TABLESAMPLE...REPEATABLE(10)] + Identifier(RESERVOIR) [22-31] [RESERVOIR] + SampleSize [33-41] [100 ROWS] + IntLiteral(100) [33-36] [100] + SampleSuffix [43-57] [REPEATABLE(10)] + RepeatableClause [43-57] [REPEATABLE(10)] + IntLiteral(10) [54-56] [10] + PipeTablesample [58-138] [|> TABLESAMPLE...as int32))] + SampleClause [61-138] [TABLESAMPLE...as int32))] + Identifier(RESERVOIR) [73-82] [RESERVOIR] + SampleSize [84-107] [cast(100 as int32) ROWS] + CastExpression [84-102] [cast(100 as int32)] + IntLiteral(100) [89-92] [100] + SimpleType [96-101] [int32] + PathExpression [96-101] [int32] + Identifier(int32) [96-101] [int32] + SampleSuffix [109-138] [REPEATABLE(cast(10 as int32))] + RepeatableClause [109-138] [REPEATABLE(cast(10 as int32))] + CastExpression [120-137] [cast(10 as int32)] + IntLiteral(10) [125-127] [10] + SimpleType [131-136] [int32] + PathExpression [131-136] [int32] + Identifier(int32) [131-136] [int32] + PipeTablesample [139-198] [|> TABLESAMPLE...ABLE(@param2)] + SampleClause [142-198] [TABLESAMPLE...EATABLE(@param2)] + Identifier(RESERVOIR) [154-163] [RESERVOIR] + SampleSize [165-177] [@param1 ROWS] + ParameterExpr [165-172] [@param1] + Identifier(param1) [166-172] [param1] + SampleSuffix [179-198] [REPEATABLE(@param2)] + RepeatableClause [179-198] [REPEATABLE(@param2)] + ParameterExpr [190-197] [@param2] + Identifier(param2) [191-197] [param2] + PipeTablesample [199-295] [|> TABLESAMPLE...as string))] + SampleClause [202-295] [TABLESAMPLE...as string))] + Identifier(bernoulli) [214-223] [bernoulli] + SampleSize [225-252] [cast(@param1 as int32) ROWS] + CastExpression [225-247] [cast(@param1 as int32)] + ParameterExpr [230-237] [@param1] + Identifier(param1) [231-237] [param1] + SimpleType [241-246] [int32] + PathExpression [241-246] [int32] + Identifier(int32) [241-246] [int32] + SampleSuffix [260-295] [REPEATABLE...as string))] + RepeatableClause [260-295] [REPEATABLE...as string))] + CastExpression [271-294] [cast(@param2 as string)] + ParameterExpr [276-283] [@param2] + Identifier(param2) [277-283] [param2] + SimpleType [287-293] [string] + PathExpression [287-293] [string] + Identifier(string) [287-293] [string] +-- +FROM + t +|> TABLESAMPLE RESERVOIR(100 ROWS) REPEATABLE (10) +|> TABLESAMPLE RESERVOIR(CAST(100 AS int32) ROWS) REPEATABLE (CAST(10 AS int32)) +|> TABLESAMPLE RESERVOIR(@param1 ROWS) REPEATABLE (@param2) +|> TABLESAMPLE bernoulli(CAST(@param1 AS int32) ROWS) REPEATABLE (CAST(@param2 AS string)) +== + +from Users +|> TABLESAMPLE abc (100 ROWS) +|> TABLESAMPLE abc (0.1 PERCENT) +|> TABLESAMPLE random (10 ROWS PARTITION BY state, zipcode) +-- +QueryStatement [0-133] [from Users..., zipcode)] + Query [0-133] [from Users..., zipcode)] + FromQuery [0-10] [from Users] + FromClause [0-10] [from Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-40] [|> TABLESAMPLE abc (100 ROWS)] + SampleClause [14-40] [TABLESAMPLE abc (100 ROWS)] + Identifier(abc) [26-29] [abc] + SampleSize [31-39] [100 ROWS] + IntLiteral(100) [31-34] [100] + PipeTablesample [41-73] [|> TABLESAMPLE...1 PERCENT)] + SampleClause [44-73] [TABLESAMPLE abc (0.1 PERCENT)] + Identifier(abc) [56-59] [abc] + SampleSize [61-72] [0.1 PERCENT] + FloatLiteral(0.1) [61-64] [0.1] + PipeTablesample [74-133] [|> TABLESAMPLE..., zipcode)] + SampleClause [77-133] [TABLESAMPLE..., zipcode)] + Identifier(random) [89-95] [random] + SampleSize [97-132] [10 ROWS PARTITION...e, zipcode] + IntLiteral(10) [97-99] [10] + PartitionBy [105-132] [PARTITION BY state, zipcode] + PathExpression [118-123] [state] + Identifier(state) [118-123] [state] + PathExpression [125-132] [zipcode] + Identifier(zipcode) [125-132] [zipcode] +-- +FROM + Users +|> TABLESAMPLE abc(100 ROWS) +|> TABLESAMPLE abc(0.1 PERCENT) +|> TABLESAMPLE random(10 ROWS PARTITION BY state, zipcode) +== + +FROM Users +|> TABLESAMPLE RESERVOIR (1 ROWS) + WITH WEIGHT {{|weight|AS weight}} {{|REPEATABLE(1)}} +-- +ALTERNATION GROUP: +-- +QueryStatement [0-59] [FROM Users...WITH WEIGHT] + Query [0-59] [FROM Users...WITH WEIGHT] + FromQuery [0-10] [FROM Users] + FromClause [0-10] [FROM Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-59] [|> TABLESAMPLE...WITH WEIGHT] + SampleClause [14-59] [TABLESAMPLE...WITH WEIGHT] + Identifier(RESERVOIR) [26-35] [RESERVOIR] + SampleSize [37-43] [1 ROWS] + IntLiteral(1) [37-38] [1] + SampleSuffix [48-59] [WITH WEIGHT] + WithWeight [48-59] [WITH WEIGHT] +-- +FROM + Users +|> TABLESAMPLE RESERVOIR(1 ROWS) WITH WEIGHT +-- +ALTERNATION GROUP: REPEATABLE(1) +-- +QueryStatement [0-74] [FROM Users...REPEATABLE(1)] + Query [0-74] [FROM Users...REPEATABLE(1)] + FromQuery [0-10] [FROM Users] + FromClause [0-10] [FROM Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-74] [|> TABLESAMPLE...REPEATABLE(1)] + SampleClause [14-74] [TABLESAMPLE...REPEATABLE(1)] + Identifier(RESERVOIR) [26-35] [RESERVOIR] + SampleSize [37-43] [1 ROWS] + IntLiteral(1) [37-38] [1] + SampleSuffix [48-74] [WITH WEIGHT REPEATABLE(1)] + WithWeight [48-74] [WITH WEIGHT REPEATABLE(1)] + RepeatableClause [61-74] [REPEATABLE(1)] + IntLiteral(1) [72-73] [1] +-- +FROM + Users +|> TABLESAMPLE RESERVOIR(1 ROWS) WITH WEIGHT REPEATABLE (1) +-- +ALTERNATION GROUP: weight, +-- +QueryStatement [0-66] [FROM Users...WEIGHT weight] + Query [0-66] [FROM Users...WEIGHT weight] + FromQuery [0-10] [FROM Users] + FromClause [0-10] [FROM Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-66] [|> TABLESAMPLE...WEIGHT weight] + SampleClause [14-66] [TABLESAMPLE...WEIGHT weight] + Identifier(RESERVOIR) [26-35] [RESERVOIR] + SampleSize [37-43] [1 ROWS] + IntLiteral(1) [37-38] [1] + SampleSuffix [48-66] [WITH WEIGHT weight] + WithWeight [48-66] [WITH WEIGHT weight] + Alias [60-66] [weight] + Identifier(weight) [60-66] [weight] +-- +FROM + Users +|> TABLESAMPLE RESERVOIR(1 ROWS) WITH WEIGHT AS weight +-- +ALTERNATION GROUP: weight,REPEATABLE(1) +-- +QueryStatement [0-80] [FROM Users...REPEATABLE(1)] + Query [0-80] [FROM Users...REPEATABLE(1)] + FromQuery [0-10] [FROM Users] + FromClause [0-10] [FROM Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-80] [|> TABLESAMPLE...REPEATABLE(1)] + SampleClause [14-80] [TABLESAMPLE...REPEATABLE(1)] + Identifier(RESERVOIR) [26-35] [RESERVOIR] + SampleSize [37-43] [1 ROWS] + IntLiteral(1) [37-38] [1] + SampleSuffix [48-80] [WITH WEIGHT...REPEATABLE(1)] + WithWeight [48-80] [WITH WEIGHT...REPEATABLE(1)] + Alias [60-66] [weight] + Identifier(weight) [60-66] [weight] + RepeatableClause [67-80] [REPEATABLE(1)] + IntLiteral(1) [78-79] [1] +-- +FROM + Users +|> TABLESAMPLE RESERVOIR(1 ROWS) WITH WEIGHT AS weight REPEATABLE (1) +-- +ALTERNATION GROUP: AS weight, +-- +QueryStatement [0-69] [FROM Users...AS weight] + Query [0-69] [FROM Users...AS weight] + FromQuery [0-10] [FROM Users] + FromClause [0-10] [FROM Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-69] [|> TABLESAMPLE...AS weight] + SampleClause [14-69] [TABLESAMPLE...AS weight] + Identifier(RESERVOIR) [26-35] [RESERVOIR] + SampleSize [37-43] [1 ROWS] + IntLiteral(1) [37-38] [1] + SampleSuffix [48-69] [WITH WEIGHT AS weight] + WithWeight [48-69] [WITH WEIGHT AS weight] + Alias [60-69] [AS weight] + Identifier(weight) [63-69] [weight] +-- +FROM + Users +|> TABLESAMPLE RESERVOIR(1 ROWS) WITH WEIGHT AS weight +-- +ALTERNATION GROUP: AS weight,REPEATABLE(1) +-- +QueryStatement [0-83] [FROM Users...REPEATABLE(1)] + Query [0-83] [FROM Users...REPEATABLE(1)] + FromQuery [0-10] [FROM Users] + FromClause [0-10] [FROM Users] + TablePathExpression [5-10] [Users] + PathExpression [5-10] [Users] + Identifier(Users) [5-10] [Users] + PipeTablesample [11-83] [|> TABLESAMPLE...REPEATABLE(1)] + SampleClause [14-83] [TABLESAMPLE...REPEATABLE(1)] + Identifier(RESERVOIR) [26-35] [RESERVOIR] + SampleSize [37-43] [1 ROWS] + IntLiteral(1) [37-38] [1] + SampleSuffix [48-83] [WITH WEIGHT...REPEATABLE(1)] + WithWeight [48-83] [WITH WEIGHT...REPEATABLE(1)] + Alias [60-69] [AS weight] + Identifier(weight) [63-69] [weight] + RepeatableClause [70-83] [REPEATABLE(1)] + IntLiteral(1) [81-82] [1] +-- +FROM + Users +|> TABLESAMPLE RESERVOIR(1 ROWS) WITH WEIGHT AS weight REPEATABLE (1) +== + +FROM Users +|> TABLESAMPLE +-- +ERROR: Syntax error: Unexpected end of statement [at 2:15] +|> TABLESAMPLE + ^ +== + +FROM Users +|> TABLESAMPLE random +-- +ERROR: Syntax error: Expected "(" but got end of statement [at 2:22] +|> TABLESAMPLE random + ^ +== + +FROM Users +|> TABLESAMPLE random() +-- +ERROR: Syntax error: Unexpected ")" [at 2:23] +|> TABLESAMPLE random() + ^ diff --git a/zetasql/parser/testdata/pipe_unpivot.test b/zetasql/parser/testdata/pipe_unpivot.test new file mode 100644 index 000000000..df884dafc --- /dev/null +++ b/zetasql/parser/testdata/pipe_unpivot.test @@ -0,0 +1,372 @@ +# Most of these tests came from unpivot.test, with cases involving combinations +# with JOIN, TVFs, subqueries, etc, removed as irrelevant. +# After we remove copybara stripping, we might combine these back into the +# main test file, with alternations on whether to add "|>". +[default language_features=PIPES] + +FROM t +|> UNPIVOT (a FOR c IN (x, y)) +-- +QueryStatement [0-37] [FROM t |>...IN (x, y))] + Query [0-37] [FROM t |>...IN (x, y))] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-37] [|> UNPIVOT (a FOR c IN (x, y))] + UnpivotClause [10-37] [UNPIVOT (a FOR c IN (x, y))] + PathExpressionList [19-20] [a] + PathExpression [19-20] [a] + Identifier(a) [19-20] [a] + PathExpression [25-26] [c] + Identifier(c) [25-26] [c] + UnpivotInItemList [30-36] [(x, y)] + UnpivotInItem [31-32] [x] + PathExpressionList [31-32] [x] + PathExpression [31-32] [x] + Identifier(x) [31-32] [x] + UnpivotInItem [34-35] [y] + PathExpressionList [34-35] [y] + PathExpression [34-35] [y] + Identifier(y) [34-35] [y] +-- +FROM + t +|> UNPIVOT(a FOR c IN ((x), (y))) +== + +# Simple UNPIVOT with EXCLUDE NULLS +# Output has alias without AS. +FROM t +|> UNPIVOT EXCLUDE NULLS (a FOR c IN (x, y)) unp +-- +QueryStatement [0-55] [FROM t |>...x, y)) unp] + Query [0-55] [FROM t |>...x, y)) unp] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-55] [|> UNPIVOT...x, y)) unp] + UnpivotClause(EXCLUDE NULLS) [10-55] [UNPIVOT EXCLUDE...x, y)) unp] + PathExpressionList [33-34] [a] + PathExpression [33-34] [a] + Identifier(a) [33-34] [a] + PathExpression [39-40] [c] + Identifier(c) [39-40] [c] + UnpivotInItemList [44-50] [(x, y)] + UnpivotInItem [45-46] [x] + PathExpressionList [45-46] [x] + PathExpression [45-46] [x] + Identifier(x) [45-46] [x] + UnpivotInItem [48-49] [y] + PathExpressionList [48-49] [y] + PathExpression [48-49] [y] + Identifier(y) [48-49] [y] + Alias [52-55] [unp] + Identifier(unp) [52-55] [unp] +-- +FROM + t +|> UNPIVOT EXCLUDE NULLS (a FOR c IN ((x), (y))) AS unp +== + +# Simple UNPIVOT with INCLUDE NULLS +# Output has alias with AS. +FROM t +|> UNPIVOT INCLUDE NULLS (a FOR c IN (x, y)) AS unp +-- +QueryStatement [0-58] [FROM t |>...y)) AS unp] + Query [0-58] [FROM t |>...y)) AS unp] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-58] [|> UNPIVOT...y)) AS unp] + UnpivotClause(INCLUDE NULLS) [10-58] [UNPIVOT INCLUDE...y)) AS unp] + PathExpressionList [33-34] [a] + PathExpression [33-34] [a] + Identifier(a) [33-34] [a] + PathExpression [39-40] [c] + Identifier(c) [39-40] [c] + UnpivotInItemList [44-50] [(x, y)] + UnpivotInItem [45-46] [x] + PathExpressionList [45-46] [x] + PathExpression [45-46] [x] + Identifier(x) [45-46] [x] + UnpivotInItem [48-49] [y] + PathExpressionList [48-49] [y] + PathExpression [48-49] [y] + Identifier(y) [48-49] [y] + Alias [52-58] [AS unp] + Identifier(unp) [55-58] [unp] +-- +FROM + t +|> UNPIVOT INCLUDE NULLS (a FOR c IN ((x), (y))) AS unp +== + +# Simple UNPIVOT with typo in EXCLUDE NULLS +FROM t +|> UNPIVOT EXCLUDE NULL (a FOR c IN (x, y)) +-- +ERROR: Syntax error: Expected keyword NULLS but got keyword NULL [at 2:20] +|> UNPIVOT EXCLUDE NULL (a FOR c IN (x, y)) + ^ +== + +# Simple UNPIVOT with single-element IN-list with parentheses. +FROM t +|> UNPIVOT((a) FOR b IN (x)) +-- +QueryStatement [0-35] [FROM t |>...b IN (x))] + Query [0-35] [FROM t |>...b IN (x))] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-35] [|> UNPIVOT((a) FOR b IN (x))] + UnpivotClause [10-35] [UNPIVOT((a) FOR b IN (x))] + PathExpressionList [19-20] [a] + PathExpression [19-20] [a] + Identifier(a) [19-20] [a] + PathExpression [26-27] [b] + Identifier(b) [26-27] [b] + UnpivotInItemList [31-34] [(x)] + UnpivotInItem [32-33] [x] + PathExpressionList [32-33] [x] + PathExpression [32-33] [x] + Identifier(x) [32-33] [x] +-- +FROM + t +|> UNPIVOT(a FOR b IN ((x))) +== + +# Multiple path expressions on the input, string labels on the output. +FROM t +|> UNPIVOT((a, b.b) FOR a.b.c IN ((f), w AS '1', (x) '2', y "3")) +-- +QueryStatement [0-72] [FROM t |>...', y "3"))] + Query [0-72] [FROM t |>...', y "3"))] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-72] [|> UNPIVOT...', y "3"))] + UnpivotClause [10-72] [UNPIVOT((a...', y "3"))] + PathExpressionList [19-25] [a, b.b] + PathExpression [19-20] [a] + Identifier(a) [19-20] [a] + PathExpression [22-25] [b.b] + Identifier(b) [22-23] [b] + Identifier(b) [24-25] [b] + PathExpression [31-36] [a.b.c] + Identifier(a) [31-32] [a] + Identifier(b) [33-34] [b] + Identifier(c) [35-36] [c] + UnpivotInItemList [40-71] [((f), w AS...2', y "3")] + UnpivotInItem [41-44] [(f)] + PathExpressionList [42-43] [f] + PathExpression [42-43] [f] + Identifier(f) [42-43] [f] + UnpivotInItem [46-54] [w AS '1'] + PathExpressionList [46-47] [w] + PathExpression [46-47] [w] + Identifier(w) [46-47] [w] + UnpivotInItemLabel [48-54] [AS '1'] + StringLiteral [51-54] ['1'] + StringLiteralComponent('1') [51-54] ['1'] + UnpivotInItem [56-63] [(x) '2'] + PathExpressionList [57-58] [x] + PathExpression [57-58] [x] + Identifier(x) [57-58] [x] + UnpivotInItemLabel [60-63] ['2'] + StringLiteral [60-63] ['2'] + StringLiteralComponent('2') [60-63] ['2'] + UnpivotInItem [65-70] [y "3"] + PathExpressionList [65-66] [y] + PathExpression [65-66] [y] + Identifier(y) [65-66] [y] + UnpivotInItemLabel [67-70] ["3"] + StringLiteral [67-70] ["3"] + StringLiteralComponent("3") [67-70] ["3"] +-- +FROM + t +|> UNPIVOT((a, b.b) FOR a.b.c IN ((f), (w) AS '1', (x) AS '2', (y) AS "3")) +== + +# Column names with integer labels. +FROM t +|> UNPIVOT(a FOR e IN (w AS 1, x AS 2)) +-- +QueryStatement [0-46] [FROM t |>..., x AS 2))] + Query [0-46] [FROM t |>..., x AS 2))] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-46] [|> UNPIVOT..., x AS 2))] + UnpivotClause [10-46] [UNPIVOT(a..., x AS 2))] + PathExpressionList [18-19] [a] + PathExpression [18-19] [a] + Identifier(a) [18-19] [a] + PathExpression [24-25] [e] + Identifier(e) [24-25] [e] + UnpivotInItemList [29-45] [(w AS 1, x AS 2)] + UnpivotInItem [30-36] [w AS 1] + PathExpressionList [30-31] [w] + PathExpression [30-31] [w] + Identifier(w) [30-31] [w] + UnpivotInItemLabel [32-36] [AS 1] + IntLiteral(1) [35-36] [1] + UnpivotInItem [38-44] [x AS 2] + PathExpressionList [38-39] [x] + PathExpression [38-39] [x] + Identifier(x) [38-39] [x] + UnpivotInItemLabel [40-44] [AS 2] + IntLiteral(2) [43-44] [2] +-- +FROM + t +|> UNPIVOT(a FOR e IN ((w) AS 1, (x) AS 2)) +== + +# Columns with labels that are of type double (neither int or string) +FROM t +|> UNPIVOT(a FOR e IN (w AS 1.1)) +-- +ERROR: Syntax error: Expected integer literal or string literal but got floating point literal "1.1" [at 2:29] +|> UNPIVOT(a FOR e IN (w AS 1.1)) + ^ +== + +# Unpivot labels with NULL values +FROM t +|> UNPIVOT(a FOR e IN (w AS null)) +-- +ERROR: Syntax error: Expected integer literal or string literal but got keyword NULL [at 2:29] +|> UNPIVOT(a FOR e IN (w AS null)) + ^ +== + +# Unpivot with negative integer labels. +FROM KeyValue +|> UNPIVOT(a for b in (Key as -1)) +-- +ERROR: Syntax error: Expected integer literal or string literal but got "-" [at 2:31] +|> UNPIVOT(a for b in (Key as -1)) + ^ +== + +FROM t +|> UNPIVOT +-- +ERROR: Syntax error: Expected "(" but got end of statement [at 2:11] +|> UNPIVOT + ^ +== + +FROM t +|> UNPIVOT() +-- +ERROR: Syntax error: Unexpected ")" [at 2:12] +|> UNPIVOT() + ^ +== + +FROM t +|> UNPIVOT(x) +-- +ERROR: Syntax error: Expected keyword FOR but got ")" [at 2:13] +|> UNPIVOT(x) + ^ +== + +FROM t +|> UNPIVOT(x FOR y) +-- +ERROR: Syntax error: Expected "." or keyword IN but got ")" [at 2:19] +|> UNPIVOT(x FOR y) + ^ +== + +FROM t +|> UNPIVOT(x FOR y IN ()) +-- +ERROR: Syntax error: Unexpected ")" [at 2:24] +|> UNPIVOT(x FOR y IN ()) + ^ +== + +# ERROR: Trailing comma in IN-list +FROM t +|> UNPIVOT(x FOR y IN (a,)) +-- +ERROR: Syntax error: Unexpected ")" [at 2:26] +|> UNPIVOT(x FOR y IN (a,)) + ^ +== + +# ERROR: Missing right-parenthesis +FROM t +|> UNPIVOT(x FOR y IN (a) +-- +ERROR: Syntax error: Expected ")" but got end of statement [at 2:26] +|> UNPIVOT(x FOR y IN (a) + ^ +== + +# ERROR: Missing unpivot output columns +FROM t +|> UNPIVOT(FOR y IN (a)) +-- +ERROR: Syntax error: Unexpected keyword FOR [at 2:12] +|> UNPIVOT(FOR y IN (a)) + ^ +== + +# ERROR: Trailing comma after last unpivot output column +FROM t +|> UNPIVOT(x, y, FOR a IN (b)) +-- +ERROR: Syntax error: Expected keyword FOR but got "," [at 2:13] +|> UNPIVOT(x, y, FOR a IN (b)) + ^ +== + +# UNPIVOT as alias of table produced by a UNPIVOT clause. +FROM t +|> UNPIVOT(a FOR b IN (c)) UNPIVOT +-- +QueryStatement [0-41] [FROM t |>...)) UNPIVOT] + Query [0-41] [FROM t |>...)) UNPIVOT] + FromQuery [0-6] [FROM t] + FromClause [0-6] [FROM t] + TablePathExpression [5-6] [t] + PathExpression [5-6] [t] + Identifier(t) [5-6] [t] + PipeUnpivot [7-41] [|> UNPIVOT...)) UNPIVOT] + UnpivotClause [10-41] [UNPIVOT(a...)) UNPIVOT] + PathExpressionList [18-19] [a] + PathExpression [18-19] [a] + Identifier(a) [18-19] [a] + PathExpression [24-25] [b] + Identifier(b) [24-25] [b] + UnpivotInItemList [29-32] [(c)] + UnpivotInItem [30-31] [c] + PathExpressionList [30-31] [c] + PathExpression [30-31] [c] + Identifier(c) [30-31] [c] + Alias [34-41] [UNPIVOT] + Identifier(UNPIVOT) [34-41] [UNPIVOT] +-- +FROM + t +|> UNPIVOT(a FOR b IN ((c))) AS UNPIVOT diff --git a/zetasql/parser/testdata/pipe_window.test b/zetasql/parser/testdata/pipe_window.test new file mode 100644 index 000000000..1e7d71b7b --- /dev/null +++ b/zetasql/parser/testdata/pipe_window.test @@ -0,0 +1,263 @@ +select 1 +|> WINDOW f() OVER (), g() OVER (PARTITION BY x ORDER BY y) x +|> WINDOW 3, h() OVER (ROWS UNBOUNDED PRECEDING) AS z +-- +QueryStatement [0-124] [select 1 |...PRECEDING) AS z] + Query [0-124] [select 1 |...PRECEDING) AS z] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeWindow [9-70] [|> WINDOW...ORDER BY y) x] + Select [12-70] [WINDOW f()...ORDER BY y) x] + SelectList [19-70] [f() OVER (...ORDER BY y) x] + SelectColumn [19-30] [f() OVER ()] + AnalyticFunctionCall [19-30] [f() OVER ()] + FunctionCall [19-22] [f()] + PathExpression [19-20] [f] + Identifier(f) [19-20] [f] + WindowSpecification [28-30] [()] + SelectColumn [32-70] [g() OVER (...ORDER BY y) x] + AnalyticFunctionCall [32-68] [g() OVER (...ORDER BY y)] + FunctionCall [32-35] [g()] + PathExpression [32-33] [g] + Identifier(g) [32-33] [g] + WindowSpecification [41-68] [(PARTITION BY x ORDER BY y)] + PartitionBy [42-56] [PARTITION BY x] + PathExpression [55-56] [x] + Identifier(x) [55-56] [x] + OrderBy [57-67] [ORDER BY y] + OrderingExpression(ASC) [66-67] [y] + PathExpression [66-67] [y] + Identifier(y) [66-67] [y] + Alias [69-70] [x] + Identifier(x) [69-70] [x] + PipeWindow [71-124] [|> WINDOW...PRECEDING) AS z] + Select [74-124] [WINDOW 3,...PRECEDING) AS z] + SelectList [81-124] [3, h() OVER...PRECEDING) AS z] + SelectColumn [81-82] [3] + IntLiteral(3) [81-82] [3] + SelectColumn [84-124] [h() OVER (...PRECEDING) AS z] + AnalyticFunctionCall [84-119] [h() OVER (...PRECEDING)] + FunctionCall [84-87] [h()] + PathExpression [84-85] [h] + Identifier(h) [84-85] [h] + WindowSpecification [93-119] [(ROWS UNBOUNDED PRECEDING)] + WindowFrame(ROWS) [94-118] [ROWS UNBOUNDED PRECEDING] + WindowFrameExpr(UNBOUNDED PRECEDING) [99-118] [UNBOUNDED PRECEDING] + Alias [120-124] [AS z] + Identifier(z) [123-124] [z] +-- +SELECT + 1 +|> WINDOW + f() OVER (), + g() OVER (PARTITION BY x + ORDER BY y) AS x +|> WINDOW + 3, + h() OVER (ROWS UNBOUNDED PRECEDING) AS z +== + +select 1 +|> WINDOW +-- +ERROR: Syntax error: Unexpected end of statement [at 2:10] +|> WINDOW + ^ +== + +# The parser doesn't check that OVER is present. It accepts any expression. +# Trailing commas are allowed. +select 0 +|> WINDOW 1, x, f()+g(), +-- +QueryStatement [0-33] [select 0 |..., f()+g(),] + Query [0-33] [select 0 |..., f()+g(),] + Select [0-8] [select 0] + SelectList [7-8] [0] + SelectColumn [7-8] [0] + IntLiteral(0) [7-8] [0] + PipeWindow [9-33] [|> WINDOW 1, x, f()+g(),] + Select [12-33] [WINDOW 1, x, f()+g(),] + SelectList [19-33] [1, x, f()+g(),] + SelectColumn [19-20] [1] + IntLiteral(1) [19-20] [1] + SelectColumn [22-23] [x] + PathExpression [22-23] [x] + Identifier(x) [22-23] [x] + SelectColumn [25-32] [f()+g()] + BinaryExpression(+) [25-32] [f()+g()] + FunctionCall [25-28] [f()] + PathExpression [25-26] [f] + Identifier(f) [25-26] [f] + FunctionCall [29-32] [g()] + PathExpression [29-30] [g] + Identifier(g) [29-30] [g] +-- +SELECT + 0 +|> WINDOW + 1, + x, + f() + g() +== + +# OVER precedence works as usual. +select 1 +|> WINDOW x+f(y+z) OVER ()+k +-- +QueryStatement [0-37] [select 1 |...OVER ()+k] + Query [0-37] [select 1 |...OVER ()+k] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeWindow [9-37] [|> WINDOW x+f(y+z) OVER ()+k] + Select [12-37] [WINDOW x+f(y+z) OVER ()+k] + SelectList [19-37] [x+f(y+z) OVER ()+k] + SelectColumn [19-37] [x+f(y+z) OVER ()+k] + BinaryExpression(+) [19-37] [x+f(y+z) OVER ()+k] + BinaryExpression(+) [19-35] [x+f(y+z) OVER ()] + PathExpression [19-20] [x] + Identifier(x) [19-20] [x] + AnalyticFunctionCall [21-35] [f(y+z) OVER ()] + FunctionCall [21-27] [f(y+z)] + PathExpression [21-22] [f] + Identifier(f) [21-22] [f] + BinaryExpression(+) [23-26] [y+z] + PathExpression [23-24] [y] + Identifier(y) [23-24] [y] + PathExpression [25-26] [z] + Identifier(z) [25-26] [z] + WindowSpecification [33-35] [()] + PathExpression [36-37] [k] + Identifier(k) [36-37] [k] +-- +SELECT + 1 +|> WINDOW + x + f(y + z) OVER () + k +== + +select 1 +|> WINDOW * +-- +ERROR: Syntax error: Unexpected "*" [at 2:11] +|> WINDOW * + ^ +== + +# Dot-star is allowed, with the optional modifiers, including +# on the output of window functions. +select 1 +|> WINDOW x.*, + f(x).* except(a), + (1+x).* replace(a as b), + (sum(x) OVER ()).* +-- +QueryStatement [0-115] [select 1 |...OVER ()).*] + Query [0-115] [select 1 |...OVER ()).*] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeWindow [9-115] [|> WINDOW...OVER ()).*] + Select [12-115] [WINDOW x.*...OVER ()).*] + SelectList [19-115] [x.*,...OVER ()).*] + SelectColumn [19-22] [x.*] + DotStar [19-22] [x.*] + PathExpression [19-20] [x] + Identifier(x) [19-20] [x] + SelectColumn [34-50] [f(x).* except(a)] + DotStarWithModifiers [34-50] [f(x).* except(a)] + FunctionCall [34-38] [f(x)] + PathExpression [34-35] [f] + Identifier(f) [34-35] [f] + PathExpression [36-37] [x] + Identifier(x) [36-37] [x] + StarModifiers [41-50] [except(a)] + StarExceptList [41-50] [except(a)] + Identifier(a) [48-49] [a] + SelectColumn [62-85] [(1+x).* replace(a as b)] + DotStarWithModifiers [62-85] [(1+x).* replace(a as b)] + BinaryExpression(+) [63-66] [1+x] + IntLiteral(1) [63-64] [1] + PathExpression [65-66] [x] + Identifier(x) [65-66] [x] + StarModifiers [70-85] [replace(a as b)] + StarReplaceItem [78-84] [a as b] + PathExpression [78-79] [a] + Identifier(a) [78-79] [a] + Identifier(b) [83-84] [b] + SelectColumn [97-115] [(sum(x) OVER ()).*] + DotStar [97-115] [(sum(x) OVER ()).*] + AnalyticFunctionCall [98-112] [sum(x) OVER ()] + FunctionCall [98-104] [sum(x)] + PathExpression [98-101] [sum] + Identifier(sum) [98-101] [sum] + PathExpression [102-103] [x] + Identifier(x) [102-103] [x] + WindowSpecification [110-112] [()] +-- +SELECT + 1 +|> WINDOW + x.*, + f(x).* EXCEPT (a), + (1 + x).* REPLACE (a AS b), + (sum(x) OVER ()).* +== + +# The precedence allows binding .* here. +select 1 +|> WINDOW sum(x) OVER ().* +-- +QueryStatement [0-35] [select 1 |...OVER ().*] + Query [0-35] [select 1 |...OVER ().*] + Select [0-8] [select 1] + SelectList [7-8] [1] + SelectColumn [7-8] [1] + IntLiteral(1) [7-8] [1] + PipeWindow [9-35] [|> WINDOW sum(x) OVER ().*] + Select [12-35] [WINDOW sum(x) OVER ().*] + SelectList [19-35] [sum(x) OVER ().*] + SelectColumn [19-35] [sum(x) OVER ().*] + DotStar [19-35] [sum(x) OVER ().*] + AnalyticFunctionCall [19-33] [sum(x) OVER ()] + FunctionCall [19-25] [sum(x)] + PathExpression [19-22] [sum] + Identifier(sum) [19-22] [sum] + PathExpression [23-24] [x] + Identifier(x) [23-24] [x] + WindowSpecification [31-33] [()] +-- +SELECT + 1 +|> WINDOW + sum(x) OVER ().* +== + +# Precedence makes this illegal. +select 1 +|> WINDOW 1+sum(x) OVER ().* +-- +ERROR: Syntax error: Unexpected "*" [at 2:28] +|> WINDOW 1+sum(x) OVER ().* + ^ +== + +select 1 +|> WINDOW sum(x).* OVER () +-- +ERROR: Syntax error: OVER keyword must follow a function call [at 2:20] +|> WINDOW sum(x).* OVER () + ^ +== + +select 1 +|> WINDOW sum(x.*) OVER () +-- +ERROR: Syntax error: Unexpected "*" [at 2:17] +|> WINDOW sum(x.*) OVER () + ^ diff --git a/zetasql/parser/testdata/pivot.test b/zetasql/parser/testdata/pivot.test index bc210d1a6..eb232a00e 100644 --- a/zetasql/parser/testdata/pivot.test +++ b/zetasql/parser/testdata/pivot.test @@ -1896,8 +1896,8 @@ QueryStatement [0-104] [SELECT * FROM...IN (1, 2))] TableSubquery [14-104] [( SELECT...IN (1, 2))] Query (pivot input) [18-71] [SELECT a,...d FROM t2)] SetOperation(UNION ALL) [18-71] [SELECT a,...d FROM t2)] - SetOperationMetadataList [37-49] [UNION ALL] - SetOperationMetadata [37-49] [UNION ALL] + SetOperationMetadataList [40-49] [UNION ALL] + SetOperationMetadata [40-49] [UNION ALL] SetOperationType [40-45] [UNION] SetOperationAllOrDistinct [46-49] [ALL] Select [18-37] [SELECT a, b FROM t1] diff --git a/zetasql/parser/testdata/set_operation.test b/zetasql/parser/testdata/set_operation.test index d5a0fd6a2..92336d989 100644 --- a/zetasql/parser/testdata/set_operation.test +++ b/zetasql/parser/testdata/set_operation.test @@ -7,11 +7,11 @@ select null from T2 QueryStatement [0-71] [select abc...null from T2] Query [0-71] [select abc...null from T2] SetOperation(UNION ALL) [0-71] [select abc...null from T2] - SetOperationMetadataList [17-51] [UNION ALL...UNION DISTINCT] - SetOperationMetadata [17-27] [UNION ALL] + SetOperationMetadataList [18-51] [UNION ALL...UNION DISTINCT] + SetOperationMetadata [18-27] [UNION ALL] SetOperationType [18-23] [UNION] SetOperationAllOrDistinct [24-27] [ALL] - SetOperationMetadata [36-51] [UNION DISTINCT] + SetOperationMetadata [37-51] [UNION DISTINCT] SetOperationType [37-42] [UNION] SetOperationAllOrDistinct [43-51] [DISTINCT] Select [0-17] [select abc from T] @@ -64,8 +64,8 @@ QueryStatement [0-43] [select 1 from...select 3)] TableSubquery [14-43] [(select 2 UNION ALL select 3)] Query [15-42] [select 2 UNION ALL select 3] SetOperation(UNION ALL) [15-42] [select 2 UNION ALL select 3] - SetOperationMetadataList [23-33] [UNION ALL] - SetOperationMetadata [23-33] [UNION ALL] + SetOperationMetadataList [24-33] [UNION ALL] + SetOperationMetadata [24-33] [UNION ALL] SetOperationType [24-29] [UNION] SetOperationAllOrDistinct [30-33] [ALL] Select [15-23] [select 2] @@ -96,8 +96,8 @@ select 2 QueryStatement [0-32] [select 1 UNION...INCT select 2] Query [0-32] [select 1 UNION...INCT select 2] SetOperation(UNION DISTINCT) [0-32] [select 1 UNION...INCT select 2] - SetOperationMetadataList [8-23] [UNION DISTINCT] - SetOperationMetadata [8-23] [UNION DISTINCT] + SetOperationMetadataList [9-23] [UNION DISTINCT] + SetOperationMetadata [9-23] [UNION DISTINCT] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-23] [DISTINCT] Select [0-8] [select 1] @@ -133,8 +133,8 @@ select 2 QueryStatement [0-28] [select 1 EXCEPT ALL select 2] Query [0-28] [select 1 EXCEPT ALL select 2] SetOperation(EXCEPT ALL) [0-28] [select 1 EXCEPT ALL select 2] - SetOperationMetadataList [8-19] [EXCEPT ALL] - SetOperationMetadata [8-19] [EXCEPT ALL] + SetOperationMetadataList [9-19] [EXCEPT ALL] + SetOperationMetadata [9-19] [EXCEPT ALL] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-19] [ALL] Select [0-8] [select 1] @@ -160,8 +160,8 @@ select 2 QueryStatement [0-33] [select 1 EXCEPT...NCT select 2] Query [0-33] [select 1 EXCEPT...NCT select 2] SetOperation(EXCEPT DISTINCT) [0-33] [select 1 EXCEPT...NCT select 2] - SetOperationMetadataList [8-24] [EXCEPT DISTINCT] - SetOperationMetadata [8-24] [EXCEPT DISTINCT] + SetOperationMetadataList [9-24] [EXCEPT DISTINCT] + SetOperationMetadata [9-24] [EXCEPT DISTINCT] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-24] [DISTINCT] Select [0-8] [select 1] @@ -207,8 +207,8 @@ select 2 QueryStatement [0-31] [select 1 INTERSEC...L select 2] Query [0-31] [select 1 INTERSEC...L select 2] SetOperation(INTERSECT ALL) [0-31] [select 1 INTERSEC...L select 2] - SetOperationMetadataList [8-22] [INTERSECT ALL] - SetOperationMetadata [8-22] [INTERSECT ALL] + SetOperationMetadataList [9-22] [INTERSECT ALL] + SetOperationMetadata [9-22] [INTERSECT ALL] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-22] [ALL] Select [0-8] [select 1] @@ -234,8 +234,8 @@ select 2 QueryStatement [0-36] [select 1 INTERSEC...T select 2] Query [0-36] [select 1 INTERSEC...T select 2] SetOperation(INTERSECT DISTINCT) [0-36] [select 1 INTERSEC...T select 2] - SetOperationMetadataList [8-27] [INTERSECT DISTINCT] - SetOperationMetadata [8-27] [INTERSECT DISTINCT] + SetOperationMetadataList [9-27] [INTERSECT DISTINCT] + SetOperationMetadata [9-27] [INTERSECT DISTINCT] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-27] [DISTINCT] Select [0-8] [select 1] @@ -273,11 +273,11 @@ select 1 QueryStatement [0-56] [select 1 UNION...INCT select 1] Query [0-56] [select 1 UNION...INCT select 1] SetOperation(UNION DISTINCT) [0-56] [select 1 UNION...INCT select 1] - SetOperationMetadataList [8-47] [UNION DISTINCT...NION DISTINCT] - SetOperationMetadata [8-23] [UNION DISTINCT] + SetOperationMetadataList [9-47] [UNION DISTINCT...NION DISTINCT] + SetOperationMetadata [9-23] [UNION DISTINCT] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-23] [DISTINCT] - SetOperationMetadata [32-47] [UNION DISTINCT] + SetOperationMetadata [33-47] [UNION DISTINCT] SetOperationType [33-38] [UNION] SetOperationAllOrDistinct [39-47] [DISTINCT] Select [0-8] [select 1] @@ -312,11 +312,11 @@ select 3 QueryStatement [0-48] [select 1 EXCEPT...ALL select 3] Query [0-48] [select 1 EXCEPT...ALL select 3] SetOperation(EXCEPT ALL) [0-48] [select 1 EXCEPT...ALL select 3] - SetOperationMetadataList [8-39] [EXCEPT ALL select 2 EXCEPT ALL] - SetOperationMetadata [8-19] [EXCEPT ALL] + SetOperationMetadataList [9-39] [EXCEPT ALL select 2 EXCEPT ALL] + SetOperationMetadata [9-19] [EXCEPT ALL] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-19] [ALL] - SetOperationMetadata [28-39] [EXCEPT ALL] + SetOperationMetadata [29-39] [EXCEPT ALL] SetOperationType [29-35] [EXCEPT] SetOperationAllOrDistinct [36-39] [ALL] Select [0-8] [select 1] @@ -351,11 +351,11 @@ select 1 QueryStatement [0-58] [select 1 EXCEPT...NCT select 1] Query [0-58] [select 1 EXCEPT...NCT select 1] SetOperation(EXCEPT DISTINCT) [0-58] [select 1 EXCEPT...NCT select 1] - SetOperationMetadataList [8-49] [EXCEPT DISTINCT...EPT DISTINCT] - SetOperationMetadata [8-24] [EXCEPT DISTINCT] + SetOperationMetadataList [9-49] [EXCEPT DISTINCT...EPT DISTINCT] + SetOperationMetadata [9-24] [EXCEPT DISTINCT] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-24] [DISTINCT] - SetOperationMetadata [33-49] [EXCEPT DISTINCT] + SetOperationMetadata [34-49] [EXCEPT DISTINCT] SetOperationType [34-40] [EXCEPT] SetOperationAllOrDistinct [41-49] [DISTINCT] Select [0-8] [select 1] @@ -390,11 +390,11 @@ select 3 QueryStatement [0-54] [select 1 INTERSEC...L select 3] Query [0-54] [select 1 INTERSEC...L select 3] SetOperation(INTERSECT ALL) [0-54] [select 1 INTERSEC...L select 3] - SetOperationMetadataList [8-45] [INTERSECT...INTERSECT ALL] - SetOperationMetadata [8-22] [INTERSECT ALL] + SetOperationMetadataList [9-45] [INTERSECT...INTERSECT ALL] + SetOperationMetadata [9-22] [INTERSECT ALL] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-22] [ALL] - SetOperationMetadata [31-45] [INTERSECT ALL] + SetOperationMetadata [32-45] [INTERSECT ALL] SetOperationType [32-41] [INTERSECT] SetOperationAllOrDistinct [42-45] [ALL] Select [0-8] [select 1] @@ -429,11 +429,11 @@ select 3 QueryStatement [0-64] [select 1 INTERSEC...T select 3] Query [0-64] [select 1 INTERSEC...T select 3] SetOperation(INTERSECT DISTINCT) [0-64] [select 1 INTERSEC...T select 3] - SetOperationMetadataList [8-55] [INTERSECT...INTERSECT DISTINCT] - SetOperationMetadata [8-27] [INTERSECT DISTINCT] + SetOperationMetadataList [9-55] [INTERSECT...INTERSECT DISTINCT] + SetOperationMetadata [9-27] [INTERSECT DISTINCT] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-27] [DISTINCT] - SetOperationMetadata [36-55] [INTERSECT DISTINCT] + SetOperationMetadata [37-55] [INTERSECT DISTINCT] SetOperationType [37-46] [INTERSECT] SetOperationAllOrDistinct [47-55] [DISTINCT] Select [0-8] [select 1] @@ -479,8 +479,8 @@ UNION ALL QueryStatement [0-49] [select 1 UNION...select 3)] Query [0-49] [select 1 UNION...select 3)] SetOperation(UNION ALL) [0-49] [select 1 UNION...select 3)] - SetOperationMetadataList [8-18] [UNION ALL] - SetOperationMetadata [8-18] [UNION ALL] + SetOperationMetadataList [9-18] [UNION ALL] + SetOperationMetadata [9-18] [UNION ALL] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-18] [ALL] Select [0-8] [select 1] @@ -489,8 +489,8 @@ QueryStatement [0-49] [select 1 UNION...select 3)] IntLiteral(1) [7-8] [1] Query [20-48] [select 2 EXCEPT ALL select 3] SetOperation(EXCEPT ALL) [20-48] [select 2 EXCEPT ALL select 3] - SetOperationMetadataList [28-39] [EXCEPT ALL] - SetOperationMetadata [28-39] [EXCEPT ALL] + SetOperationMetadataList [29-39] [EXCEPT ALL] + SetOperationMetadata [29-39] [EXCEPT ALL] SetOperationType [29-35] [EXCEPT] SetOperationAllOrDistinct [36-39] [ALL] Select [20-28] [select 2] @@ -520,8 +520,8 @@ INTERSECT ALL QueryStatement [0-61] [select 1 INTERSEC...select 3)] Query [0-61] [select 1 INTERSEC...select 3)] SetOperation(INTERSECT ALL) [0-61] [select 1 INTERSEC...select 3)] - SetOperationMetadataList [8-22] [INTERSECT ALL] - SetOperationMetadata [8-22] [INTERSECT ALL] + SetOperationMetadataList [9-22] [INTERSECT ALL] + SetOperationMetadata [9-22] [INTERSECT ALL] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-22] [ALL] Select [0-8] [select 1] @@ -530,8 +530,8 @@ QueryStatement [0-61] [select 1 INTERSEC...select 3)] IntLiteral(1) [7-8] [1] Query [24-60] [select 2 INTERSEC...T select 3] SetOperation(INTERSECT DISTINCT) [24-60] [select 2 INTERSEC...T select 3] - SetOperationMetadataList [32-51] [INTERSECT DISTINCT] - SetOperationMetadata [32-51] [INTERSECT DISTINCT] + SetOperationMetadataList [33-51] [INTERSECT DISTINCT] + SetOperationMetadata [33-51] [INTERSECT DISTINCT] SetOperationType [33-42] [INTERSECT] SetOperationAllOrDistinct [43-51] [DISTINCT] Select [24-32] [select 2] @@ -564,8 +564,8 @@ UNION ALL QueryStatement [0-76] [select 1 UNION...select 4))] Query [0-76] [select 1 UNION...select 4))] SetOperation(UNION ALL) [0-76] [select 1 UNION...select 4))] - SetOperationMetadataList [8-18] [UNION ALL] - SetOperationMetadata [8-18] [UNION ALL] + SetOperationMetadataList [9-18] [UNION ALL] + SetOperationMetadata [9-18] [UNION ALL] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-18] [ALL] Select [0-8] [select 1] @@ -574,8 +574,8 @@ QueryStatement [0-76] [select 1 UNION...select 4))] IntLiteral(1) [7-8] [1] Query [20-75] [select 2...select 4)] SetOperation(INTERSECT ALL) [20-75] [select 2...select 4)] - SetOperationMetadataList [28-43] [INTERSECT ALL] - SetOperationMetadata [28-43] [INTERSECT ALL] + SetOperationMetadataList [30-43] [INTERSECT ALL] + SetOperationMetadata [30-43] [INTERSECT ALL] SetOperationType [30-39] [INTERSECT] SetOperationAllOrDistinct [40-43] [ALL] Select [20-28] [select 2] @@ -584,8 +584,8 @@ QueryStatement [0-76] [select 1 UNION...select 4))] IntLiteral(2) [27-28] [2] Query [46-74] [select 3 EXCEPT ALL select 4] SetOperation(EXCEPT ALL) [46-74] [select 3 EXCEPT ALL select 4] - SetOperationMetadataList [54-65] [EXCEPT ALL] - SetOperationMetadata [54-65] [EXCEPT ALL] + SetOperationMetadataList [55-65] [EXCEPT ALL] + SetOperationMetadata [55-65] [EXCEPT ALL] SetOperationType [55-61] [EXCEPT] SetOperationAllOrDistinct [62-65] [ALL] Select [46-54] [select 3] @@ -620,8 +620,8 @@ select 2 QueryStatement [0-41] [select 1 UNION...ALL select 2] Query [0-41] [select 1 UNION...ALL select 2] SetOperation(UNION ALL) [0-41] [select 1 UNION...ALL select 2] - SetOperationMetadataList [8-32] [UNION @{ key = 5 } ALL] - SetOperationMetadata [8-32] [UNION @{ key = 5 } ALL] + SetOperationMetadataList [9-32] [UNION @{ key = 5 } ALL] + SetOperationMetadata [9-32] [UNION @{ key = 5 } ALL] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [29-32] [ALL] Hint [16-28] [@{ key = 5 }] @@ -653,8 +653,8 @@ select 2 QueryStatement [0-45] [select 1 INTERSEC...L select 2] Query [0-45] [select 1 INTERSEC...L select 2] SetOperation(INTERSECT ALL) [0-45] [select 1 INTERSEC...L select 2] - SetOperationMetadataList [8-36] [INTERSECT @{ key = 5 } ALL] - SetOperationMetadata [8-36] [INTERSECT @{ key = 5 } ALL] + SetOperationMetadataList [9-36] [INTERSECT @{ key = 5 } ALL] + SetOperationMetadata [9-36] [INTERSECT @{ key = 5 } ALL] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [33-36] [ALL] Hint [20-32] [@{ key = 5 }] @@ -686,8 +686,8 @@ select 2 QueryStatement [0-42] [select 1 EXCEPT...ALL select 2] Query [0-42] [select 1 EXCEPT...ALL select 2] SetOperation(EXCEPT ALL) [0-42] [select 1 EXCEPT...ALL select 2] - SetOperationMetadataList [8-33] [EXCEPT @{ key = 5 } ALL] - SetOperationMetadata [8-33] [EXCEPT @{ key = 5 } ALL] + SetOperationMetadataList [9-33] [EXCEPT @{ key = 5 } ALL] + SetOperationMetadata [9-33] [EXCEPT @{ key = 5 } ALL] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [30-33] [ALL] Hint [17-29] [@{ key = 5 }] @@ -719,8 +719,8 @@ select 2 QueryStatement [0-46] [select 1 UNION...INCT select 2] Query [0-46] [select 1 UNION...INCT select 2] SetOperation(UNION DISTINCT) [0-46] [select 1 UNION...INCT select 2] - SetOperationMetadataList [8-37] [UNION @{ key = 5 } DISTINCT] - SetOperationMetadata [8-37] [UNION @{ key = 5 } DISTINCT] + SetOperationMetadataList [9-37] [UNION @{ key = 5 } DISTINCT] + SetOperationMetadata [9-37] [UNION @{ key = 5 } DISTINCT] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [29-37] [DISTINCT] Hint [16-28] [@{ key = 5 }] @@ -751,8 +751,8 @@ select 2 QueryStatement [0-49] [select 1 INTERSEC...T select 2] Query [0-49] [select 1 INTERSEC...T select 2] SetOperation(INTERSECT DISTINCT) [0-49] [select 1 INTERSEC...T select 2] - SetOperationMetadataList [8-40] [INTERSECT...} DISTINCT] - SetOperationMetadata [8-40] [INTERSECT...} DISTINCT] + SetOperationMetadataList [9-40] [INTERSECT...} DISTINCT] + SetOperationMetadata [9-40] [INTERSECT...} DISTINCT] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [32-40] [DISTINCT] Hint [19-31] [@{ key = 5 }] @@ -784,8 +784,8 @@ select 2 QueryStatement [0-47] [select 1 EXCEPT...NCT select 2] Query [0-47] [select 1 EXCEPT...NCT select 2] SetOperation(EXCEPT DISTINCT) [0-47] [select 1 EXCEPT...NCT select 2] - SetOperationMetadataList [8-38] [EXCEPT @{ key = 5 } DISTINCT] - SetOperationMetadata [8-38] [EXCEPT @{ key = 5 } DISTINCT] + SetOperationMetadataList [9-38] [EXCEPT @{ key = 5 } DISTINCT] + SetOperationMetadata [9-38] [EXCEPT @{ key = 5 } DISTINCT] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [30-38] [DISTINCT] Hint [17-29] [@{ key = 5 }] @@ -818,15 +818,15 @@ select 3 QueryStatement [0-59] [select 1 UNION...ALL select 3] Query [0-59] [select 1 UNION...ALL select 3] SetOperation(UNION ALL) [0-59] [select 1 UNION...ALL select 3] - SetOperationMetadataList [8-50] [UNION @{ key...UNION ALL] - SetOperationMetadata [8-31] [UNION @{ key = 5 } ALL] + SetOperationMetadataList [9-50] [UNION @{ key...UNION ALL] + SetOperationMetadata [9-31] [UNION @{ key = 5 } ALL] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [28-31] [ALL] Hint [15-27] [@{ key = 5 }] HintEntry [18-25] [key = 5] Identifier(key) [18-21] [key] IntLiteral(5) [24-25] [5] - SetOperationMetadata [40-50] [UNION ALL] + SetOperationMetadata [41-50] [UNION ALL] SetOperationType [41-46] [UNION] SetOperationAllOrDistinct [47-50] [ALL] Select [0-8] [select 1] @@ -862,8 +862,8 @@ select 3 QueryStatement [0-78] [(select 1...ALL select 3] Query [0-78] [(select 1...ALL select 3] SetOperation(UNION ALL) [0-78] [(select 1...ALL select 3] - SetOperationMetadataList [46-69] [UNION @{ key = 2 } ALL] - SetOperationMetadata [46-69] [UNION @{ key = 2 } ALL] + SetOperationMetadataList [47-69] [UNION @{ key = 2 } ALL] + SetOperationMetadata [47-69] [UNION @{ key = 2 } ALL] SetOperationType [47-52] [UNION] SetOperationAllOrDistinct [66-69] [ALL] Hint [53-65] [@{ key = 2 }] @@ -872,8 +872,8 @@ QueryStatement [0-78] [(select 1...ALL select 3] IntLiteral(2) [62-63] [2] Query [1-45] [select 1...select 2] SetOperation(UNION ALL) [1-45] [select 1...select 2] - SetOperationMetadataList [9-34] [UNION @{ key = 1 } ALL] - SetOperationMetadata [9-34] [UNION @{ key = 1 } ALL] + SetOperationMetadataList [12-34] [UNION @{ key = 1 } ALL] + SetOperationMetadata [12-34] [UNION @{ key = 1 } ALL] SetOperationType [12-17] [UNION] SetOperationAllOrDistinct [31-34] [ALL] Hint [18-30] [@{ key = 1 }] @@ -914,8 +914,8 @@ UNION @{ key = 2 } ALL QueryStatement [0-80] [select 3 UNION...select 2)] Query [0-80] [select 3 UNION...select 2)] SetOperation(UNION ALL) [0-80] [select 3 UNION...select 2)] - SetOperationMetadataList [8-31] [UNION @{ key = 2 } ALL] - SetOperationMetadata [8-31] [UNION @{ key = 2 } ALL] + SetOperationMetadataList [9-31] [UNION @{ key = 2 } ALL] + SetOperationMetadata [9-31] [UNION @{ key = 2 } ALL] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [28-31] [ALL] Hint [15-27] [@{ key = 2 }] @@ -928,8 +928,8 @@ QueryStatement [0-80] [select 3 UNION...select 2)] IntLiteral(3) [7-8] [3] Query [35-79] [select 1...select 2] SetOperation(UNION ALL) [35-79] [select 1...select 2] - SetOperationMetadataList [43-68] [UNION @{ key = 1 } ALL] - SetOperationMetadata [43-68] [UNION @{ key = 1 } ALL] + SetOperationMetadataList [46-68] [UNION @{ key = 1 } ALL] + SetOperationMetadata [46-68] [UNION @{ key = 1 } ALL] SetOperationType [46-51] [UNION] SetOperationAllOrDistinct [65-68] [ALL] Hint [52-64] [@{ key = 1 }] @@ -963,8 +963,8 @@ ALTERNATION GROUP: UNION QueryStatement [0-30] [SELECT 3 UNION @5 ALL SELECT 2] Query [0-30] [SELECT 3 UNION @5 ALL SELECT 2] SetOperation(UNION ALL) [0-30] [SELECT 3 UNION @5 ALL SELECT 2] - SetOperationMetadataList [8-21] [UNION @5 ALL] - SetOperationMetadata [8-21] [UNION @5 ALL] + SetOperationMetadataList [9-21] [UNION @5 ALL] + SetOperationMetadata [9-21] [UNION @5 ALL] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [18-21] [ALL] Hint [15-17] [@5] @@ -989,8 +989,8 @@ ALTERNATION GROUP: INTERSECT QueryStatement [0-34] [SELECT 3 INTERSEC...L SELECT 2] Query [0-34] [SELECT 3 INTERSEC...L SELECT 2] SetOperation(INTERSECT ALL) [0-34] [SELECT 3 INTERSEC...L SELECT 2] - SetOperationMetadataList [8-25] [INTERSECT @5 ALL] - SetOperationMetadata [8-25] [INTERSECT @5 ALL] + SetOperationMetadataList [9-25] [INTERSECT @5 ALL] + SetOperationMetadata [9-25] [INTERSECT @5 ALL] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [22-25] [ALL] Hint [19-21] [@5] @@ -1015,8 +1015,8 @@ ALTERNATION GROUP: EXCEPT QueryStatement [0-31] [SELECT 3 EXCEPT...ALL SELECT 2] Query [0-31] [SELECT 3 EXCEPT...ALL SELECT 2] SetOperation(EXCEPT ALL) [0-31] [SELECT 3 EXCEPT...ALL SELECT 2] - SetOperationMetadataList [8-22] [EXCEPT @5 ALL] - SetOperationMetadata [8-22] [EXCEPT @5 ALL] + SetOperationMetadataList [9-22] [EXCEPT @5 ALL] + SetOperationMetadata [9-22] [EXCEPT @5 ALL] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [19-22] [ALL] Hint [16-18] [@5] diff --git a/zetasql/parser/testdata/set_operation_corresponding.test b/zetasql/parser/testdata/set_operation_corresponding.test index 06add5d1e..266469efa 100644 --- a/zetasql/parser/testdata/set_operation_corresponding.test +++ b/zetasql/parser/testdata/set_operation_corresponding.test @@ -7,12 +7,12 @@ select null from T2 QueryStatement [0-94] [select abc...null from T2] Query [0-94] [select abc...null from T2] SetOperation(UNION ALL) [0-94] [select abc...null from T2] - SetOperationMetadataList [17-74] [UNION ALL...CORRESPONDING] - SetOperationMetadata [17-41] [UNION ALL CORRESPONDING] + SetOperationMetadataList [18-74] [UNION ALL...CORRESPONDING] + SetOperationMetadata [18-41] [UNION ALL CORRESPONDING] SetOperationType [18-23] [UNION] SetOperationAllOrDistinct [24-27] [ALL] SetOperationColumnMatchMode [28-41] [CORRESPONDING] - SetOperationMetadata [50-74] [UNION ALL CORRESPONDING] + SetOperationMetadata [51-74] [UNION ALL CORRESPONDING] SetOperationType [51-56] [UNION] SetOperationAllOrDistinct [57-60] [ALL] SetOperationColumnMatchMode [61-74] [CORRESPONDING] @@ -66,8 +66,8 @@ QueryStatement [0-57] [select 1 from...select 3)] TableSubquery [14-57] [(select 2...select 3)] Query [15-56] [select 2 UNION...DING select 3] SetOperation(UNION ALL) [15-56] [select 2 UNION...DING select 3] - SetOperationMetadataList [23-47] [UNION ALL CORRESPONDING] - SetOperationMetadata [23-47] [UNION ALL CORRESPONDING] + SetOperationMetadataList [24-47] [UNION ALL CORRESPONDING] + SetOperationMetadata [24-47] [UNION ALL CORRESPONDING] SetOperationType [24-29] [UNION] SetOperationAllOrDistinct [30-33] [ALL] SetOperationColumnMatchMode [34-47] [CORRESPONDING] @@ -100,8 +100,8 @@ select 2 QueryStatement [0-46] [select 1 UNION...DING select 2] Query [0-46] [select 1 UNION...DING select 2] SetOperation(UNION DISTINCT) [0-46] [select 1 UNION...DING select 2] - SetOperationMetadataList [8-37] [UNION DISTINCT CORRESPONDING] - SetOperationMetadata [8-37] [UNION DISTINCT CORRESPONDING] + SetOperationMetadataList [9-37] [UNION DISTINCT CORRESPONDING] + SetOperationMetadata [9-37] [UNION DISTINCT CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-23] [DISTINCT] SetOperationColumnMatchMode [24-37] [CORRESPONDING] @@ -138,8 +138,8 @@ select 2 QueryStatement [0-42] [select 1 EXCEPT...ING select 2] Query [0-42] [select 1 EXCEPT...ING select 2] SetOperation(EXCEPT ALL) [0-42] [select 1 EXCEPT...ING select 2] - SetOperationMetadataList [8-33] [EXCEPT ALL CORRESPONDING] - SetOperationMetadata [8-33] [EXCEPT ALL CORRESPONDING] + SetOperationMetadataList [9-33] [EXCEPT ALL CORRESPONDING] + SetOperationMetadata [9-33] [EXCEPT ALL CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnMatchMode [20-33] [CORRESPONDING] @@ -166,8 +166,8 @@ select 2 QueryStatement [0-47] [select 1 EXCEPT...ING select 2] Query [0-47] [select 1 EXCEPT...ING select 2] SetOperation(EXCEPT DISTINCT) [0-47] [select 1 EXCEPT...ING select 2] - SetOperationMetadataList [8-38] [EXCEPT DISTINCT CORRESPONDING] - SetOperationMetadata [8-38] [EXCEPT DISTINCT CORRESPONDING] + SetOperationMetadataList [9-38] [EXCEPT DISTINCT CORRESPONDING] + SetOperationMetadata [9-38] [EXCEPT DISTINCT CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnMatchMode [25-38] [CORRESPONDING] @@ -214,8 +214,8 @@ select 2 QueryStatement [0-45] [select 1 INTERSEC...G select 2] Query [0-45] [select 1 INTERSEC...G select 2] SetOperation(INTERSECT ALL) [0-45] [select 1 INTERSEC...G select 2] - SetOperationMetadataList [8-36] [INTERSECT ALL CORRESPONDING] - SetOperationMetadata [8-36] [INTERSECT ALL CORRESPONDING] + SetOperationMetadataList [9-36] [INTERSECT ALL CORRESPONDING] + SetOperationMetadata [9-36] [INTERSECT ALL CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-22] [ALL] SetOperationColumnMatchMode [23-36] [CORRESPONDING] @@ -242,8 +242,8 @@ select 2 QueryStatement [0-50] [select 1 INTERSEC...G select 2] Query [0-50] [select 1 INTERSEC...G select 2] SetOperation(INTERSECT DISTINCT) [0-50] [select 1 INTERSEC...G select 2] - SetOperationMetadataList [8-41] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-41] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [9-41] [INTERSECT...CORRESPONDING] + SetOperationMetadata [9-41] [INTERSECT...CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-27] [DISTINCT] SetOperationColumnMatchMode [28-41] [CORRESPONDING] @@ -282,12 +282,12 @@ select 1 QueryStatement [0-84] [select 1 UNION...DING select 1] Query [0-84] [select 1 UNION...DING select 1] SetOperation(UNION DISTINCT) [0-84] [select 1 UNION...DING select 1] - SetOperationMetadataList [8-75] [UNION DISTINCT...CORRESPONDING] - SetOperationMetadata [8-37] [UNION DISTINCT CORRESPONDING] + SetOperationMetadataList [9-75] [UNION DISTINCT...CORRESPONDING] + SetOperationMetadata [9-37] [UNION DISTINCT CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-23] [DISTINCT] SetOperationColumnMatchMode [24-37] [CORRESPONDING] - SetOperationMetadata [46-75] [UNION DISTINCT CORRESPONDING] + SetOperationMetadata [47-75] [UNION DISTINCT CORRESPONDING] SetOperationType [47-52] [UNION] SetOperationAllOrDistinct [53-61] [DISTINCT] SetOperationColumnMatchMode [62-75] [CORRESPONDING] @@ -323,12 +323,12 @@ select 3 QueryStatement [0-76] [select 1 EXCEPT...ING select 3] Query [0-76] [select 1 EXCEPT...ING select 3] SetOperation(EXCEPT ALL) [0-76] [select 1 EXCEPT...ING select 3] - SetOperationMetadataList [8-67] [EXCEPT ALL...CORRESPONDING] - SetOperationMetadata [8-33] [EXCEPT ALL CORRESPONDING] + SetOperationMetadataList [9-67] [EXCEPT ALL...CORRESPONDING] + SetOperationMetadata [9-33] [EXCEPT ALL CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-19] [ALL] SetOperationColumnMatchMode [20-33] [CORRESPONDING] - SetOperationMetadata [42-67] [EXCEPT ALL CORRESPONDING] + SetOperationMetadata [43-67] [EXCEPT ALL CORRESPONDING] SetOperationType [43-49] [EXCEPT] SetOperationAllOrDistinct [50-53] [ALL] SetOperationColumnMatchMode [54-67] [CORRESPONDING] @@ -364,12 +364,12 @@ select 1 QueryStatement [0-86] [select 1 EXCEPT...ING select 1] Query [0-86] [select 1 EXCEPT...ING select 1] SetOperation(EXCEPT DISTINCT) [0-86] [select 1 EXCEPT...ING select 1] - SetOperationMetadataList [8-77] [EXCEPT DISTINCT...ORRESPONDING] - SetOperationMetadata [8-38] [EXCEPT DISTINCT CORRESPONDING] + SetOperationMetadataList [9-77] [EXCEPT DISTINCT...ORRESPONDING] + SetOperationMetadata [9-38] [EXCEPT DISTINCT CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-24] [DISTINCT] SetOperationColumnMatchMode [25-38] [CORRESPONDING] - SetOperationMetadata [47-77] [EXCEPT DISTINCT CORRESPONDING] + SetOperationMetadata [48-77] [EXCEPT DISTINCT CORRESPONDING] SetOperationType [48-54] [EXCEPT] SetOperationAllOrDistinct [55-63] [DISTINCT] SetOperationColumnMatchMode [64-77] [CORRESPONDING] @@ -405,12 +405,12 @@ select 3 QueryStatement [0-82] [select 1 INTERSEC...G select 3] Query [0-82] [select 1 INTERSEC...G select 3] SetOperation(INTERSECT ALL) [0-82] [select 1 INTERSEC...G select 3] - SetOperationMetadataList [8-73] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-36] [INTERSECT ALL CORRESPONDING] + SetOperationMetadataList [9-73] [INTERSECT...CORRESPONDING] + SetOperationMetadata [9-36] [INTERSECT ALL CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-22] [ALL] SetOperationColumnMatchMode [23-36] [CORRESPONDING] - SetOperationMetadata [45-73] [INTERSECT ALL CORRESPONDING] + SetOperationMetadata [46-73] [INTERSECT ALL CORRESPONDING] SetOperationType [46-55] [INTERSECT] SetOperationAllOrDistinct [56-59] [ALL] SetOperationColumnMatchMode [60-73] [CORRESPONDING] @@ -446,12 +446,12 @@ select 3 QueryStatement [0-92] [select 1 INTERSEC...G select 3] Query [0-92] [select 1 INTERSEC...G select 3] SetOperation(INTERSECT DISTINCT) [0-92] [select 1 INTERSEC...G select 3] - SetOperationMetadataList [8-83] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-41] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [9-83] [INTERSECT...CORRESPONDING] + SetOperationMetadata [9-41] [INTERSECT...CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-27] [DISTINCT] SetOperationColumnMatchMode [28-41] [CORRESPONDING] - SetOperationMetadata [50-83] [INTERSECT...CORRESPONDING] + SetOperationMetadata [51-83] [INTERSECT...CORRESPONDING] SetOperationType [51-60] [INTERSECT] SetOperationAllOrDistinct [61-69] [DISTINCT] SetOperationColumnMatchMode [70-83] [CORRESPONDING] @@ -498,8 +498,8 @@ UNION ALL CORRESPONDING QueryStatement [0-77] [select 1 UNION...select 3)] Query [0-77] [select 1 UNION...select 3)] SetOperation(UNION ALL) [0-77] [select 1 UNION...select 3)] - SetOperationMetadataList [8-32] [UNION ALL CORRESPONDING] - SetOperationMetadata [8-32] [UNION ALL CORRESPONDING] + SetOperationMetadataList [9-32] [UNION ALL CORRESPONDING] + SetOperationMetadata [9-32] [UNION ALL CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-18] [ALL] SetOperationColumnMatchMode [19-32] [CORRESPONDING] @@ -509,8 +509,8 @@ QueryStatement [0-77] [select 1 UNION...select 3)] IntLiteral(1) [7-8] [1] Query [34-76] [select 2 EXCEPT...ING select 3] SetOperation(EXCEPT ALL) [34-76] [select 2 EXCEPT...ING select 3] - SetOperationMetadataList [42-67] [EXCEPT ALL CORRESPONDING] - SetOperationMetadata [42-67] [EXCEPT ALL CORRESPONDING] + SetOperationMetadataList [43-67] [EXCEPT ALL CORRESPONDING] + SetOperationMetadata [43-67] [EXCEPT ALL CORRESPONDING] SetOperationType [43-49] [EXCEPT] SetOperationAllOrDistinct [50-53] [ALL] SetOperationColumnMatchMode [54-67] [CORRESPONDING] @@ -541,8 +541,8 @@ INTERSECT ALL CORRESPONDING QueryStatement [0-89] [select 1 INTERSEC...select 3)] Query [0-89] [select 1 INTERSEC...select 3)] SetOperation(INTERSECT ALL) [0-89] [select 1 INTERSEC...select 3)] - SetOperationMetadataList [8-36] [INTERSECT ALL CORRESPONDING] - SetOperationMetadata [8-36] [INTERSECT ALL CORRESPONDING] + SetOperationMetadataList [9-36] [INTERSECT ALL CORRESPONDING] + SetOperationMetadata [9-36] [INTERSECT ALL CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [19-22] [ALL] SetOperationColumnMatchMode [23-36] [CORRESPONDING] @@ -552,8 +552,8 @@ QueryStatement [0-89] [select 1 INTERSEC...select 3)] IntLiteral(1) [7-8] [1] Query [38-88] [select 2 INTERSEC...G select 3] SetOperation(INTERSECT DISTINCT) [38-88] [select 2 INTERSEC...G select 3] - SetOperationMetadataList [46-79] [INTERSECT...CORRESPONDING] - SetOperationMetadata [46-79] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [47-79] [INTERSECT...CORRESPONDING] + SetOperationMetadata [47-79] [INTERSECT...CORRESPONDING] SetOperationType [47-56] [INTERSECT] SetOperationAllOrDistinct [57-65] [DISTINCT] SetOperationColumnMatchMode [66-79] [CORRESPONDING] @@ -587,8 +587,8 @@ UNION ALL CORRESPONDING QueryStatement [0-118] [select 1 UNION...select 4))] Query [0-118] [select 1 UNION...select 4))] SetOperation(UNION ALL) [0-118] [select 1 UNION...select 4))] - SetOperationMetadataList [8-32] [UNION ALL CORRESPONDING] - SetOperationMetadata [8-32] [UNION ALL CORRESPONDING] + SetOperationMetadataList [9-32] [UNION ALL CORRESPONDING] + SetOperationMetadata [9-32] [UNION ALL CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [15-18] [ALL] SetOperationColumnMatchMode [19-32] [CORRESPONDING] @@ -598,8 +598,8 @@ QueryStatement [0-118] [select 1 UNION...select 4))] IntLiteral(1) [7-8] [1] Query [34-117] [select 2...select 4)] SetOperation(INTERSECT ALL) [34-117] [select 2...select 4)] - SetOperationMetadataList [42-71] [INTERSECT ALL CORRESPONDING] - SetOperationMetadata [42-71] [INTERSECT ALL CORRESPONDING] + SetOperationMetadataList [44-71] [INTERSECT ALL CORRESPONDING] + SetOperationMetadata [44-71] [INTERSECT ALL CORRESPONDING] SetOperationType [44-53] [INTERSECT] SetOperationAllOrDistinct [54-57] [ALL] SetOperationColumnMatchMode [58-71] [CORRESPONDING] @@ -609,8 +609,8 @@ QueryStatement [0-118] [select 1 UNION...select 4))] IntLiteral(2) [41-42] [2] Query [74-116] [select 3 EXCEPT...ING select 4] SetOperation(EXCEPT ALL) [74-116] [select 3 EXCEPT...ING select 4] - SetOperationMetadataList [82-107] [EXCEPT ALL CORRESPONDING] - SetOperationMetadata [82-107] [EXCEPT ALL CORRESPONDING] + SetOperationMetadataList [83-107] [EXCEPT ALL CORRESPONDING] + SetOperationMetadata [83-107] [EXCEPT ALL CORRESPONDING] SetOperationType [83-89] [EXCEPT] SetOperationAllOrDistinct [90-93] [ALL] SetOperationColumnMatchMode [94-107] [CORRESPONDING] @@ -646,8 +646,8 @@ select 2 QueryStatement [0-55] [select 1 UNION...DING select 2] Query [0-55] [select 1 UNION...DING select 2] SetOperation(UNION ALL) [0-55] [select 1 UNION...DING select 2] - SetOperationMetadataList [8-46] [UNION @{...CORRESPONDING] - SetOperationMetadata [8-46] [UNION @{...CORRESPONDING] + SetOperationMetadataList [9-46] [UNION @{...CORRESPONDING] + SetOperationMetadata [9-46] [UNION @{...CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [29-32] [ALL] Hint [16-28] [@{ key = 5 }] @@ -680,8 +680,8 @@ select 2 QueryStatement [0-59] [select 1 INTERSEC...G select 2] Query [0-59] [select 1 INTERSEC...G select 2] SetOperation(INTERSECT ALL) [0-59] [select 1 INTERSEC...G select 2] - SetOperationMetadataList [8-50] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-50] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [9-50] [INTERSECT...CORRESPONDING] + SetOperationMetadata [9-50] [INTERSECT...CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [33-36] [ALL] Hint [20-32] [@{ key = 5 }] @@ -714,8 +714,8 @@ select 2 QueryStatement [0-56] [select 1 EXCEPT...ING select 2] Query [0-56] [select 1 EXCEPT...ING select 2] SetOperation(EXCEPT ALL) [0-56] [select 1 EXCEPT...ING select 2] - SetOperationMetadataList [8-47] [EXCEPT @{...CORRESPONDING] - SetOperationMetadata [8-47] [EXCEPT @{...CORRESPONDING] + SetOperationMetadataList [9-47] [EXCEPT @{...CORRESPONDING] + SetOperationMetadata [9-47] [EXCEPT @{...CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [30-33] [ALL] Hint [17-29] [@{ key = 5 }] @@ -748,8 +748,8 @@ select 2 QueryStatement [0-60] [select 1 UNION...DING select 2] Query [0-60] [select 1 UNION...DING select 2] SetOperation(UNION DISTINCT) [0-60] [select 1 UNION...DING select 2] - SetOperationMetadataList [8-51] [UNION @{...CORRESPONDING] - SetOperationMetadata [8-51] [UNION @{...CORRESPONDING] + SetOperationMetadataList [9-51] [UNION @{...CORRESPONDING] + SetOperationMetadata [9-51] [UNION @{...CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [29-37] [DISTINCT] Hint [16-28] [@{ key = 5 }] @@ -781,8 +781,8 @@ select 2 QueryStatement [0-63] [select 1 INTERSEC...G select 2] Query [0-63] [select 1 INTERSEC...G select 2] SetOperation(INTERSECT DISTINCT) [0-63] [select 1 INTERSEC...G select 2] - SetOperationMetadataList [8-54] [INTERSECT...CORRESPONDING] - SetOperationMetadata [8-54] [INTERSECT...CORRESPONDING] + SetOperationMetadataList [9-54] [INTERSECT...CORRESPONDING] + SetOperationMetadata [9-54] [INTERSECT...CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [32-40] [DISTINCT] Hint [19-31] [@{ key = 5 }] @@ -815,8 +815,8 @@ select 2 QueryStatement [0-61] [select 1 EXCEPT...ING select 2] Query [0-61] [select 1 EXCEPT...ING select 2] SetOperation(EXCEPT DISTINCT) [0-61] [select 1 EXCEPT...ING select 2] - SetOperationMetadataList [8-52] [EXCEPT @{...CORRESPONDING] - SetOperationMetadata [8-52] [EXCEPT @{...CORRESPONDING] + SetOperationMetadataList [9-52] [EXCEPT @{...CORRESPONDING] + SetOperationMetadata [9-52] [EXCEPT @{...CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [30-38] [DISTINCT] Hint [17-29] [@{ key = 5 }] @@ -850,8 +850,8 @@ select 3 QueryStatement [0-87] [select 1 UNION...DING select 3] Query [0-87] [select 1 UNION...DING select 3] SetOperation(UNION ALL) [0-87] [select 1 UNION...DING select 3] - SetOperationMetadataList [8-78] [UNION @{ key...CORRESPONDING] - SetOperationMetadata [8-45] [UNION @{ key...CORRESPONDING] + SetOperationMetadataList [9-78] [UNION @{ key...CORRESPONDING] + SetOperationMetadata [9-45] [UNION @{ key...CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [28-31] [ALL] Hint [15-27] [@{ key = 5 }] @@ -859,7 +859,7 @@ QueryStatement [0-87] [select 1 UNION...DING select 3] Identifier(key) [18-21] [key] IntLiteral(5) [24-25] [5] SetOperationColumnMatchMode [32-45] [CORRESPONDING] - SetOperationMetadata [54-78] [UNION ALL CORRESPONDING] + SetOperationMetadata [55-78] [UNION ALL CORRESPONDING] SetOperationType [55-60] [UNION] SetOperationAllOrDistinct [61-64] [ALL] SetOperationColumnMatchMode [65-78] [CORRESPONDING] @@ -896,8 +896,8 @@ select 3 QueryStatement [0-106] [(select 1...ESPONDING select 3] Query [0-106] [(select 1...ESPONDING select 3] SetOperation(UNION ALL) [0-106] [(select 1...ESPONDING select 3] - SetOperationMetadataList [60-97] [UNION @{ key...CORRESPONDING] - SetOperationMetadata [60-97] [UNION @{ key...CORRESPONDING] + SetOperationMetadataList [61-97] [UNION @{ key...CORRESPONDING] + SetOperationMetadata [61-97] [UNION @{ key...CORRESPONDING] SetOperationType [61-66] [UNION] SetOperationAllOrDistinct [80-83] [ALL] Hint [67-79] [@{ key = 2 }] @@ -907,8 +907,8 @@ QueryStatement [0-106] [(select 1...ESPONDING select 3] SetOperationColumnMatchMode [84-97] [CORRESPONDING] Query [1-59] [select 1...select 2] SetOperation(UNION ALL) [1-59] [select 1...select 2] - SetOperationMetadataList [9-48] [UNION @{ key...CORRESPONDING] - SetOperationMetadata [9-48] [UNION @{ key...CORRESPONDING] + SetOperationMetadataList [12-48] [UNION @{ key...CORRESPONDING] + SetOperationMetadata [12-48] [UNION @{ key...CORRESPONDING] SetOperationType [12-17] [UNION] SetOperationAllOrDistinct [31-34] [ALL] Hint [18-30] [@{ key = 1 }] @@ -950,8 +950,8 @@ UNION @{ key = 2 } ALL CORRESPONDING QueryStatement [0-108] [select 3 UNION...select 2)] Query [0-108] [select 3 UNION...select 2)] SetOperation(UNION ALL) [0-108] [select 3 UNION...select 2)] - SetOperationMetadataList [8-45] [UNION @{ key...CORRESPONDING] - SetOperationMetadata [8-45] [UNION @{ key...CORRESPONDING] + SetOperationMetadataList [9-45] [UNION @{ key...CORRESPONDING] + SetOperationMetadata [9-45] [UNION @{ key...CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [28-31] [ALL] Hint [15-27] [@{ key = 2 }] @@ -965,8 +965,8 @@ QueryStatement [0-108] [select 3 UNION...select 2)] IntLiteral(3) [7-8] [3] Query [49-107] [select 1...select 2] SetOperation(UNION ALL) [49-107] [select 1...select 2] - SetOperationMetadataList [57-96] [UNION @{ key...CORRESPONDING] - SetOperationMetadata [57-96] [UNION @{ key...CORRESPONDING] + SetOperationMetadataList [60-96] [UNION @{ key...CORRESPONDING] + SetOperationMetadata [60-96] [UNION @{ key...CORRESPONDING] SetOperationType [60-65] [UNION] SetOperationAllOrDistinct [79-82] [ALL] Hint [66-78] [@{ key = 1 }] @@ -1001,8 +1001,8 @@ ALTERNATION GROUP: UNION QueryStatement [0-44] [SELECT 3 UNION...DING SELECT 2] Query [0-44] [SELECT 3 UNION...DING SELECT 2] SetOperation(UNION ALL) [0-44] [SELECT 3 UNION...DING SELECT 2] - SetOperationMetadataList [8-35] [UNION @5 ALL CORRESPONDING] - SetOperationMetadata [8-35] [UNION @5 ALL CORRESPONDING] + SetOperationMetadataList [9-35] [UNION @5 ALL CORRESPONDING] + SetOperationMetadata [9-35] [UNION @5 ALL CORRESPONDING] SetOperationType [9-14] [UNION] SetOperationAllOrDistinct [18-21] [ALL] Hint [15-17] [@5] @@ -1028,8 +1028,8 @@ ALTERNATION GROUP: INTERSECT QueryStatement [0-48] [SELECT 3 INTERSEC...G SELECT 2] Query [0-48] [SELECT 3 INTERSEC...G SELECT 2] SetOperation(INTERSECT ALL) [0-48] [SELECT 3 INTERSEC...G SELECT 2] - SetOperationMetadataList [8-39] [INTERSECT @5 ALL CORRESPONDING] - SetOperationMetadata [8-39] [INTERSECT @5 ALL CORRESPONDING] + SetOperationMetadataList [9-39] [INTERSECT @5 ALL CORRESPONDING] + SetOperationMetadata [9-39] [INTERSECT @5 ALL CORRESPONDING] SetOperationType [9-18] [INTERSECT] SetOperationAllOrDistinct [22-25] [ALL] Hint [19-21] [@5] @@ -1055,8 +1055,8 @@ ALTERNATION GROUP: EXCEPT QueryStatement [0-45] [SELECT 3 EXCEPT...ING SELECT 2] Query [0-45] [SELECT 3 EXCEPT...ING SELECT 2] SetOperation(EXCEPT ALL) [0-45] [SELECT 3 EXCEPT...ING SELECT 2] - SetOperationMetadataList [8-36] [EXCEPT @5 ALL CORRESPONDING] - SetOperationMetadata [8-36] [EXCEPT @5 ALL CORRESPONDING] + SetOperationMetadataList [9-36] [EXCEPT @5 ALL CORRESPONDING] + SetOperationMetadata [9-36] [EXCEPT @5 ALL CORRESPONDING] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [19-22] [ALL] Hint [16-18] [@5] diff --git a/zetasql/parser/testdata/star.test b/zetasql/parser/testdata/star.test index d0a1facde..5a5aa7911 100644 --- a/zetasql/parser/testdata/star.test +++ b/zetasql/parser/testdata/star.test @@ -311,8 +311,8 @@ select * except (c) QueryStatement [0-52] [select * except...except (c)] Query [0-52] [select * except...except (c)] SetOperation(EXCEPT ALL) [0-52] [select * except...except (c)] - SetOperationMetadataList [21-32] [except all] - SetOperationMetadata [21-32] [except all] + SetOperationMetadataList [22-32] [except all] + SetOperationMetadata [22-32] [except all] SetOperationType [22-28] [except] SetOperationAllOrDistinct [29-32] [all] Select [0-21] [select * except (a,b)] @@ -345,8 +345,8 @@ select * QueryStatement [0-52] [select * replace...ct select *] Query [0-52] [select * replace...ct select *] SetOperation(EXCEPT DISTINCT) [0-52] [select * replace...ct select *] - SetOperationMetadataList [27-43] [except distinct] - SetOperationMetadata [27-43] [except distinct] + SetOperationMetadataList [28-43] [except distinct] + SetOperationMetadata [28-43] [except distinct] SetOperationType [28-34] [except] SetOperationAllOrDistinct [35-43] [distinct] Select [0-27] [select * replace (a+1 as b)] @@ -379,8 +379,8 @@ select x.* QueryStatement [0-48] [select x.*...select x.*] Query [0-48] [select x.*...select x.*] SetOperation(EXCEPT ALL) [0-48] [select x.*...select x.*] - SetOperationMetadataList [26-37] [except all] - SetOperationMetadata [26-37] [except all] + SetOperationMetadataList [27-37] [except all] + SetOperationMetadata [27-37] [except all] SetOperationType [27-33] [except] SetOperationAllOrDistinct [34-37] [all] Select [0-26] [select x.* replace(a as b)] @@ -415,8 +415,8 @@ select x.* QueryStatement [0-47] [select x.*...select x.*] Query [0-47] [select x.*...select x.*] SetOperation(EXCEPT DISTINCT) [0-47] [select x.*...select x.*] - SetOperationMetadataList [20-36] [except distinct] - SetOperationMetadata [20-36] [except distinct] + SetOperationMetadataList [21-36] [except distinct] + SetOperationMetadata [21-36] [except distinct] SetOperationType [21-27] [except] SetOperationAllOrDistinct [28-36] [distinct] Select [0-20] [select x.* except(a)] @@ -586,8 +586,8 @@ SELECT * EXCEPT ALL SELECT 3 QueryStatement [0-28] [SELECT * EXCEPT ALL SELECT 3] Query [0-28] [SELECT * EXCEPT ALL SELECT 3] SetOperation(EXCEPT ALL) [0-28] [SELECT * EXCEPT ALL SELECT 3] - SetOperationMetadataList [8-19] [EXCEPT ALL] - SetOperationMetadata [8-19] [EXCEPT ALL] + SetOperationMetadataList [9-19] [EXCEPT ALL] + SetOperationMetadata [9-19] [EXCEPT ALL] SetOperationType [9-15] [EXCEPT] SetOperationAllOrDistinct [16-19] [ALL] Select [0-8] [SELECT *] diff --git a/zetasql/parser/testdata/tvf.test b/zetasql/parser/testdata/tvf.test index 66b6e8814..37bcabe10 100644 --- a/zetasql/parser/testdata/tvf.test +++ b/zetasql/parser/testdata/tvf.test @@ -2718,3 +2718,45 @@ FROM KeyValue GROUP BY Key ), DESCRIPTOR(Key)) +== + +# Regression test for b/358469854 where the unparser used to print the inner +# alias in the wrong place. +SELECT * FROM tvf() AS t PIVOT(sum(a) FOR b IN (1, 2, 3)) AS p +-- +QueryStatement [0-62] [SELECT * FROM..., 3)) AS p] + Query [0-62] [SELECT * FROM..., 3)) AS p] + Select [0-62] [SELECT * FROM..., 3)) AS p] + SelectList [7-8] [*] + SelectColumn [7-8] [*] + Star(*) [7-8] [*] + FromClause [9-62] [FROM tvf()..., 3)) AS p] + TVF [14-62] [tvf() AS t..., 3)) AS p] + PathExpression [14-17] [tvf] + Identifier(tvf) [14-17] [tvf] + Alias [20-24] [AS t] + Identifier(t) [23-24] [t] + PivotClause [25-62] [PIVOT(sum(..., 3)) AS p] + PivotExpressionList [31-37] [sum(a)] + PivotExpression [31-37] [sum(a)] + FunctionCall [31-37] [sum(a)] + PathExpression [31-34] [sum] + Identifier(sum) [31-34] [sum] + PathExpression [35-36] [a] + Identifier(a) [35-36] [a] + PathExpression [42-43] [b] + Identifier(b) [42-43] [b] + PivotValueList [48-55] [1, 2, 3] + PivotValue [48-49] [1] + IntLiteral(1) [48-49] [1] + PivotValue [51-52] [2] + IntLiteral(2) [51-52] [2] + PivotValue [54-55] [3] + IntLiteral(3) [54-55] [3] + Alias [58-62] [AS p] + Identifier(p) [61-62] [p] +-- +SELECT + * +FROM + tvf() AS t PIVOT(sum(a) FOR b IN (1, 2, 3)) AS p diff --git a/zetasql/parser/testdata/unpivot.test b/zetasql/parser/testdata/unpivot.test index c8bf482fc..06c8742d1 100644 --- a/zetasql/parser/testdata/unpivot.test +++ b/zetasql/parser/testdata/unpivot.test @@ -243,14 +243,14 @@ QueryStatement [0-67] [SELECT * FROM...', y "3"))] PathExpressionList [52-53] [x] PathExpression [52-53] [x] Identifier(x) [52-53] [x] - UnpivotInItemLabel [54-58] ['2'] + UnpivotInItemLabel [55-58] ['2'] StringLiteral [55-58] ['2'] StringLiteralComponent('2') [55-58] ['2'] UnpivotInItem [60-65] [y "3"] PathExpressionList [60-61] [y] PathExpression [60-61] [y] Identifier(y) [60-61] [y] - UnpivotInItemLabel [61-65] ["3"] + UnpivotInItemLabel [62-65] ["3"] StringLiteral [62-65] ["3"] StringLiteralComponent("3") [62-65] ["3"] -- diff --git a/zetasql/parser/testdata/with.test b/zetasql/parser/testdata/with.test index 9035ebd73..7c83b5d21 100644 --- a/zetasql/parser/testdata/with.test +++ b/zetasql/parser/testdata/with.test @@ -601,8 +601,8 @@ QueryStatement [0-84] [WITH q1 AS...* from q1] Identifier(q1) [5-7] [q1] Query [12-39] [select 5 UNION ALL select 6] SetOperation(UNION ALL) [12-39] [select 5 UNION ALL select 6] - SetOperationMetadataList [20-30] [UNION ALL] - SetOperationMetadata [20-30] [UNION ALL] + SetOperationMetadataList [21-30] [UNION ALL] + SetOperationMetadata [21-30] [UNION ALL] SetOperationType [21-26] [UNION] SetOperationAllOrDistinct [27-30] [ALL] Select [12-20] [select 5] @@ -614,8 +614,8 @@ QueryStatement [0-84] [WITH q1 AS...* from q1] SelectColumn [38-39] [6] IntLiteral(6) [38-39] [6] SetOperation(UNION ALL) [41-84] [select * from...* from q1] - SetOperationMetadataList [57-67] [UNION ALL] - SetOperationMetadata [57-67] [UNION ALL] + SetOperationMetadataList [58-67] [UNION ALL] + SetOperationMetadata [58-67] [UNION ALL] SetOperationType [58-63] [UNION] SetOperationAllOrDistinct [64-67] [ALL] Select [41-57] [select * from q1] @@ -672,8 +672,8 @@ UNION ALL QueryStatement [0-75] [select * from...* from q1)] Query [0-75] [select * from...* from q1)] SetOperation(UNION ALL) [0-75] [select * from...* from q1)] - SetOperationMetadataList [16-26] [UNION ALL] - SetOperationMetadata [16-26] [UNION ALL] + SetOperationMetadataList [17-26] [UNION ALL] + SetOperationMetadata [17-26] [UNION ALL] SetOperationType [17-22] [UNION] SetOperationAllOrDistinct [23-26] [ALL] Select [0-16] [select * from q1] @@ -902,8 +902,8 @@ QueryStatement [0-111] [with recursive...from test] Identifier(test) [15-19] [test] Query [27-77] [select 1 as...from test] SetOperation(UNION ALL) [27-77] [select 1 as...from test] - SetOperationMetadataList [40-52] [union all] - SetOperationMetadata [40-52] [union all] + SetOperationMetadataList [43-52] [union all] + SetOperationMetadata [43-52] [union all] SetOperationType [43-48] [union] SetOperationAllOrDistinct [49-52] [all] Select [27-40] [select 1 as n] @@ -961,8 +961,8 @@ QueryStatement [0-117] [with recursive...from test] Identifier(test) [15-19] [test] Query [27-77] [select 1 as...from test] SetOperation(UNION ALL) [27-77] [select 1 as...from test] - SetOperationMetadataList [40-52] [union all] - SetOperationMetadata [40-52] [union all] + SetOperationMetadataList [43-52] [union all] + SetOperationMetadata [43-52] [union all] SetOperationType [43-48] [union] SetOperationAllOrDistinct [49-52] [all] Select [27-40] [select 1 as n] @@ -1021,8 +1021,8 @@ QueryStatement [0-127] [with recursive...from test] Identifier(test) [15-19] [test] Query [27-77] [select 1 as...from test] SetOperation(UNION ALL) [27-77] [select 1 as...from test] - SetOperationMetadataList [40-52] [union all] - SetOperationMetadata [40-52] [union all] + SetOperationMetadataList [43-52] [union all] + SetOperationMetadata [43-52] [union all] SetOperationType [43-48] [union] SetOperationAllOrDistinct [49-52] [all] Select [27-40] [select 1 as n] @@ -1083,8 +1083,8 @@ QueryStatement [0-119] [with recursive...from test] Identifier(test) [15-19] [test] Query [27-77] [select 1 as...from test] SetOperation(UNION ALL) [27-77] [select 1 as...from test] - SetOperationMetadataList [40-52] [union all] - SetOperationMetadata [40-52] [union all] + SetOperationMetadataList [43-52] [union all] + SetOperationMetadata [43-52] [union all] SetOperationType [43-48] [union] SetOperationAllOrDistinct [49-52] [all] Select [27-40] [select 1 as n] @@ -1144,8 +1144,8 @@ QueryStatement [0-125] [with recursive...from test] Identifier(test) [15-19] [test] Query [27-77] [select 1 as...from test] SetOperation(UNION ALL) [27-77] [select 1 as...from test] - SetOperationMetadataList [40-52] [union all] - SetOperationMetadata [40-52] [union all] + SetOperationMetadataList [43-52] [union all] + SetOperationMetadata [43-52] [union all] SetOperationType [43-48] [union] SetOperationAllOrDistinct [49-52] [all] Select [27-40] [select 1 as n] @@ -1206,8 +1206,8 @@ QueryStatement [0-135] [with recursive...from test] Identifier(test) [15-19] [test] Query [27-77] [select 1 as...from test] SetOperation(UNION ALL) [27-77] [select 1 as...from test] - SetOperationMetadataList [40-52] [union all] - SetOperationMetadata [40-52] [union all] + SetOperationMetadataList [43-52] [union all] + SetOperationMetadata [43-52] [union all] SetOperationType [43-48] [union] SetOperationAllOrDistinct [49-52] [all] Select [27-40] [select 1 as n] diff --git a/zetasql/parser/unparser.cc b/zetasql/parser/unparser.cc index 3cccb76e7..934b8ebb6 100644 --- a/zetasql/parser/unparser.cc +++ b/zetasql/parser/unparser.cc @@ -359,14 +359,17 @@ void Unparser::visitASTTVF(const ASTTVF* node, void* data) { if (node->hint() != nullptr) { node->hint()->Accept(this, data); } + if (node->alias() != nullptr) { + node->alias()->Accept(this, data); + } if (node->pivot_clause() != nullptr) { node->pivot_clause()->Accept(this, data); } if (node->unpivot_clause() != nullptr) { node->unpivot_clause()->Accept(this, data); } - if (node->alias() != nullptr) { - node->alias()->Accept(this, data); + if (node->match_recognize_clause()) { + node->match_recognize_clause()->Accept(this, data); } if (node->sample() != nullptr) { node->sample()->Accept(this, data); @@ -418,6 +421,22 @@ std::string Unparser::GetCreateStatementPrefix( return output; } +void Unparser::visitASTCreateConnectionStatement( + const ASTCreateConnectionStatement* node, void* data) { + print(GetCreateStatementPrefix(node, "CONNECTION")); + node->name()->Accept(this, data); + if (node->options_list() != nullptr) { + print("OPTIONS"); + node->options_list()->Accept(this, data); + } +} + +void Unparser::visitASTAlterConnectionStatement( + const ASTAlterConnectionStatement* node, void* data) { + print("ALTER CONNECTION"); + VisitAlterStatementBase(node, data); +} + void Unparser::visitASTCreateConstantStatement( const ASTCreateConstantStatement* node, void* data) { print(GetCreateStatementPrefix(node, "CONSTANT")); @@ -1460,6 +1479,181 @@ void Unparser::visitASTQuery(const ASTQuery* node, void* data) { PrintCloseParenIfNeeded(node); } +void Unparser::visitASTAliasedQueryExpression( + const ASTAliasedQueryExpression* node, void* data) { + println(); + print("("); + { + Formatter::Indenter indenter(&formatter_); + node->query()->Accept(this, data); + } + println(); + print(")"); + node->alias()->Accept(this, data); +} + +void Unparser::visitASTFromQuery(const ASTFromQuery* node, void* data) { + visitASTChildren(node, data); +} + +Formatter::PipeAndIndent::PipeAndIndent(Formatter* formatter) + : formatter_(formatter) { + formatter_->FormatLine(""); + formatter_->Format("|>"); + // Several of the pipe operator unparsers call the regular operator + // unparser, like "WHERE" or "LIMIT", which call println, but we want + // to skip that newline after the pipe. + formatter_->SetSuppressNextNewline(); + + // Indent any extra lines inside this pipe operator so they line up. + formatter_->Indent(3); // 3 is size of "|> ". +} + +Formatter::PipeAndIndent::~PipeAndIndent() { formatter_->Dedent(3); } + +void Unparser::visitASTPipeWhere(const ASTPipeWhere* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeSelect(const ASTPipeSelect* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeLimitOffset(const ASTPipeLimitOffset* node, + void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeOrderBy(const ASTPipeOrderBy* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeExtend(const ASTPipeExtend* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("EXTEND"); + // Only the SELECT should be present. The ASTSelect visitor + // would print "SELECT" which we don't want, but visiting the children + // will find just the one clause that exists. + visitASTChildren(node->select(), data); +} + +void Unparser::visitASTPipeRenameItem(const ASTPipeRenameItem* node, + void* data) { + node->old_name()->Accept(this, data); + print("AS"); + node->new_name()->Accept(this, data); +} + +void Unparser::visitASTPipeRename(const ASTPipeRename* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("RENAME"); + UnparseVectorWithSeparator(node->rename_item_list(), data, ","); +} + +void Unparser::visitASTPipeAggregate(const ASTPipeAggregate* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("AGGREGATE"); + // Only the SELECT and GROUP BY should be present. The ASTSelect visitor + // would print "SELECT" which we don't want, but we can visit all the + // other children the normal way, and we'll get the two clauses we want. + visitASTChildren(node->select(), data); +} + +void Unparser::visitASTPipeSetOperation(const ASTPipeSetOperation* node, + void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + node->metadata()->Accept(this, data); + println(); + UnparseVectorWithSeparator(node->inputs(), data, ",\n"); +} + +void Unparser::visitASTPipeJoin(const ASTPipeJoin* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeJoinLhsPlaceholder( + const ASTPipeJoinLhsPlaceholder* node, void* data) { + // Nothing to print. +} + +void Unparser::visitASTPipeCall(const ASTPipeCall* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("CALL"); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeWindow(const ASTPipeWindow* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("WINDOW"); + // Only the SELECT should be present. The ASTSelect visitor + // would print "SELECT" which we don't want, but visiting the children + // will find just the one clause that exists. + visitASTChildren(node->select(), data); +} + +void Unparser::visitASTPipeDistinct(const ASTPipeDistinct* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("DISTINCT"); + // Don't visit children, because by definition, pipe DISTINCT doesn't have + // any children. +} + +void Unparser::visitASTPipeTablesample(const ASTPipeTablesample* node, + void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeAs(const ASTPipeAs* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeStaticDescribe(const ASTPipeStaticDescribe* node, + void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("STATIC_DESCRIBE"); +} + +void Unparser::visitASTPipeAssert(const ASTPipeAssert* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("ASSERT"); + UnparseChildrenWithSeparator(node, data, ","); +} + +void Unparser::visitASTPipeDrop(const ASTPipeDrop* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("DROP"); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeSetItem(const ASTPipeSetItem* node, void* data) { + node->column()->Accept(this, data); + print("="); + node->expression()->Accept(this, data); +} + +void Unparser::visitASTPipeSet(const ASTPipeSet* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + print("SET"); + UnparseChildrenWithSeparator(node, data, ","); +} + +void Unparser::visitASTPipePivot(const ASTPipePivot* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + +void Unparser::visitASTPipeUnpivot(const ASTPipeUnpivot* node, void* data) { + Formatter::PipeAndIndent pipe_and_indent(&formatter_); + visitASTChildren(node, data); +} + void Unparser::visitASTSetOperation(const ASTSetOperation* node, void* data) { PrintOpenParenIfNeeded(node); if (node->metadata() == nullptr) { @@ -1729,6 +1923,9 @@ void Unparser::visitASTParenthesizedJoin(const ASTParenthesizedJoin* node, println(); print(")"); + if (node->match_recognize_clause()) { + node->match_recognize_clause()->Accept(this, data); + } if (node->sample_clause() != nullptr) { node->sample_clause()->Accept(this, data); } @@ -1810,6 +2007,21 @@ void Unparser::visitASTGroupingSetList(const ASTGroupingSetList* node, print(")"); } +void Unparser::visitASTGroupingItemOrder(const ASTGroupingItemOrder* node, + void* data) { + switch (node->ordering_spec()) { + case ASTOrderingExpression::ASC: + print("ASC"); + break; + case ASTOrderingExpression::DESC: + print("DESC"); + break; + default: + break; + } + visitASTChildren(node, data); +} + void Unparser::visitASTGroupingItem(const ASTGroupingItem* node, void* data) { ABSL_DCHECK_LE((node->expression() != nullptr) + (node->rollup() != nullptr) + (node->cube() != nullptr) + @@ -1818,6 +2030,12 @@ void Unparser::visitASTGroupingItem(const ASTGroupingItem* node, void* data) { << "At most one of expression, rollup, cube, and grouping_set_list exist"; if (node->expression() != nullptr) { node->expression()->Accept(this, data); + if (node->alias() != nullptr) { + node->alias()->Accept(this, data); + } + if (node->grouping_item_order() != nullptr) { + node->grouping_item_order()->Accept(this, data); + } } else if (node->rollup() != nullptr) { node->rollup()->Accept(this, data); } else if (node->cube() != nullptr) { @@ -1836,6 +2054,9 @@ void Unparser::visitASTGroupBy(const ASTGroupBy* node, void* data) { if (node->hint() != nullptr) { node->hint()->Accept(this, data); } + if (node->and_order_by()) { + print("AND ORDER"); + } print("BY"); if (node->all() != nullptr) { node->all()->Accept(this, data); @@ -1895,6 +2116,10 @@ void Unparser::visitASTOrderingExpression(const ASTOrderingExpression* node, print("ASC"); } if (node->null_order()) node->null_order()->Accept(this, data); + if (node->option_list()) { + print("OPTIONS"); + node->option_list()->Accept(this, data); + } } void Unparser::visitASTLimitOffset(const ASTLimitOffset* node, void* data) { @@ -2265,7 +2490,20 @@ void Unparser::visitASTDotIdentifier(const ASTDotIdentifier* node, void* data) { void Unparser::visitASTDotGeneralizedField(const ASTDotGeneralizedField* node, void* data) { PrintOpenParenIfNeeded(node); + + // We need an inner paren to avoid the dot (.) binding tightly to the inner + // expression for numbers. E.g. `SELECT (1).(x)` should unparse as the same, + // and not as `SELECT 1.(x)`, which looks like a function call on a float + // literal. + bool print_inner_paren = node->expr()->node_kind() == AST_INT_LITERAL || + node->expr()->node_kind() == AST_FLOAT_LITERAL; + if (print_inner_paren) { + print("("); + } node->expr()->Accept(this, data); + if (print_inner_paren) { + print(")"); + } print(".("); node->path()->Accept(this, data); print(")"); @@ -2303,6 +2541,11 @@ static bool IsPostfix(ASTUnaryExpression::Op op) { void Unparser::visitASTUnaryExpression(const ASTUnaryExpression* node, void* data) { + if (!ThreadHasEnoughStack()) { + println(""); + return; + } + PrintOpenParenIfNeeded(node); if (IsPostfix(node->op())) { node->operand()->Accept(this, data); @@ -2325,6 +2568,10 @@ void Unparser::visitASTFormatClause(const ASTFormatClause* node, void* data) { void Unparser::visitASTCastExpression(const ASTCastExpression* node, void* data) { + if (!ThreadHasEnoughStack()) { + println(""); + return; + } print(node->is_safe_cast() ? "SAFE_CAST(" : "CAST("); node->expr()->Accept(this, data); print("AS"); @@ -3352,6 +3599,10 @@ void Unparser::visitASTReplaceFieldsArg(const ASTReplaceFieldsArg* node, void Unparser::visitASTReplaceFieldsExpression( const ASTReplaceFieldsExpression* node, void* data) { + if (!ThreadHasEnoughStack()) { + println(""); + return; + } print("REPLACE_FIELDS("); node->expr()->Accept(this, data); print(", "); @@ -3453,6 +3704,86 @@ void Unparser::visitASTUnpivotClause(const ASTUnpivotClause* node, void* data) { } } +void Unparser::visitASTMatchRecognizeClause(const ASTMatchRecognizeClause* node, + void* data) { + println("MATCH_RECOGNIZE("); + { + Formatter::Indenter indenter(&formatter_); + if (node->partition_by() != nullptr) { + node->partition_by()->Accept(this, data); + println(); + } + ABSL_DCHECK(node->order_by() != nullptr); + node->order_by()->Accept(this, data); + println(); + + ABSL_DCHECK(node->measures() != nullptr); + print("MEASURES"); + node->measures()->Accept(this, data); + println(); + + ABSL_DCHECK(node->pattern() != nullptr); + print("PATTERN ("); + node->pattern()->Accept(this, data); + println(")"); + + ABSL_DCHECK(node->pattern_variable_definition_list() != nullptr); + print("DEFINE "); + bool is_first = true; + for (const auto& var : + node->pattern_variable_definition_list()->columns()) { + if (is_first) { + is_first = false; + } else { + print(", "); + } + // We accept the identifier here rather than alias to prevent "AS" from + // being incorrectly printed before the alias. + var->alias()->identifier()->Accept(this, data); + print("AS"); + var->expression()->Accept(this, data); + } + } + println(")"); + if (node->output_alias() != nullptr) { + node->output_alias()->Accept(this, data); + } +} + +void Unparser::visitASTRowPatternVariable(const ASTRowPatternVariable* node, + void* data) { + node->name()->Accept(this, data); +} + +void Unparser::visitASTRowPatternOperation(const ASTRowPatternOperation* node, + void* data) { + if (node->parenthesized()) { + print("("); + } + + switch (node->op_type()) { + case ASTRowPatternOperation::CONCAT: + ABSL_DCHECK_GE(node->inputs().size(), 2); + UnparseChildrenWithSeparator(node, data, " "); + break; + case ASTRowPatternOperation::ALTERNATE: + ABSL_DCHECK_GE(node->inputs().size(), 2); + UnparseChildrenWithSeparator(node, data, "|"); + break; + case ASTRowPatternOperation::EXCLUDE: + case ASTRowPatternOperation::PERMUTE: + ABSL_LOG(ERROR) << "Row pattern operation not supported: " << node->op_type(); + break; + case ASTRowPatternOperationEnums::OPERATION_TYPE_UNSPECIFIED: + ABSL_LOG(ERROR) << "Graph label operation type is not set"; + break; + } + + if (node->parenthesized()) { + print(")"); + } +} + void Unparser::visitASTSampleSize(const ASTSampleSize* node, void* data) { node->size()->Accept(this, data); print(node->GetSQLForUnit()); @@ -3835,6 +4166,10 @@ void Unparser::visitASTCreateIndexStatement(const ASTCreateIndexStatement* node, println(); node->optional_index_storing_expressions()->Accept(this, data); } + if (node->optional_partition_by() != nullptr) { + println(); + node->optional_partition_by()->Accept(this, data); + } if (node->options_list() != nullptr) { println(); print("OPTIONS"); @@ -3949,6 +4284,10 @@ void Unparser::visitASTBeginEndBlock(const ASTBeginEndBlock* node, void* data) { void Unparser::visitASTIndexAllColumns(const ASTIndexAllColumns* node, void* data) { UnparseLeafNode(node); + if (node->column_options()) { + print("WITH COLUMN OPTIONS"); + node->column_options()->Accept(this, data); + } } void Unparser::visitASTIndexItemList(const ASTIndexItemList* node, void* data) { @@ -4533,6 +4872,5 @@ void Unparser::visitASTMapType(const ASTMapType* node, void* data) { node->value_type()->Accept(this, data); print(">"); } - } // namespace parser } // namespace zetasql diff --git a/zetasql/parser/unparser.h b/zetasql/parser/unparser.h index de847aa8a..a667f3188 100644 --- a/zetasql/parser/unparser.h +++ b/zetasql/parser/unparser.h @@ -53,6 +53,20 @@ class Formatter { Formatter* formatter_; }; + // This prints a newline and the pipe symbol, and then indents. + // It's used as a scoped object inside each pipe operator unparse method. + class PipeAndIndent { + public: + explicit PipeAndIndent(Formatter* formatter); + ~PipeAndIndent(); + + PipeAndIndent(const PipeAndIndent&) = delete; + PipeAndIndent& operator=(const PipeAndIndent&) = delete; + + private: + Formatter* formatter_; + }; + explicit Formatter(std::string* unparsed) : unparsed_(unparsed) {} Formatter(const Formatter&) = delete; Formatter& operator=(const Formatter&) = delete; @@ -168,6 +182,10 @@ class Unparser : public ParseTreeVisitor { void visitASTTVFSchema(const ASTTVFSchema* node, void* data) override; void visitASTTVFSchemaColumn(const ASTTVFSchemaColumn* node, void* data) override; + void visitASTCreateConnectionStatement( + const ASTCreateConnectionStatement* node, void* data) override; + void visitASTAlterConnectionStatement(const ASTAlterConnectionStatement* node, + void* data) override; void visitASTCreateConstantStatement(const ASTCreateConstantStatement* node, void* data) override; void visitASTCreateDatabaseStatement(const ASTCreateDatabaseStatement* node, @@ -294,6 +312,44 @@ class Unparser : public ParseTreeVisitor { void* data) override; void visitASTWithClause(const ASTWithClause* node, void* data) override; void visitASTQuery(const ASTQuery* node, void* data) override; + void visitASTAliasedQueryExpression(const ASTAliasedQueryExpression* node, + void* data) override; + void visitASTFromQuery(const ASTFromQuery* node, void* data) override; + void visitASTPipeWhere(const ASTPipeWhere* node, void* data) override; + void visitASTPipeSelect(const ASTPipeSelect* node, void* data) override; + void visitASTPipeLimitOffset(const ASTPipeLimitOffset* node, + void* data) override; + void visitASTPipeOrderBy(const ASTPipeOrderBy* node, void* data) override; + void visitASTPipeExtend(const ASTPipeExtend* node, void* data) override; + void visitASTPipeRenameItem(const ASTPipeRenameItem* node, + void* data) override; + void visitASTPipeRename(const ASTPipeRename* node, void* data) override; + void visitASTPipeAggregate(const ASTPipeAggregate* node, void* data) override; + void visitASTPipeSetOperation(const ASTPipeSetOperation* node, + void* data) override; + void visitASTPipeJoin(const ASTPipeJoin* node, void* data) override; + void visitASTPipeJoinLhsPlaceholder(const ASTPipeJoinLhsPlaceholder* node, + void* data) override; + void visitASTPipeCall(const ASTPipeCall* node, void* data) override; + void visitASTPipeWindow(const ASTPipeWindow* node, void* data) override; + void visitASTPipeDistinct(const ASTPipeDistinct* node, void* data) override; + void visitASTPipeTablesample(const ASTPipeTablesample* node, + void* data) override; + void visitASTPipeAs(const ASTPipeAs* node, void* data) override; + void visitASTPipeStaticDescribe(const ASTPipeStaticDescribe* node, + void* data) override; + void visitASTPipeAssert(const ASTPipeAssert* node, void* data) override; + void visitASTPipeDrop(const ASTPipeDrop* node, void* data) override; + void visitASTPipeSetItem(const ASTPipeSetItem* node, void* data) override; + void visitASTPipeSet(const ASTPipeSet* node, void* data) override; + void visitASTPipePivot(const ASTPipePivot* node, void* data) override; + void visitASTPipeUnpivot(const ASTPipeUnpivot* node, void* data) override; + void visitASTMatchRecognizeClause(const ASTMatchRecognizeClause* node, + void* data) override; + void visitASTRowPatternVariable(const ASTRowPatternVariable* node, + void* data) override; + void visitASTRowPatternOperation(const ASTRowPatternOperation* node, + void* data) override; void visitASTSetOperation(const ASTSetOperation* node, void* data) override; void visitASTSelect(const ASTSelect* node, void* data) override; void visitASTSelectAs(const ASTSelectAs* node, void* data) override; @@ -348,6 +404,8 @@ class Unparser : public ParseTreeVisitor { void* data) override; void visitASTIdentityColumnMinValue(const ASTIdentityColumnMinValue* node, void* data) override; + void visitASTGroupingItemOrder(const ASTGroupingItemOrder* node, + void* data) override; void visitASTGroupingItem(const ASTGroupingItem* node, void* data) override; void visitASTGroupingSet(const ASTGroupingSet* node, void* data) override; void visitASTGroupingSetList(const ASTGroupingSetList* node, diff --git a/zetasql/public/BUILD b/zetasql/public/BUILD index c36e9214f..8225f1d5f 100644 --- a/zetasql/public/BUILD +++ b/zetasql/public/BUILD @@ -196,6 +196,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:compare", ], ) @@ -207,7 +208,9 @@ cc_test( ":civil_time", "//zetasql/base", "//zetasql/base/testing:zetasql_gtest_main", + "@com_google_absl//absl/hash:hash_testing", "@com_google_absl//absl/time", + "@com_google_absl//absl/types:compare", ], ) @@ -237,7 +240,6 @@ cc_library( ":constant", ":evaluator_table_iterator", ":function", - ":id_string", ":simple_connection_cc_proto", ":simple_constant_cc_proto", ":simple_model_cc_proto", @@ -268,7 +270,6 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_absl//absl/types:span", @@ -286,19 +287,27 @@ cc_library( ":analyzer", ":analyzer_options", ":analyzer_output", + ":catalog", + ":function", ":function_cc_proto", ":function_headers", ":simple_catalog", ":sql_function", + ":sql_tvf", ":sql_view", ":templated_sql_function", + ":templated_sql_tvf", "//zetasql/base:ret_check", "//zetasql/base:status", "//zetasql/resolved_ast", "//zetasql/resolved_ast:resolved_ast_enums_cc_proto", "//zetasql/resolved_ast:resolved_node_kind_cc_proto", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", ], ) @@ -313,15 +322,19 @@ cc_test( deps = [ ":analyzer", ":analyzer_options", + ":catalog", + ":function", ":function_headers", ":options_cc_proto", ":simple_catalog", ":simple_catalog_util", + ":value", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/public/types", "//zetasql/resolved_ast", "//zetasql/resolved_ast:resolved_node_kind_cc_proto", + "@com_google_absl//absl/status", ], ) @@ -382,6 +395,7 @@ cc_library( ":uuid_value", ":value", "//zetasql/base", + "//zetasql/base:check", "//zetasql/base:map_util", "//zetasql/base:ret_check", "//zetasql/base:status", @@ -401,7 +415,7 @@ cc_library( "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/memory", + "@com_google_absl//absl/log", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", @@ -467,6 +481,7 @@ cc_library( "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:compare", "@com_google_absl//absl/types:optional", ], ) @@ -552,6 +567,7 @@ cc_test( srcs = ["json_value_test.cc"], deps = [ ":json_value", + "//zetasql/base:check", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "@com_google_absl//absl/container:flat_hash_map", @@ -1474,6 +1490,7 @@ cc_library( ":language_options", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", ], ) @@ -1676,6 +1693,7 @@ cc_test( "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", ], ) @@ -1834,7 +1852,7 @@ cc_library( "//zetasql/parser:bison_parser_generated_lib", "//zetasql/parser:bison_parser_mode", "//zetasql/parser:keywords", - "//zetasql/parser:token_disambiguator", + "//zetasql/parser:lookahead_transformer", "//zetasql/public/functions:convert_string", "//zetasql/resolved_ast:resolved_node_kind_cc_proto", "@com_google_absl//absl/container:flat_hash_map", @@ -1951,6 +1969,7 @@ cc_library( ":options_cc_proto", "//zetasql/resolved_ast", "//zetasql/resolved_ast:target_syntax", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", ], @@ -2369,6 +2388,7 @@ cc_test( "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/parser", + "//zetasql/public:analyzer_options", "//zetasql/resolved_ast", "//zetasql/resolved_ast:resolved_node_kind_cc_proto", "@com_google_absl//absl/container:btree", diff --git a/zetasql/public/analyzer.cc b/zetasql/public/analyzer.cc index f0d078cc7..de081ec2f 100644 --- a/zetasql/public/analyzer.cc +++ b/zetasql/public/analyzer.cc @@ -42,6 +42,7 @@ #include "zetasql/parser/parser.h" #include "zetasql/public/analyzer_options.h" #include "zetasql/public/analyzer_output.h" +#include "zetasql/public/catalog.h" #include "zetasql/public/cycle_detector.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_helpers.h" @@ -56,6 +57,7 @@ #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "zetasql/base/map_util.h" #include "zetasql/base/source_location.h" #include "zetasql/base/ret_check.h" @@ -255,13 +257,13 @@ static absl::Status AnalyzeStatementHelper( ZETASQL_RET_CHECK(options.AllArenasAreInitialized()); std::unique_ptr resolved_statement; - Resolver resolver(catalog, type_factory, &options); + auto resolver = std::make_unique(catalog, type_factory, &options); absl::Status status = FinishResolveStatementImpl( - sql, ast_statement, &resolver, options, catalog, type_factory, + sql, ast_statement, resolver.get(), options, catalog, type_factory, &analyzer_runtime_info, &resolved_statement); const absl::StatusOr& type_assignments = - resolver.AssignTypesToUndeclaredParameters(); + resolver->AssignTypesToUndeclaredParameters(); internal::UpdateStatus(&status, type_assignments.status()); if (!status.ok()) { @@ -278,13 +280,13 @@ static absl::Status AnalyzeStatementHelper( *output = std::make_unique( options.id_string_pool(), options.arena(), - std::move(resolved_statement), resolver.analyzer_output_properties(), + std::move(resolved_statement), resolver->analyzer_output_properties(), std::move(owned_parser_output), ConvertInternalErrorLocationsAndAdjustErrorStrings( options.error_message_options(), sql, - resolver.deprecation_warnings()), - *type_assignments, resolver.undeclared_positional_parameters(), - resolver.max_column_id() + resolver->deprecation_warnings()), + *type_assignments, resolver->undeclared_positional_parameters(), + resolver->max_column_id() ); ZETASQL_RETURN_IF_ERROR( RewriteResolvedAstImpl(options, sql, catalog, type_factory, **output)); @@ -304,27 +306,27 @@ static absl::Status AnalyzeStatementFromParserOutputImpl( bool take_ownership_on_success, const AnalyzerOptions& options, absl::string_view sql, Catalog* catalog, TypeFactory* type_factory, std::unique_ptr* output) { - AnalyzerOptions local_options = options; + auto local_options = std::make_unique(options); // If the arena and IdStringPool are not set in , use the // arena and IdStringPool from the parser output by default. - if (local_options.arena() == nullptr) { + if (local_options->arena() == nullptr) { ZETASQL_RET_CHECK((*statement_parser_output)->arena() != nullptr); - local_options.set_arena((*statement_parser_output)->arena()); + local_options->set_arena((*statement_parser_output)->arena()); } - if (local_options.id_string_pool() == nullptr) { + if (local_options->id_string_pool() == nullptr) { ZETASQL_RET_CHECK((*statement_parser_output)->id_string_pool() != nullptr); - local_options.set_id_string_pool( + local_options->set_id_string_pool( (*statement_parser_output)->id_string_pool()); } CycleDetector owned_cycle_detector; - if (local_options.find_options().cycle_detector() == nullptr) { - local_options.mutable_find_options()->set_cycle_detector( + if (local_options->find_options().cycle_detector() == nullptr) { + local_options->mutable_find_options()->set_cycle_detector( &owned_cycle_detector); } const ASTStatement* ast_statement = (*statement_parser_output)->statement(); - return AnalyzeStatementHelper(*ast_statement, local_options, sql, catalog, + return AnalyzeStatementHelper(*ast_statement, *local_options, sql, catalog, type_factory, statement_parser_output, take_ownership_on_success, output); } diff --git a/zetasql/public/analyzer_options.cc b/zetasql/public/analyzer_options.cc index 1ff7d4275..5d55ec499 100644 --- a/zetasql/public/analyzer_options.cc +++ b/zetasql/public/analyzer_options.cc @@ -309,7 +309,8 @@ AnalyzerOptions::AnalyzerOptions(const LanguageOptions& language_options) absl::GetFlag(FLAGS_zetasql_validate_resolved_ast), .error_message_stability = GetDefaultErrorMessageStability()}) { ZETASQL_CHECK_OK(FindTimeZoneByName("America/Los_Angeles", // Crash OK - &data_->default_timezone)); + &data_->default_timezone)) + << "Did you need to install the tzdata package?"; } AnalyzerOptions::~AnalyzerOptions() = default; diff --git a/zetasql/public/analyzer_output_properties.h b/zetasql/public/analyzer_output_properties.h index 50840fe8e..545c2e48f 100644 --- a/zetasql/public/analyzer_output_properties.h +++ b/zetasql/public/analyzer_output_properties.h @@ -20,6 +20,7 @@ #include "zetasql/public/options.pb.h" #include "zetasql/resolved_ast/resolved_node.h" #include "zetasql/resolved_ast/target_syntax.h" +#include "absl/base/attributes.h" #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" @@ -27,12 +28,6 @@ namespace zetasql { class AnalyzerOutputProperties { public: - // TODO: Remove when external references drop to zero. - ABSL_DEPRECATED( - "Client code should consider this struct internal. " - "It doesn't mean what you think it means.") - bool has_flatten = false; // NOLINT - // TODO: Remove when external references drop to zero. ABSL_DEPRECATED( "Client code should consider this struct internal. " @@ -40,36 +35,30 @@ class AnalyzerOutputProperties { bool has_anonymization = false; // NOLINT // Marks the given `rewrite` as being applicable to the resolved AST. + ABSL_DEPRECATED( + "REWRITE_ANONYMIZATION is the only rewrite still checked through this " + "mechanism.") void MarkRelevant(ResolvedASTRewrite rewrite) { relevant_rewrites_.insert(rewrite); - if (rewrite == REWRITE_FLATTEN) { - has_flatten = true; - } if (rewrite == REWRITE_ANONYMIZATION) { has_anonymization = true; } } // Returns true if the rewrite was marked relevant by the resolver. + ABSL_DEPRECATED( + "REWRITE_ANONYMIZATION is the only rewrite still checked through this " + "mechanism.") bool IsRelevant(ResolvedASTRewrite rewrite) const { return relevant_rewrites_.contains(rewrite); } - // Returns the set of rewrites marked as relevant by the resolver. The - // rewriter may identify more rewrites during rewriting. - const absl::btree_set& relevant_rewrites() { - return relevant_rewrites_; - } - - void MarkTargetSyntax(ResolvedNode* key, SQLBuildTargetSyntax target_syntax) { - target_syntax_.insert({key, target_syntax}); - } - private: + // Defined in zetasql/common/internal_analyzer_output_properties.h. + friend class InternalAnalyzerOutputProperties; + absl::btree_set relevant_rewrites_; TargetSyntaxMap target_syntax_; - - friend class AnalyzerTestRunner; // To read `target_syntax_` }; } // namespace zetasql diff --git a/zetasql/public/anon_function.cc b/zetasql/public/anon_function.cc index be63ccfed..d6944bebc 100644 --- a/zetasql/public/anon_function.cc +++ b/zetasql/public/anon_function.cc @@ -26,11 +26,12 @@ #include "absl/strings/ascii.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" namespace zetasql { static std::string AnonFunctionSQL(absl::string_view display_name, - const std::vector& inputs) { + absl::Span inputs) { const std::string upper_case_display_name = absl::AsciiStrToUpper(display_name); if (upper_case_display_name == "ANON_PERCENTILE_CONT" || diff --git a/zetasql/public/builtin_function.cc b/zetasql/public/builtin_function.cc index b90923dbb..6f3224425 100644 --- a/zetasql/public/builtin_function.cc +++ b/zetasql/public/builtin_function.cc @@ -255,6 +255,10 @@ absl::Status GetBuiltinFunctionsAndTypes(const BuiltinFunctionOptions& options, ZETASQL_RETURN_IF_ERROR( GetArrayZipFunctions(&type_factory, options, &functions, &types)); } + if (options.language_options.LanguageFeatureEnabled( + FEATURE_TO_JSON_UNSUPPORTED_FIELDS)) { + ZETASQL_RETURN_IF_ERROR(GetToJsonBuiltinEnumTypes(&type_factory, options, &types)); + } ZETASQL_RETURN_IF_ERROR( GetStandaloneBuiltinEnumTypes(&type_factory, options, &types)); GetMapCoreFunctions(&type_factory, options, &functions); diff --git a/zetasql/public/builtin_function.proto b/zetasql/public/builtin_function.proto index d8c628104..ac74723bb 100644 --- a/zetasql/public/builtin_function.proto +++ b/zetasql/public/builtin_function.proto @@ -40,7 +40,7 @@ enum FunctionSignatureId { // in ranges: // // 0002-0999 Non-standard function calls (NextId: 310) - // 1000-1099 String functions (NextId: 1083) + // 1000-1099 String functions (NextId: 1084) // 1100-1199 Control flow functions (NextId: 1108) // 1200-1299 Time functions (Fully used) // 1300-1399 Math functions (Fully used) @@ -54,8 +54,8 @@ enum FunctionSignatureId { // 2200-2231 Anonymization functions (NextId: 2232) // 2300-2499 Numeric, more math & distances (NextId: 2388) // 2500-2599 Array and proto map functions (NextId: 2561) - // 2600-2699 More misc functions. (NextId: 2661) - // 2700-2799 Aggregate functions II (NextId: 2773) + // 2600-2699 More misc functions. (NextId: 2675) + // 2700-2799 Aggregate functions II (NextId: 2780) // 2800-2899 Differential Privacy functions (NextId: 2832) // 2900-2999 Range functions (NextId: 2921) // 3000-3100 Map functions (NextId: 3001) @@ -322,6 +322,8 @@ enum FunctionSignatureId { 1079; // regex_instr(string, string[, int64[, int64[, int64]]]) FN_REGEXP_INSTR_BYTES = 1080; // regex_instr(bytes, bytes[, int64[, int64[, int64]]]) + FN_SPLIT_SUBSTR = + 1083; // split_substr(string, string, int64[, int64]) -> string // Control flow functions FN_IF = 1100; // if @@ -1529,6 +1531,10 @@ enum FunctionSignatureId { FN_MAP_VALUES_SORTED = 3009; // map_values_sorted(map) -> array FN_MAP_VALUES_UNSORTED = 3010; // map_values_unsorted(map) // -> array - // map_values_sorted_by_key(map) -> array - FN_MAP_VALUES_SORTED_BY_KEY = 3011; + FN_MAP_VALUES_SORTED_BY_KEY = 3011; // map_values_sorted_by_key(map) + // -> array + FN_MAP_EMPTY = 3012; // map_empty(map) -> bool + FN_MAP_INSERT = 3013; // map_insert(map, K, V[, K, V, ...]) -> bool + // map_insert_or_replace(map, K, V[, K, V, ...]) -> bool + FN_MAP_INSERT_OR_REPLACE = 3014; } diff --git a/zetasql/public/builtin_function_test.cc b/zetasql/public/builtin_function_test.cc index 9d4436f1e..0dda5eaef 100644 --- a/zetasql/public/builtin_function_test.cc +++ b/zetasql/public/builtin_function_test.cc @@ -1248,9 +1248,18 @@ TEST(SimpleFunctionTests, TestAllReleasedFunctions) { for (const auto& [name, function] : all_functions) { all_function_names.insert(name); } + + // The primary function names are controlled by a language option, so we have + // to exclude them from the test. + constexpr auto kFunctionsToIgnore = zetasql_base::fixed_flat_set_of( + {"kll_quantiles.merge_point_double", "kll_quantiles.init_double", + "kll_quantiles.extract_point_double", "kll_quantiles.merge_double", + "kll_quantiles.extract_double"}); absl::flat_hash_set min_function_names; for (const auto& [name, function] : min_functions) { - min_function_names.insert(name); + if (!kFunctionsToIgnore.contains(name)) { + min_function_names.insert(name); + } } EXPECT_THAT(all_function_names, IsSupersetOf(min_function_names)); EXPECT_THAT(min_function_names, Not(IsSupersetOf(all_function_names))); diff --git a/zetasql/public/civil_time.h b/zetasql/public/civil_time.h index 7fe72d8c9..6e1cabe41 100644 --- a/zetasql/public/civil_time.h +++ b/zetasql/public/civil_time.h @@ -19,8 +19,10 @@ #include #include +#include #include "absl/time/civil_time.h" +#include "absl/types/compare.h" namespace zetasql { @@ -200,6 +202,27 @@ class TimeValue { // Pack the hour/minute/second/nanos into a bit field. int64_t Packed64TimeNanos() const; + // Note that TimeValues with IsValid() false will compare unequal to + // everything, including other TimeValues with IsValid() false and the exact + // same contents, which naturally have the same hash. This violates the + // expectation that is should be rare to find objects with equal hashes that + // are unequal. + template + friend H AbslHashValue(H h, const TimeValue& time_value) { + return H::combine(std::move(h), time_value.IsValid(), + time_value.Packed64TimeNanos()); + } + +#ifdef __cpp_impl_three_way_comparison + friend std::partial_ordering operator<=>(const TimeValue& lhs, + const TimeValue& rhs) { + if (!lhs.IsValid() || !rhs.IsValid()) { + return std::partial_ordering::unordered; + } + return lhs.Packed64TimeNanos() <=> rhs.Packed64TimeNanos(); + } +#endif + private: static TimeValue FromHMSAndNanosInternal(int64_t hour, int64_t minute, int64_t second, int64_t nanosecond); @@ -361,6 +384,29 @@ class DatetimeValue { return absl::CivilSecond(year_, month_, day_, hour_, minute_, second_); } + // Note that DatetimeValues with IsValid() false will compare unequal to + // everything, including other DatetimeValues with IsValid() false and the + // exact same contents, which naturally have the same hash. This violates the + // expectation that is should be rare to find objects with equal hashes that + // are unequal. + template + friend H AbslHashValue(H h, const DatetimeValue& datetime_value) { + return H::combine(std::move(h), datetime_value.IsValid(), + datetime_value.Packed64DatetimeSeconds(), + datetime_value.Nanoseconds()); + } + +#ifdef __cpp_impl_three_way_comparison + friend std::partial_ordering operator<=>(const DatetimeValue& lhs, + const DatetimeValue& rhs) { + if (!lhs.IsValid() || !rhs.IsValid()) { + return std::partial_ordering::unordered; + } + return std::make_pair(lhs.Packed64DatetimeSeconds(), lhs.Nanoseconds()) <=> + std::make_pair(rhs.Packed64DatetimeSeconds(), rhs.Nanoseconds()); + } +#endif + private: static DatetimeValue FromYMDHMSAndNanosInternal(int64_t year, int64_t month, int64_t day, int64_t hour, diff --git a/zetasql/public/civil_time_test.cc b/zetasql/public/civil_time_test.cc index 86831905c..82a360fca 100644 --- a/zetasql/public/civil_time_test.cc +++ b/zetasql/public/civil_time_test.cc @@ -23,7 +23,9 @@ #include "zetasql/base/logging.h" #include "gtest/gtest.h" +#include "absl/hash/hash_testing.h" #include "absl/time/civil_time.h" +#include "absl/types/compare.h" using zetasql::TimeValue; using zetasql::DatetimeValue; @@ -558,4 +560,72 @@ TEST(CivilTimeValuesTest, DatetimeValueNormalizationTest) { ASSERT_FALSE(datetime.IsValid()); } +TEST(CivilTimeValuesTest, HashConsistencyForDateTime) { + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + DatetimeValue(), // 1970-01-01 00:00:00 + // 0001-01-01 00:00:00 + DatetimeValue::FromYMDHMSAndMicros(1, 1, 1, 0, 0, 0, 0), + // 9999-12-31 23:59:59.999999 + DatetimeValue::FromYMDHMSAndMicros(9999, 12, 31, 23, 59, 59, 999999), + // 2006-01-02 03:04:05.654321 + DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 654321), + DatetimeValue::FromYMDHMSAndMicrosNormalized(2006, 1, 2, 3, 4, 5, 123456), + DatetimeValue::FromYMDHMSAndMicrosNormalized(10000, 0, 2, 3, 4, 5, + 123456), + // Invalid case + DatetimeValue::FromPacked64Micros(0x1EC8420F0000000), + })); +} +TEST(CivilTimeValuesTest, HashConsistencyForTime) { + EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({ + TimeValue(), + TimeValue::FromHMSAndMicros(0, 0, 0, 0), + TimeValue::FromHMSAndMicros(23, 59, 59, 999999), + TimeValue::FromHMSAndMicros(12, 34, 56, 654321), + TimeValue::FromHMSAndMicrosNormalized(12, 34, 56, 123456), + // Invalid case + TimeValue::FromHMSAndMicros(23, 59, 59, -10), + })); +} + +TEST(CivilTimeValuesTest, ComparisonOperatorsForDatetime) { +#ifndef __cpp_impl_three_way_comparison + GTEST_SKIP() << "<=> operator not supported"; +#else + DatetimeValue invalid = DatetimeValue::FromPacked64Micros(0x1EC8420F0000000); + + EXPECT_EQ(DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 654321) <=> + DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 654321), + std::partial_ordering::equivalent); + EXPECT_EQ(DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 6, 654321) <=> + DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 654321), + std::partial_ordering::greater); + EXPECT_EQ(DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 654320) <=> + DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 654321), + std::partial_ordering::less); + EXPECT_EQ(invalid <=> DatetimeValue(), std::partial_ordering::unordered); + EXPECT_EQ(DatetimeValue() <=> invalid, std::partial_ordering::unordered); +#endif +} + +TEST(CivilTimeValuesTest, ComparisonOperatorsForTime) { +#ifndef __cpp_impl_three_way_comparison + GTEST_SKIP() << "<=> operator not supported"; +#else + TimeValue invalid = TimeValue::FromHMSAndMicros(23, 59, 60, 0); + + EXPECT_EQ(TimeValue::FromHMSAndMicros(23, 59, 51, 7) <=> + TimeValue::FromHMSAndMicros(23, 59, 51, 7), + std::partial_ordering::equivalent); + EXPECT_EQ(TimeValue::FromHMSAndMicros(23, 59, 52, 7) <=> + TimeValue::FromHMSAndMicros(23, 59, 51, 7), + std::partial_ordering::greater); + EXPECT_EQ(TimeValue::FromHMSAndMicros(23, 59, 50, 7) <=> + TimeValue::FromHMSAndMicros(23, 59, 51, 7), + std::partial_ordering::less); + EXPECT_EQ(invalid <=> TimeValue(), std::partial_ordering::unordered); + EXPECT_EQ(TimeValue() <=> invalid, std::partial_ordering::unordered); +#endif +} + } // namespace diff --git a/zetasql/public/coercer.cc b/zetasql/public/coercer.cc index ca518bb46..69a4e2d7b 100644 --- a/zetasql/public/coercer.cc +++ b/zetasql/public/coercer.cc @@ -18,10 +18,8 @@ #include #include -#include #include #include -#include #include #include #include @@ -32,23 +30,24 @@ #include "zetasql/public/functions/string.h" #include "zetasql/public/input_argument_type.h" #include "zetasql/public/language_options.h" -#include "zetasql/public/numeric_value.h" #include "zetasql/public/options.pb.h" +#include "zetasql/public/signature_match_result.h" #include "zetasql/public/type.h" #include "zetasql/public/type.pb.h" #include "zetasql/public/types/type.h" #include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" #include "absl/algorithm/container.h" +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" -#include "absl/memory/memory.h" +#include "zetasql/base/check.h" +#include "absl/log/log.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/match.h" #include "absl/types/span.h" #include "zetasql/base/map_util.h" #include "zetasql/base/ret_check.h" -#include "zetasql/base/status.h" #include "zetasql/base/status_builder.h" #include "zetasql/base/status_macros.h" @@ -741,6 +740,12 @@ absl::StatusOr Coercer::GetCommonSuperTypeImpl( } for (const InputArgumentType& argument : argument_set.arguments()) { + // This method gets called somehow for non-expression arguments and + // relies on argument.type() being non-NULL, even though non-expression + // arguments shouldn't have types, and only do by accident because + // of code in the InputArgumentType factory methods. + ZETASQL_RET_CHECK(argument.type() != nullptr) << argument.DebugString(); + // There are dedicated methods for special cases. if (argument.type()->IsStruct()) { return GetCommonStructSuperType(argument_set); diff --git a/zetasql/public/deprecation_warning.proto b/zetasql/public/deprecation_warning.proto index d91f574cb..1b9087acd 100644 --- a/zetasql/public/deprecation_warning.proto +++ b/zetasql/public/deprecation_warning.proto @@ -53,6 +53,12 @@ message DeprecationWarning { // // The option is deprecated in favor of the synonym MAX_GROUPS_CONTRIBUTED. DEPRECATED_ANONYMIZATION_OPTION_KAPPA = 5; + + // Indicates that the query used PIVOT or UNPIVOT on an array scan. + // This is supposed to be an error, but some existing code just dropped the + // PIVOT/UNPIVOT. + // See b/359591559 for details. + PIVOT_OR_UNPIVOT_ON_ARRAY_SCAN = 6; } optional Kind kind = 1; diff --git a/zetasql/public/evaluator_test.cc b/zetasql/public/evaluator_test.cc index 77419fea4..79edd3793 100644 --- a/zetasql/public/evaluator_test.cc +++ b/zetasql/public/evaluator_test.cc @@ -1174,8 +1174,7 @@ TEST(EvaluatorTest, ExplainAfterPrepare) { ZETASQL_ASSERT_OK(options.AddQueryParameter("param", types::Int64Type())); ZETASQL_ASSERT_OK(options.AddExpressionColumn("col", types::Int64Type())); ZETASQL_ASSERT_OK(expr.Prepare(options)); - EXPECT_THAT(expr.ExplainAfterPrepare(), - IsOkAndHolds("RootExpr(Add($param, $col))")); + EXPECT_THAT(expr.ExplainAfterPrepare(), IsOkAndHolds("Add($param, $col)")); } TEST(EvaluatorTest, GetReferencedParametersAsProperSubset) { diff --git a/zetasql/public/formatter_options.h b/zetasql/public/formatter_options.h index 86099d389..acd294dac 100644 --- a/zetasql/public/formatter_options.h +++ b/zetasql/public/formatter_options.h @@ -41,7 +41,8 @@ class FormatterOptions { capitalize_keywords_(true), preserve_line_breaks_(false), expand_format_ranges_(false), - enforce_single_quotes_(false) {} + enforce_single_quotes_(false), + format_structured_strings_(true) {} // Creates options overwriting defaults using the given proto. Not set fields // in the proto are ignored. @@ -110,6 +111,15 @@ class FormatterOptions { } bool IsEnforcingSingleQuotes() const { return enforce_single_quotes_; } + // If true, formatter attempts to format the contents of annotated string + // literals with structured content. Supported annotations: /*sql*/. + void FormatStructuredStrings(bool format_structured_strings) { + format_structured_strings_ = format_structured_strings; + } + bool IsFormattingStructuredStrings() const { + return format_structured_strings_; + } + private: std::string new_line_type_; int line_length_limit_; @@ -120,6 +130,7 @@ class FormatterOptions { bool preserve_line_breaks_; bool expand_format_ranges_; bool enforce_single_quotes_; + bool format_structured_strings_; }; // Represents a range in the input to be formatted. The range may be in byte diff --git a/zetasql/public/function.cc b/zetasql/public/function.cc index db29c451f..c4cdcaac9 100644 --- a/zetasql/public/function.cc +++ b/zetasql/public/function.cc @@ -402,9 +402,9 @@ absl::Status Function::AddSignature(const TypeKind result_kind, return absl::OkStatus(); } -Function* Function::AddSignatureOrDie( - const TypeKind result_kind, const std::vector& input_kinds, - void* context, TypeFactory* factory) { +Function* Function::AddSignatureOrDie(const TypeKind result_kind, + absl::Span input_kinds, + void* context, TypeFactory* factory) { ZETASQL_CHECK_OK(AddSignature(result_kind, input_kinds, context, factory)); return this; } @@ -429,7 +429,7 @@ std::string Function::DebugString(bool verbose) const { FullName(), (alias_name().empty() ? "" : absl::StrCat("|", alias_name())), (function_signatures_.empty() ? "" : "\n"), - FunctionSignature::SignaturesToString(function_signatures_)); + FunctionSignature::SignaturesToString(function_signatures_, verbose)); } return FullName(); } diff --git a/zetasql/public/function.h b/zetasql/public/function.h index d2d71b991..c376beb33 100644 --- a/zetasql/public/function.h +++ b/zetasql/public/function.h @@ -178,9 +178,6 @@ class AggregateFunctionEvaluator { public: virtual ~AggregateFunctionEvaluator() = default; - // Sets an evaluation context. - virtual void SetEvaluationContext(EvaluationContext* context) {}; - // Resets the accumulation. This method will be called before any value // accumulation and between groups, and should restore any state variables // needed to keep track of the accumulated result to their initial values. @@ -786,7 +783,7 @@ class Function { // Convenience function that returns the same Function object so that // calls can be chained. Function* AddSignatureOrDie(TypeKind result_kind, - const std::vector& input_kinds, + absl::Span input_kinds, void* context, TypeFactory* factory); Mode mode() const { return mode_; } diff --git a/zetasql/public/function.proto b/zetasql/public/function.proto index 0823a5a64..079f51e09 100644 --- a/zetasql/public/function.proto +++ b/zetasql/public/function.proto @@ -308,6 +308,8 @@ message FunctionEnums { // The argument can be passed as a positional argument only. // We can add names to positional-only arguments for documentation purposes, // including error messages, but they cannot be used in SQL. + // Positional-only is the default NamedArgumentKind for ZetaSQL built-in + // functions, unless otherwise specified. POSITIONAL_ONLY = 1; // The argument can be passed as either a positional or named argument. diff --git a/zetasql/public/function_signature.cc b/zetasql/public/function_signature.cc index 9b91d8c68..ea8a1ddd6 100644 --- a/zetasql/public/function_signature.cc +++ b/zetasql/public/function_signature.cc @@ -1375,12 +1375,21 @@ std::string FunctionSignature::GetSQLDeclaration( } namespace { +inline bool IsRelatedToAny1(SignatureArgumentKind kind) { + return kind == ARG_TYPE_ANY_1 || kind == ARG_ARRAY_TYPE_ANY_1 || + kind == ARG_MAP_TYPE_ANY_1_2 || kind == ARG_RANGE_TYPE_ANY_1; +} + +inline bool IsRelatedToAny2(SignatureArgumentKind kind) { + return kind == ARG_TYPE_ANY_2 || kind == ARG_ARRAY_TYPE_ANY_2 || + kind == ARG_MAP_TYPE_ANY_1_2; +} // Returns true if `kind_1` is an ARRAY templated type of `kind_2` static inline bool TemplatedKindRelatedArrayType( const SignatureArgumentKind kind_1, const SignatureArgumentKind kind_2) { - return (kind_1 == ARG_ARRAY_TYPE_ANY_1 && kind_2 == ARG_TYPE_ANY_1) || - (kind_1 == ARG_ARRAY_TYPE_ANY_2 && kind_2 == ARG_TYPE_ANY_2) || + return (kind_1 == ARG_ARRAY_TYPE_ANY_1 && IsRelatedToAny1(kind_2)) || + (kind_1 == ARG_ARRAY_TYPE_ANY_2 && IsRelatedToAny2(kind_2)) || (kind_1 == ARG_ARRAY_TYPE_ANY_3 && kind_2 == ARG_TYPE_ANY_3) || (kind_1 == ARG_ARRAY_TYPE_ANY_4 && kind_2 == ARG_TYPE_ANY_4) || (kind_1 == ARG_ARRAY_TYPE_ANY_5 && kind_2 == ARG_TYPE_ANY_5); @@ -1396,14 +1405,14 @@ static inline bool TemplatedKindRelatedProtoMapType( // Returns true if `kind_1` is a RANGE templated type of `kind_2` static inline bool TemplatedKindRelatedRangeType( const SignatureArgumentKind kind_1, const SignatureArgumentKind kind_2) { - return (kind_1 == ARG_RANGE_TYPE_ANY_1 && kind_2 == ARG_TYPE_ANY_1); + return (kind_1 == ARG_RANGE_TYPE_ANY_1 && IsRelatedToAny1(kind_2)); } // Returns true if `kind_1` is a MAP templated type of `kind_2` static inline bool TemplatedKindRelatedMapType( const SignatureArgumentKind kind_1, const SignatureArgumentKind kind_2) { - return (kind_1 == ARG_MAP_TYPE_ANY_1_2 && kind_2 == ARG_TYPE_ANY_1) || - (kind_1 == ARG_MAP_TYPE_ANY_1_2 && kind_2 == ARG_TYPE_ANY_2); + return (kind_1 == ARG_MAP_TYPE_ANY_1_2 && IsRelatedToAny1(kind_2)) || + (kind_1 == ARG_MAP_TYPE_ANY_1_2 && IsRelatedToAny2(kind_2)); } // Returns true if `kind_1` is a templated type containing `kind_2` @@ -1422,6 +1431,9 @@ bool FunctionArgumentType::TemplatedKindIsRelated(SignatureArgumentKind kind) if (!IsTemplated()) { return false; } + if (kind_ == ARG_TYPE_ARBITRARY || kind == ARG_TYPE_ARBITRARY) { + return false; + } if (kind_ == kind) { return true; } @@ -1459,11 +1471,11 @@ absl::Status FunctionSignature::IsValid(ProductMode product_mode) const { if (result_type_.kind() == ARG_MAP_TYPE_ANY_1_2) { const bool has_arg_type_any_1 = absl::c_find_if(arguments_, [](auto& arg) { - return arg.kind() == ARG_TYPE_ANY_1; + return arg.TemplatedKindIsRelated(ARG_TYPE_ANY_1); }) != arguments_.end(); const bool has_arg_type_any_2 = absl::c_find_if(arguments_, [](auto& arg) { - return arg.kind() == ARG_TYPE_ANY_2; + return arg.TemplatedKindIsRelated(ARG_TYPE_ANY_2); }) != arguments_.end(); if (!has_arg_type_any_1 || !has_arg_type_any_2) { return MakeSqlError() @@ -1752,9 +1764,37 @@ FunctionSignature::GetArgumentsUserFacingTextWithCardinality( FunctionArgumentType::NamePrintingStyle print_style, bool print_template_details) const { std::vector argument_texts; - for (const FunctionArgumentType& argument : arguments()) { - argument_texts.push_back(argument.UserFacingNameWithCardinality( - language_options.product_mode(), print_style, print_template_details)); + + const int first_repeated = FirstRepeatedArgumentIndex(); + const int last_repeated = LastRepeatedArgumentIndex(); + std::string repeated_arg_text; + + for (int i = 0; i < arguments().size(); ++i) { + const FunctionArgumentType& argument = arguments()[i]; + + // If there are multiple repeated arguments, they are interpreted as a + // repeated tuple in the matcher, so they should be grouped together in the + // output. For example: [[T1, T2, T3], ...] + if (i >= first_repeated && i <= last_repeated) { + ABSL_DCHECK(argument.repeated()); + if (i != first_repeated) { + absl::StrAppend(&repeated_arg_text, ", "); + } + absl::StrAppend(&repeated_arg_text, + argument.UserFacingName(language_options.product_mode(), + print_template_details)); + if (i == last_repeated) { + if (first_repeated != last_repeated) { + repeated_arg_text = absl::StrCat("[", repeated_arg_text, "]"); + } + argument_texts.push_back( + absl::StrCat("[", repeated_arg_text, ", ...]")); + } + } else { + argument_texts.push_back(argument.UserFacingNameWithCardinality( + language_options.product_mode(), print_style, + print_template_details)); + } } return argument_texts; } diff --git a/zetasql/public/function_signature.h b/zetasql/public/function_signature.h index 2bf565419..234bf9ad5 100644 --- a/zetasql/public/function_signature.h +++ b/zetasql/public/function_signature.h @@ -676,6 +676,9 @@ class FunctionArgumentType { // Returns information about a lambda typed function argument. const ArgumentTypeLambda& lambda() const { ABSL_DCHECK(IsLambda()); + ABSL_DCHECK(lambda_ != nullptr) + << "FunctionArgumentType with ARG_TYPE_LAMBDA constructed directly is " + "not allowed. Use FunctionArgumentType::Lambda instead."; return *lambda_; } diff --git a/zetasql/public/function_signature_test.cc b/zetasql/public/function_signature_test.cc index 71f43c094..635309e36 100644 --- a/zetasql/public/function_signature_test.cc +++ b/zetasql/public/function_signature_test.cc @@ -1532,6 +1532,9 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { FunctionArgumentType arg_array_type_any_2_lambda = FunctionArgumentType::Lambda({type_factory.get_int64()}, ARG_ARRAY_TYPE_ANY_2); + FunctionArgumentType arg_type_arbitrary(ARG_TYPE_ARBITRARY); + + EXPECT_FALSE(arg_type_arbitrary.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); EXPECT_FALSE(arg_type_fixed.TemplatedKindIsRelated(ARG_TYPE_FIXED)); EXPECT_FALSE(arg_type_fixed.TemplatedKindIsRelated(ARG_TYPE_ANY_1)); @@ -1539,6 +1542,7 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_type_fixed.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_type_fixed.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_type_fixed.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE(arg_type_fixed.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); EXPECT_FALSE(arg_type_any_1.TemplatedKindIsRelated(ARG_TYPE_FIXED)); EXPECT_TRUE(arg_type_any_1.TemplatedKindIsRelated(ARG_TYPE_ANY_1)); @@ -1548,6 +1552,7 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_type_any_1.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_type_any_1.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_type_any_1.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE(arg_type_any_1.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); // arg_type_any_1_lambda is has the same behavior as arg_type_any_1 EXPECT_FALSE(arg_type_any_1_lambda.TemplatedKindIsRelated(ARG_TYPE_FIXED)); @@ -1560,6 +1565,8 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_type_any_1_lambda.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_type_any_1_lambda.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_type_any_1_lambda.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE( + arg_type_any_1_lambda.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); EXPECT_FALSE(arg_array_type_any_1.TemplatedKindIsRelated(ARG_TYPE_FIXED)); EXPECT_TRUE(arg_array_type_any_1.TemplatedKindIsRelated(ARG_TYPE_ANY_1)); @@ -1571,6 +1578,7 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_array_type_any_1.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_array_type_any_1.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_array_type_any_1.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE(arg_array_type_any_1.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); EXPECT_FALSE(arg_type_any_2.TemplatedKindIsRelated(ARG_TYPE_FIXED)); EXPECT_FALSE(arg_type_any_2.TemplatedKindIsRelated(ARG_TYPE_ANY_1)); @@ -1580,6 +1588,7 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_type_any_2.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_type_any_2.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_type_any_2.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE(arg_type_any_2.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); // arg_type_any_2_lambda is has the same behavior as arg_type_any_2 EXPECT_FALSE(arg_type_any_2_lambda.TemplatedKindIsRelated(ARG_TYPE_FIXED)); @@ -1592,6 +1601,8 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_type_any_2_lambda.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_type_any_2_lambda.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_type_any_2_lambda.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE( + arg_type_any_2_lambda.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); EXPECT_FALSE(arg_array_type_any_2.TemplatedKindIsRelated(ARG_TYPE_FIXED)); EXPECT_FALSE(arg_array_type_any_2.TemplatedKindIsRelated(ARG_TYPE_ANY_1)); @@ -1603,6 +1614,7 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_array_type_any_2.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_array_type_any_2.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE(arg_array_type_any_2.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE(arg_array_type_any_2.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); // arg_array_type_any_2_lambda is has the same behavior as // arg_array_type_any_2 @@ -1622,6 +1634,8 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { arg_array_type_any_2_lambda.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_FALSE( arg_array_type_any_2_lambda.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE( + arg_array_type_any_2_lambda.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); EXPECT_FALSE(arg_enum_any.TemplatedKindIsRelated(ARG_TYPE_FIXED)); EXPECT_FALSE(arg_enum_any.TemplatedKindIsRelated(ARG_TYPE_ANY_1)); @@ -1631,6 +1645,7 @@ TEST(FunctionArgumentTypeTests, TestTemplatedKindIsRelated) { EXPECT_FALSE(arg_enum_any.TemplatedKindIsRelated(ARG_PROTO_ANY)); EXPECT_FALSE(arg_enum_any.TemplatedKindIsRelated(ARG_STRUCT_ANY)); EXPECT_TRUE(arg_enum_any.TemplatedKindIsRelated(ARG_ENUM_ANY)); + EXPECT_FALSE(arg_enum_any.TemplatedKindIsRelated(ARG_TYPE_ARBITRARY)); } static void CheckConcreteArgumentType( @@ -2379,6 +2394,14 @@ INSTANTIATE_TEST_SUITE_P( {.result_type = {ARG_MAP_TYPE_ANY_1_2}, .arguments = {ARG_TYPE_ANY_2}}, {.result_type = {ARG_MAP_TYPE_ANY_1_2}, .arguments = {ARG_TYPE_ANY_1, ARG_TYPE_ANY_3}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {ARG_TYPE_ANY_1, ARG_ARRAY_TYPE_ANY_1}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, + ARG_TYPE_ANY_1)}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {FunctionArgumentType::Lambda({ARG_TYPE_ANY_2}, + ARG_TYPE_ANY_2)}}, // Templated proto map type should fail if key and value are not // present. {.result_type = {ARG_PROTO_MAP_ANY}, .arguments = {}}, @@ -2394,4 +2417,45 @@ INSTANTIATE_TEST_SUITE_P( // .arguments = {ARG_PROTO_MAP_VALUE_ANY}}, })); +struct FunctionSignatureValidTestParams { + const FunctionArgumentType result_type; + const std::vector arguments; +}; + +class FunctionSignatureValidTest + : public ::testing::TestWithParam {}; + +TEST_P(FunctionSignatureValidTest, + SignatureValidWhenTemplatedTypeIsFullyResolved) { + std::unique_ptr signature; + signature = std::make_unique( + GetParam().result_type, GetParam().arguments, /*context_id=*/-1); + + ZETASQL_EXPECT_OK(signature->IsValid(ProductMode::PRODUCT_EXTERNAL)); +} + +INSTANTIATE_TEST_SUITE_P( + FunctionSignatureTests, FunctionSignatureValidTest, + ::testing::ValuesIn({ + // Templated map type should succeed if key and value are both present + // or inferrable. + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {ARG_TYPE_ANY_1, ARG_TYPE_ANY_2}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_2}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {ARG_ARRAY_TYPE_ANY_1, ARG_ARRAY_TYPE_ANY_2}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {ARG_RANGE_TYPE_ANY_1, ARG_TYPE_ANY_2}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {ARG_RANGE_TYPE_ANY_1, ARG_ARRAY_TYPE_ANY_2}}, + {.result_type = {ARG_MAP_TYPE_ANY_1_2}, + .arguments = {FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, + ARG_TYPE_ANY_2)}}, + {.result_type = {types::BoolType()}, + .arguments = {ARG_MAP_TYPE_ANY_1_2, ARG_TYPE_ANY_2, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, + ARG_TYPE_ANY_1)}}, + })); + } // namespace zetasql diff --git a/zetasql/public/function_test.cc b/zetasql/public/function_test.cc index b08821171..13288d2dc 100644 --- a/zetasql/public/function_test.cc +++ b/zetasql/public/function_test.cc @@ -50,6 +50,7 @@ #include "zetasql/base/check.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "zetasql/base/case.h" // Note - test coverage for the 'Function' class interface is primarily @@ -390,8 +391,8 @@ class FunctionSerializationTests : public ::testing::Test { } static void ExpectEqualsIgnoringCallbacks( - const std::vector& list1, - const std::vector& list2) { + absl::Span list1, + absl::Span list2) { EXPECT_EQ(list1.size(), list2.size()); for (int i = 0; i < list1.size(); ++i) { ExpectEqualsIgnoringCallbacks(list1[i], list2[i]); diff --git a/zetasql/public/functions/BUILD b/zetasql/public/functions/BUILD index 21e08d3ee..1b17e9f19 100644 --- a/zetasql/public/functions/BUILD +++ b/zetasql/public/functions/BUILD @@ -254,6 +254,7 @@ cc_test( "//zetasql/compliance:functions_testlib", "//zetasql/public:civil_time", "//zetasql/public:value", + "//zetasql/public/types", "//zetasql/testdata:test_schema_cc_proto", "//zetasql/testing:test_function", "//zetasql/testing:test_value", @@ -262,6 +263,7 @@ cc_test( "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/random", + "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", @@ -278,6 +280,7 @@ cc_test( "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/common/testing:testing_proto_util", + "//zetasql/public/types", "//zetasql/testdata:test_schema_cc_proto", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", @@ -658,9 +661,11 @@ cc_library( ":like", ":string", ":util", + "//zetasql/base:check", "//zetasql/base:status", "//zetasql/common:utf_util", "//zetasql/public:collator_lite", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", @@ -703,7 +708,6 @@ cc_test( ":normalize_mode_cc_proto", ":string", "//zetasql/base", - "//zetasql/base:status", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/compliance:functions_testlib", @@ -711,6 +715,7 @@ cc_test( "//zetasql/public:type_cc_proto", "//zetasql/public:value", "//zetasql/testing:test_function", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", ], ) @@ -721,13 +726,15 @@ cc_test( srcs = ["string_with_collation_test.cc"], deps = [ ":string_with_collation", - "//zetasql/base:status", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/compliance:functions_testlib", "//zetasql/public:collator", # buildcleaner: keep "//zetasql/public:type_cc_proto", + "//zetasql/public:value", + "//zetasql/public/types", "//zetasql/testing:test_function", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", ], ) @@ -835,6 +842,21 @@ cc_proto_library( deps = [":array_find_mode_proto"], ) +proto_library( + name = "unsupported_fields_proto", + srcs = ["unsupported_fields.proto"], +) + +cc_proto_library( + name = "unsupported_fields_cc_proto", + deps = [":unsupported_fields_proto"], +) + +java_proto_library( + name = "unsupported_fields_java_proto", + deps = [":unsupported_fields_proto"], +) + proto_library( name = "differential_privacy_proto", srcs = ["differential_privacy.proto"], @@ -1160,6 +1182,7 @@ cc_library( hdrs = ["to_json.h"], deps = [ ":json_format", + ":unsupported_fields_cc_proto", "//zetasql/base:logging", "//zetasql/base:ret_check", "//zetasql/base:status", @@ -1175,6 +1198,7 @@ cc_library( "//zetasql/public/types", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", ], ) @@ -1184,6 +1208,7 @@ cc_test( srcs = ["to_json_test.cc"], deps = [ ":to_json", + ":unsupported_fields_cc_proto", "//zetasql/base:map_util", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", @@ -1194,6 +1219,7 @@ cc_test( "//zetasql/public:language_options", "//zetasql/public:options_cc_proto", "//zetasql/public:value", + "//zetasql/public/types", "//zetasql/testing:test_function", "//zetasql/testing:test_value", "@com_google_absl//absl/status", diff --git a/zetasql/public/functions/cast_date_time.cc b/zetasql/public/functions/cast_date_time.cc index f4550f03f..e997dd980 100644 --- a/zetasql/public/functions/cast_date_time.cc +++ b/zetasql/public/functions/cast_date_time.cc @@ -1243,9 +1243,10 @@ absl::Status ValidateDateTimeFormatElements( // We store at most 2 elements inside this map, since this is enough to // print in error message when duplicate checks fail for a category. - if (category_to_elements_map[format_element.category].size() < 2) { - category_to_elements_map[format_element.category].push_back( - &format_element); + std::vector& elements = + category_to_elements_map[format_element.category]; + if (elements.size() < 2) { + elements.push_back(&format_element); } if (type_to_element_map.contains(format_element.type)) { @@ -1346,7 +1347,7 @@ absl::Status ValidateDateTimeFormatElementsForDateType( } absl::Status ValidateDateTimeFormatElementsForTimeType( - const std::vector& format_elements) { + absl::Span format_elements) { return ValidateDateTimeFormatElements( format_elements, {FormatElementCategory::kYear, FormatElementCategory::kMonth, diff --git a/zetasql/public/functions/format_test.cc b/zetasql/public/functions/format_test.cc index ddbbee725..2240391bb 100644 --- a/zetasql/public/functions/format_test.cc +++ b/zetasql/public/functions/format_test.cc @@ -39,6 +39,7 @@ #include "absl/flags/flag.h" #include "absl/functional/bind_front.h" #include "absl/random/random.h" +#include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/status/statusor.h" #include "zetasql/base/map_util.h" diff --git a/zetasql/public/functions/string.cc b/zetasql/public/functions/string.cc index 0c329dc90..6418138cc 100644 --- a/zetasql/public/functions/string.cc +++ b/zetasql/public/functions/string.cc @@ -51,6 +51,7 @@ #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h" @@ -2375,5 +2376,73 @@ absl::Status ValidateFormat(absl::string_view format) { return absl::OkStatus(); } +absl::Status SplitSubstrWithCount(absl::string_view text, + absl::string_view delimiter, + int64_t start_index, int64_t count, + std::string* out) { + if (delimiter.empty()) { + return absl::Status(absl::StatusCode::kOutOfRange, + "Delimiter of SPLIT_SUBSTR function must be non-empty"); + } + + if (!IsWellFormedUTF8(text)) { + return absl::Status( + absl::StatusCode::kOutOfRange, + "Text input to SPLIT_SUBSTR function must be valid UTF-8"); + } + + if (!IsWellFormedUTF8(delimiter)) { + return absl::Status( + absl::StatusCode::kOutOfRange, + "Delimiter of SPLIT_SUBSTR function must be valid UTF-8"); + } + + if (count < 0) { + return absl::Status(absl::StatusCode::kOutOfRange, + "Count of SPLIT_SUBSTR function must be non-negative"); + } + + if (count == 0 || text.empty()) { + *out = ""; + return absl::OkStatus(); + } + + std::vector splits = absl::StrSplit(text, delimiter); + int64_t num_splits = splits.size(); + + // Normalize positive start_index to 0-based indexing. + if (start_index >= 1) { + start_index -= 1; + } + + // Normalize negative start_index. + if (start_index < 0 && start_index >= -num_splits) { + start_index = num_splits + start_index; + } + + // Move start_index to first split if it is in the left of the first split. + if (start_index < 0) { + start_index = 0; + } + + // Return empty string if start_index is in the right of the last split. + if (start_index >= num_splits) { + *out = ""; + return absl::OkStatus(); + } + + // Normalize count. + count = std::min(count, num_splits - start_index); + *out = absl::StrJoin(splits.begin() + start_index, + splits.begin() + start_index + count, delimiter); + return absl::OkStatus(); +} + +absl::Status SplitSubstr(absl::string_view text, absl::string_view delimiter, + int64_t start_index, std::string* out) { + return SplitSubstrWithCount(text, delimiter, start_index, + std::numeric_limits::max(), out); +} + } // namespace functions } // namespace zetasql diff --git a/zetasql/public/functions/string.h b/zetasql/public/functions/string.h index 95daf3d9f..7d41f07a4 100644 --- a/zetasql/public/functions/string.h +++ b/zetasql/public/functions/string.h @@ -51,6 +51,7 @@ #include "zetasql/public/functions/normalize_mode.pb.h" #include "absl/container/flat_hash_map.h" +#include "absl/status/status.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" @@ -563,6 +564,20 @@ typedef absl::flat_hash_map +bool EvaluateFunction(FunctionType function, Args&&... args, OutType* out, + absl::Status* status) { + if constexpr (std::is_invocable_v) { + return function(args..., out, status); + } else { + *status = function(args..., out); + return status->ok(); + } +} + template void TestFunction(FunctionType function, const QueryParamsWithResult& param, Args... args) { OutType out; absl::Status status; - EXPECT_EQ(function(args..., &out, &status), param.status().ok()); + bool evaluation_result = EvaluateFunction( + function, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); if (param.status().ok()) { EXPECT_EQ(absl::OkStatus(), status); EXPECT_TRUE(param.result().Equals(Value::Make(out))) @@ -70,7 +84,10 @@ void TestArrayFunction(FunctionType function, const QueryParamsWithResult& param, Args... args) { std::vector out; absl::Status status; - EXPECT_EQ(function(args..., &out, &status), param.status().ok()); + bool evaluation_result = + EvaluateFunction, FunctionType, Args...>( + function, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); Value array_value; if (status.ok()) { std::vector elements; @@ -94,7 +111,9 @@ void TestStringFunction(FunctionType function, const QueryParamsWithResult& param, Args... args) { OutType out; absl::Status status; - EXPECT_EQ(function(args..., &out, &status), param.status().ok()); + bool evaluation_result = EvaluateFunction( + function, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); if (param.status().ok()) { EXPECT_EQ(absl::OkStatus(), status); EXPECT_TRUE(param.result().Equals(Value::String(out))) @@ -114,7 +133,9 @@ void TestBytesFunction(FunctionType function, const QueryParamsWithResult& param, Args... args) { OutType out; absl::Status status; - EXPECT_EQ(function(args..., &out, &status), param.status().ok()); + bool evaluation_result = EvaluateFunction( + function, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); if (param.status().ok()) { EXPECT_EQ(absl::OkStatus(), status); EXPECT_TRUE(param.result().Equals(Value::Bytes(out))) @@ -1241,6 +1262,35 @@ INSTANTIATE_TEST_SUITE_P( String, BytesStringConversionTemplateTest, testing::ValuesIn(GetFunctionTestsBytesStringConversion())); +typedef testing::TestWithParam SplitSubstrTemplateTest; +TEST_P(SplitSubstrTemplateTest, Testlib) { + const FunctionTestCall& param = GetParam(); + int64_t num_params = param.params.num_params(); + + for (int i = 0; i < num_params; ++i) { + if (param.params.param(i).is_null()) { + return; + } + } + + if (num_params == 3) { + TestStringFunction(&SplitSubstr, param.params, + param.params.param(0).string_value(), + param.params.param(1).string_value(), + param.params.param(2).int64_value()); + } else { + TestStringFunction(&SplitSubstrWithCount, param.params, + param.params.param(0).string_value(), + param.params.param(1).string_value(), + param.params.param(2).int64_value(), + param.params.param(3).int64_value()); + } +} + +INSTANTIATE_TEST_SUITE_P( + String, SplitSubstrTemplateTest, + testing::ValuesIn(GetFunctionTestsSplitSubstr(/*skip_collation=*/true))); + } // anonymous namespace } // namespace functions } // namespace zetasql diff --git a/zetasql/public/functions/string_with_collation.cc b/zetasql/public/functions/string_with_collation.cc index 55128d268..55f5bd032 100644 --- a/zetasql/public/functions/string_with_collation.cc +++ b/zetasql/public/functions/string_with_collation.cc @@ -17,28 +17,34 @@ #include "zetasql/public/functions/string_with_collation.h" #include +#include #include #include #include #include -#include #include #include "zetasql/common/utf_util.h" +#include "zetasql/public/collator.h" #include "zetasql/public/functions/like.h" #include "zetasql/public/functions/string.h" #include "zetasql/public/functions/util.h" +#include "absl/base/optimization.h" +#include "zetasql/base/check.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "unicode/coleitr.h" #include "unicode/errorcode.h" #include "unicode/stsearch.h" #include "unicode/tblcoll.h" +#include "unicode/ucol.h" #include "unicode/unistr.h" #include "unicode/usearch.h" #include "unicode/ustring.h" +#include "unicode/utypes.h" #include "re2/re2.h" #include "zetasql/base/status_macros.h" @@ -105,6 +111,105 @@ absl::StatusOr IsUnicodeStringEmpty(const ZetaSqlCollator& collator, return result == UCOL_EQUAL; } +struct SplitPoint { + int start_pos; + int end_pos; +}; + +absl::Status GetSplitPoints(const ZetaSqlCollator& collator, + absl::string_view text, absl::string_view delimiter, + std::vector* out) { + out->clear(); + icu::UnicodeString unicode_str = icu::UnicodeString::fromUTF8(text); + // This cast is necessary because the StringSearch API requires a non-const + // collator. The collator is not changed by the StringSearch methods used + // below which makes the cast here okay. + icu::RuleBasedCollator* icu_collator = + const_cast(collator.GetIcuCollator()); + icu::ErrorCode icu_error; + absl::Status status; + if (delimiter.empty()) { + // Splitting on an empty delimiter produces an array of collation elements. + std::unique_ptr coll_iterator = + absl::WrapUnique( + icu_collator->createCollationElementIterator(unicode_str)); + int32_t utf16_start = 0; + int32_t utf8_start = 0; + while (coll_iterator->next(icu_error) != + icu::CollationElementIterator::NULLORDER) { + int32_t next_utf16_start = coll_iterator->getOffset(); + // Some characters (e.g. "ä") expand to multiple collation elements. + // Without this check, there will be spurious empty strings in the result. + if (utf16_start == next_utf16_start) { + continue; + } + int32_t utf8_length; + if (!GetUtf8Length(unicode_str, utf16_start, next_utf16_start, + &utf8_length, &status)) { + return status; + } else { + ZETASQL_DCHECK_OK(status); + } + + out->push_back( + {.start_pos = utf8_start, .end_pos = utf8_start + utf8_length - 1}); + utf16_start = next_utf16_start; + utf8_start += utf8_length; + } + if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( + icu_error, "Error when iterating through text", &status))) { + return status; + } + return absl::OkStatus(); + } + + icu::UnicodeString unicode_delimiter = + icu::UnicodeString::fromUTF8(delimiter); + icu::StringSearch stsearch(unicode_delimiter, unicode_str, icu_collator, + /*breakiter=*/nullptr, icu_error); + + if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( + icu_error, "Error initializing StringSearch", &status))) { + return status; + } + + int32_t utf16_pos = 0; + int32_t utf8_pos = 0; + while (true) { + int32_t utf16_match_index = stsearch.next(icu_error); + if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( + icu_error, "Error in StringSearch operation", &status))) { + return status; + } + + if (utf16_match_index == USEARCH_DONE) { + break; + } + + int32_t unmatched_string_utf8_length; + if (!GetUtf8Length(unicode_str, utf16_pos, utf16_match_index, + &unmatched_string_utf8_length, &status)) { + return status; + } + int32_t matched_string_utf8_length; + if (!GetUtf8Length(unicode_str, utf16_match_index, + utf16_match_index + stsearch.getMatchedLength(), + &matched_string_utf8_length, &status)) { + return status; + } + + out->push_back({.start_pos = utf8_pos, + .end_pos = utf8_pos + unmatched_string_utf8_length - 1}); + + utf16_pos = utf16_match_index + stsearch.getMatchedLength(); + utf8_pos += unmatched_string_utf8_length + matched_string_utf8_length; + } + + out->push_back( + {.start_pos = utf8_pos, .end_pos = static_cast(text.size()) - 1}); + return absl::OkStatus(); +} + } // anonymous namespace // REPLACE(COLLATOR, STRING, STRING, STRING) -> STRING @@ -147,55 +252,33 @@ bool ReplaceUtf8WithCollation(const ZetaSqlCollator& collator, // not necessary. Use the non-collation version of REPLACE in this case. return ReplaceUtf8(str, oldsub, newsub, out, status); } - icu::ErrorCode icu_error; - icu::UnicodeString old_sequence = icu::UnicodeString::fromUTF8(oldsub); - icu::UnicodeString original = icu::UnicodeString::fromUTF8(str); - // This cast is necessary because the StringSearch API requires a non-const - // collator. The collator is not changed by the StringSearch methods used - // below which makes the cast here okay. - icu::RuleBasedCollator* icu_collator = - const_cast(collator.GetIcuCollator()); - icu::StringSearch stsearch(old_sequence, original, icu_collator, nullptr, - icu_error); - if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( - icu_error, "Error initializing StringSearch", status))) { + + std::vector split_points; + absl::Status split_status = + GetSplitPoints(collator, str, oldsub, &split_points); + if (!split_status.ok()) { + internal::UpdateError(status, split_status.message()); return false; } - int32_t utf16_pos = 0; - int32_t utf8_pos = 0; - while (true) { - int32_t utf16_match_index = stsearch.next(icu_error); + for (int64_t i = 0; i < split_points.size(); ++i) { + bool append_newsub = i < split_points.size() - 1; + int64_t append_size = + split_points[i].end_pos - split_points[i].start_pos + 1 + + (append_newsub ? static_cast(newsub.size()) : 0); - if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( - icu_error, "Error in StringSearch operation", status))) { - return false; - } - if (utf16_match_index == USEARCH_DONE) { - break; - } - int32_t unmatched_string_utf8_length; - if (!GetUtf8Length(original, utf16_pos, utf16_match_index, - &unmatched_string_utf8_length, status)) { - return false; - } - const size_t total_append_size = - unmatched_string_utf8_length + newsub.length(); - if (out->size() + total_append_size > kMaxOutputSize) { + if (out->size() + append_size > kMaxOutputSize) { return internal::UpdateError(status, kExceededReplaceOutputSize); } - int32_t matched_string_utf8_length; - if (!GetUtf8Length(original, utf16_match_index, - utf16_match_index + stsearch.getMatchedLength(), - &matched_string_utf8_length, status)) { - return false; - } - out->append(str, utf8_pos, unmatched_string_utf8_length).append(newsub); - utf16_pos = utf16_match_index + stsearch.getMatchedLength(); - utf8_pos += unmatched_string_utf8_length + matched_string_utf8_length; + out->append( + str.substr(split_points[i].start_pos, + split_points[i].end_pos - split_points[i].start_pos + 1)); + if (append_newsub) { + out->append(newsub); + } } - out->append(str, utf8_pos); + return true; } @@ -224,79 +307,20 @@ bool SplitUtf8WithCollationImpl(const ZetaSqlCollator& collator, out->push_back(""); return true; } - icu::UnicodeString unicode_str = icu::UnicodeString::fromUTF8(str); - // This cast is necessary because the StringSearch API requires a non-const - // collator. The collator is not changed by the StringSearch methods used - // below which makes the cast here okay. - icu::RuleBasedCollator* icu_collator = - const_cast(collator.GetIcuCollator()); - icu::ErrorCode icu_error; - if (delimiter.empty()) { - // Splitting on an empty delimiter produces an array of collation elements. - std::unique_ptr coll_iterator = - absl::WrapUnique( - icu_collator->createCollationElementIterator(unicode_str)); - int32_t utf16_start = 0; - int32_t utf8_start = 0; - while (coll_iterator->next(icu_error) != - icu::CollationElementIterator::NULLORDER) { - int32_t next_utf16_start = coll_iterator->getOffset(); - // Some characters (e.g. "ä") expand to multiple collation elements. - // Without this check, there will be spurious empty strings in the result. - if (utf16_start == next_utf16_start) { - continue; - } - int32_t utf8_length; - if (!GetUtf8Length(unicode_str, utf16_start, next_utf16_start, - &utf8_length, status)) { - return false; - } - out->push_back(str.substr(utf8_start, utf8_length)); - utf16_start = next_utf16_start; - utf8_start += utf8_length; - } - if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( - icu_error, "Error when iterating through a value in SPLIT", - status))) { - return false; - } - return true; - } - icu::UnicodeString unicode_delimiter = - icu::UnicodeString::fromUTF8(delimiter); - icu::StringSearch stsearch(unicode_delimiter, unicode_str, icu_collator, - /*breakiter=*/nullptr, icu_error); - if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( - icu_error, "Error initializing StringSearch", status))) { + + std::vector split_points; + absl::Status split_status = + GetSplitPoints(collator, str, delimiter, &split_points); + if (!split_status.ok()) { + internal::UpdateError(status, split_status.message()); return false; } - int32_t utf16_pos = 0; - int32_t utf8_pos = 0; - while (true) { - int32_t utf16_match_index = stsearch.next(icu_error); - if (ABSL_PREDICT_FALSE(!MoveIcuErrorIntoStatusAndReset( - icu_error, "Error in StringSearch operation", status))) { - return false; - } - if (utf16_match_index == USEARCH_DONE) { - break; - } - int32_t unmatched_string_utf8_length; - if (!GetUtf8Length(unicode_str, utf16_pos, utf16_match_index, - &unmatched_string_utf8_length, status)) { - return false; - } - int32_t matched_string_utf8_length; - if (!GetUtf8Length(unicode_str, utf16_match_index, - utf16_match_index + stsearch.getMatchedLength(), - &matched_string_utf8_length, status)) { - return false; - } - out->push_back(str.substr(utf8_pos, unmatched_string_utf8_length)); - utf16_pos = utf16_match_index + stsearch.getMatchedLength(); - utf8_pos += unmatched_string_utf8_length + matched_string_utf8_length; + + for (const auto& split_point : split_points) { + out->push_back(str.substr(split_point.start_pos, + split_point.end_pos - split_point.start_pos + 1)); } - out->push_back(str.substr(utf8_pos)); + return true; } @@ -1054,5 +1078,75 @@ absl::StatusOr LikeUtf8WithCollationAllowUnderscore( return true; } +absl::Status SplitSubstrWithCollation(const ZetaSqlCollator& collator, + absl::string_view text, + absl::string_view delimiter, + int64_t start_index, int64_t count, + std::string* out) { + if (collator.IsBinaryComparison()) { + return SplitSubstrWithCount(text, delimiter, start_index, count, out); + } + + if (delimiter.empty()) { + return absl::Status(absl::StatusCode::kOutOfRange, + "Delimiter of SPLIT_SUBSTR function must be non-empty"); + } + + if (!IsWellFormedUTF8(text)) { + return absl::Status( + absl::StatusCode::kOutOfRange, + "Text input in SPLIT_SUBSTR function must be valid UTF-8"); + } + + if (!IsWellFormedUTF8(delimiter)) { + return absl::Status( + absl::StatusCode::kOutOfRange, + "Delimiter of SPLIT_SUBSTR function must be valid UTF-8"); + } + + if (count < 0) { + return absl::Status(absl::StatusCode::kOutOfRange, + "Count of SPLIT_SUBSTR function must be non-negative"); + } + + if (count == 0 || text.empty()) { + *out = ""; + return absl::OkStatus(); + } + + std::vector split_points; + ZETASQL_RETURN_IF_ERROR(GetSplitPoints(collator, text, delimiter, &split_points)); + + int64_t num_splits = split_points.size(); + // Normalize positive start_index to 0-based indexing. + if (start_index >= 1) { + start_index -= 1; + } + + // Normalize negative start_index. + if (start_index < 0 && start_index >= -num_splits) { + start_index = num_splits + start_index; + } + + // Move start_index to first split if it is in the left of the first split. + if (start_index < 0) { + start_index = 0; + } + + // Return empty string if start_index is in the right of the last split. + if (start_index >= num_splits) { + *out = ""; + return absl::OkStatus(); + } + + // Normalize count. + count = std::min(count, num_splits - start_index); + int start_pos = split_points[start_index].start_pos; + int end_pos = split_points[start_index + count - 1].end_pos; + + *out = text.substr(start_pos, end_pos - start_pos + 1); + return absl::OkStatus(); +} + } // namespace functions } // namespace zetasql diff --git a/zetasql/public/functions/string_with_collation.h b/zetasql/public/functions/string_with_collation.h index ec8ece04a..082b3a83d 100644 --- a/zetasql/public/functions/string_with_collation.h +++ b/zetasql/public/functions/string_with_collation.h @@ -17,10 +17,13 @@ #ifndef ZETASQL_PUBLIC_FUNCTIONS_STRING_WITH_COLLATION_H_ #define ZETASQL_PUBLIC_FUNCTIONS_STRING_WITH_COLLATION_H_ +#include #include #include #include "zetasql/public/collator.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "zetasql/base/status.h" @@ -97,6 +100,15 @@ absl::StatusOr LikeUtf8WithCollation(absl::string_view text, absl::StatusOr LikeUtf8WithCollationAllowUnderscore( absl::string_view text, absl::string_view pattern, const ZetaSqlCollator& collator); + +// Splits the `text` based on specified `delimiter` and returns the substring +// from the `start_index` split, combining `count` number of splits. +// (Proposal doc (broken link)). +absl::Status SplitSubstrWithCollation(const ZetaSqlCollator& collator, + absl::string_view text, + absl::string_view delimiter, + int64_t start_index, int64_t count, + std::string* out); } // namespace functions } // namespace zetasql diff --git a/zetasql/public/functions/string_with_collation_test.cc b/zetasql/public/functions/string_with_collation_test.cc index 0f0c14f0d..4199239c8 100644 --- a/zetasql/public/functions/string_with_collation_test.cc +++ b/zetasql/public/functions/string_with_collation_test.cc @@ -18,18 +18,26 @@ #include +#include +#include +#include +#include #include +#include +#include #include #include "zetasql/base/testing/status_matchers.h" #include "zetasql/compliance/functions_testlib.h" +#include "zetasql/public/collator.h" #include "zetasql/public/type.pb.h" +#include "zetasql/public/types/type_factory.h" +#include "zetasql/public/value.h" #include "zetasql/testing/test_function.h" #include "gtest/gtest.h" -#include "absl/strings/str_cat.h" +#include "absl/status/status.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" -#include "zetasql/base/status.h" namespace zetasql { namespace functions { @@ -338,6 +346,18 @@ std::vector LikeWithUnderscoreTestCases() { }; } +template +bool EvaluateFunction(FunctionType function, const ZetaSqlCollator& collator, + Args&&... args, OutType* out, absl::Status* status) { + if constexpr (std::is_invocable_v) { + return function(collator, args..., out, status); + } else { + *status = function(collator, args..., out); + return status->ok(); + } +} + template void TestStringFunctionWithCollation(FunctionType function, const QueryParamsWithResult& param, @@ -347,7 +367,9 @@ void TestStringFunctionWithCollation(FunctionType function, absl::Status status; ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr collator, MakeSqlCollator(collation_spec)); - EXPECT_EQ(function(*collator, args..., &out, &status), param.status().ok()); + bool evaluation_result = EvaluateFunction( + function, *collator, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); if (param.status().ok()) { EXPECT_EQ(absl::OkStatus(), status); EXPECT_TRUE(param.result().Equals(Value::String(out))) @@ -362,6 +384,18 @@ void TestStringFunctionWithCollation(FunctionType function, } } +template +std::string GetArgumentString(Arg&& arg) { + std::ostringstream os; + os << arg; + return os.str(); +} + +template +std::string GetArgumentString(First&& first, Rest&&... rest) { + return GetArgumentString(first) + ", " + GetArgumentString(rest...); +} + template void TestStringArrayFunctionWithCollation(FunctionType function, const QueryParamsWithResult& param, @@ -371,7 +405,10 @@ void TestStringArrayFunctionWithCollation(FunctionType function, absl::Status status; ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr collator, MakeSqlCollator(collation_spec)); - EXPECT_EQ(function(*collator, args..., &out, &status), param.status().ok()); + bool evaluation_result = + EvaluateFunction, FunctionType, Args...>( + function, *collator, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); if (param.status().ok()) { std::vector out_values; for (auto element : out) { @@ -382,7 +419,9 @@ void TestStringArrayFunctionWithCollation(FunctionType function, EXPECT_EQ(absl::OkStatus(), status); EXPECT_TRUE(param.result().Equals(out_array)) << "Expected: " << param.result() << "\n" - << "Actual: '" << out_array << "'\n"; + << "Actual: '" << out_array << "'\n" + << "Collation: " << collation_spec << "\n" + << "Inputs: " << GetArgumentString(args...) << "\n"; } else if (!param.status().message().empty()) { // If an error message is provided, it means we want to test the exact // message instead of a binary success/failure. @@ -400,12 +439,17 @@ void TestFunctionWithCollation(FunctionType function, absl::Status status; ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr collator, MakeSqlCollator(collation_spec)); - EXPECT_EQ(function(*collator, args..., &out, &status), param.status().ok()); + + bool evaluation_result = EvaluateFunction( + function, *collator, std::forward(args)..., &out, &status); + EXPECT_EQ(evaluation_result, param.status().ok()); if (param.status().ok()) { EXPECT_EQ(absl::OkStatus(), status); EXPECT_TRUE(param.result().Equals(Value::Make(out))) << "Expected: " << param.result() << "\n" - << "Actual: '" << out << "'\n"; + << "Actual: '" << out << "'\n" + << "Collation: " << collation_spec << "\n" + << "Inputs: " << GetArgumentString(args...) << "\n"; } else if (!param.status().message().empty()) { // If an error message is provided, it means we want to test the exact // message instead of a binary success/failure. @@ -623,6 +667,35 @@ TEST(LikeWithCollationMatchTest, UnderscoreOnlyAllowedForUndci) { "than und:ci"))); } +typedef testing::TestWithParam SplitSubstrTemplateTest; +TEST_P(SplitSubstrTemplateTest, Testlib) { + const FunctionTestCall& param = GetParam(); + int64_t num_params = param.params.num_params(); + const std::vector& args = param.params.params(); + + for (int i = 0; i < num_params; ++i) { + if (param.params.param(i).is_null()) { + return; + } + } + + if (num_params == 5) { + TestFunctionWithCollation( + &SplitSubstrWithCollation, param.params, args[0].string_value(), + args[1].string_value(), args[2].string_value(), args[3].int64_value(), + args[4].int64_value()); + } else { + TestFunctionWithCollation( + &SplitSubstrWithCollation, param.params, args[0].string_value(), + args[1].string_value(), args[2].string_value(), args[3].int64_value(), + std::numeric_limits::max()); + } +} + +INSTANTIATE_TEST_SUITE_P( + String, SplitSubstrTemplateTest, + testing::ValuesIn(GetFunctionTestsSplitSubstr(/*skip_collation=*/false))); + } // anonymous namespace } // namespace functions } // namespace zetasql diff --git a/zetasql/public/functions/to_json.cc b/zetasql/public/functions/to_json.cc index 0655cdfb1..fd3243972 100644 --- a/zetasql/public/functions/to_json.cc +++ b/zetasql/public/functions/to_json.cc @@ -29,6 +29,7 @@ #include "zetasql/common/errors.h" #include "zetasql/common/thread_stack.h" #include "zetasql/public/functions/json_format.h" +#include "zetasql/public/functions/unsupported_fields.pb.h" #include "zetasql/public/interval_value.h" #include "zetasql/public/json_value.h" #include "zetasql/public/language_options.h" @@ -40,6 +41,8 @@ #include "zetasql/public/value.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "absl/types/span.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_macros.h" @@ -80,19 +83,19 @@ template absl::StatusOr ToJsonFromNumeric( const T& value, bool stringify_wide_number, const LanguageOptions& language_options, const std::string_view type_name, - bool canonicalize_zero) { + bool canonicalize_zero, UnsupportedFields unsupported_fields) { if (!value.HasFractionalPart()) { // Check whether the value is int64_t if (value >= T(kInt64Min) && value <= T(kInt64Max)) { ZETASQL_ASSIGN_OR_RETURN(int64_t int64value, value.template To()); return ToJson(Value::Int64(int64value), stringify_wide_number, - language_options, canonicalize_zero); + language_options, canonicalize_zero, unsupported_fields); } // Check whether the value is uint64_t if (value >= T(kUint64Min) && value <= T(kUint64Max)) { ZETASQL_ASSIGN_OR_RETURN(uint64_t uint64value, value.template To()); return ToJson(Value::Uint64(uint64value), stringify_wide_number, - language_options, canonicalize_zero); + language_options, canonicalize_zero, unsupported_fields); } } // Check whether the value can be converted to double without precision loss @@ -139,7 +142,8 @@ absl::StatusOr ToJsonHelper(const Value& value, bool stringify_wide_numbers, const LanguageOptions& language_options, int current_nesting_level, - bool canonicalize_zero) { + bool canonicalize_zero, + UnsupportedFields unsupported_fields) { // Check the stack usage iff the not less than // kNestingLevelStackCheckThreshold. if (current_nesting_level >= kNestingLevelStackCheckThreshold) { @@ -180,12 +184,12 @@ absl::StatusOr ToJsonHelper(const Value& value, return ToJsonFromNumeric( value.numeric_value(), stringify_wide_numbers, language_options, value.type()->ShortTypeName(language_options.product_mode()), - canonicalize_zero); + canonicalize_zero, unsupported_fields); case TYPE_BIGNUMERIC: return ToJsonFromNumeric( value.bignumeric_value(), stringify_wide_numbers, language_options, value.type()->ShortTypeName(language_options.product_mode()), - canonicalize_zero); + canonicalize_zero, unsupported_fields); break; case TYPE_STRING: { return JSONValue(value.string_value()); @@ -254,7 +258,8 @@ absl::StatusOr ToJsonHelper(const Value& value, ZETASQL_ASSIGN_OR_RETURN( JSONValue json_member_value, ToJsonHelper(field_value, stringify_wide_numbers, language_options, - current_nesting_level + 1, canonicalize_zero)); + current_nesting_level + 1, canonicalize_zero, + unsupported_fields)); JSONValueRef member_value_ref = json_value_ref.GetMember(name); member_value_ref.Set(std::move(json_member_value)); } @@ -268,13 +273,30 @@ absl::StatusOr ToJsonHelper(const Value& value, json_value_ref.GetArrayElement(value.num_elements() - 1); int element_index = 0; for (const auto& element_value : value.elements()) { - ZETASQL_ASSIGN_OR_RETURN( - JSONValue json_element, + auto json_element = ToJsonHelper(element_value, stringify_wide_numbers, language_options, current_nesting_level + 1, - canonicalize_zero)); + canonicalize_zero, UnsupportedFields::FAIL); + if (json_element.status().code() == + absl::StatusCode::kUnimplemented) { + // The value type is not supported by TO_JSON, and we should + // handle this entire array field as a whole, e.g. for IGNORE we + // return just one `NULL`, instead of an `ARRAY(NULL, ...)`. + if (unsupported_fields == UnsupportedFields::FAIL) { + return json_element.status(); + } + if (unsupported_fields == UnsupportedFields::IGNORE) { + return JSONValue(); + } + ZETASQL_RET_CHECK_EQ(unsupported_fields, UnsupportedFields::PLACEHOLDER); + return JSONValue( + absl::StrCat("Unsupported: array of ", + element_value.type()->ShortTypeName( + language_options.product_mode()))); + } + ZETASQL_RETURN_IF_ERROR(json_element.status()); json_value_ref.GetArrayElement(element_index) - .Set(std::move(json_element)); + .Set(std::move(json_element.value())); element_index++; } } @@ -292,27 +314,39 @@ absl::StatusOr ToJsonHelper(const Value& value, JSONValueRef json_value_ref = json_value.GetRef(); json_value_ref.SetToEmptyObject(); - ZETASQL_ASSIGN_OR_RETURN( - JSONValue json_member_value, - ToJsonHelper(value.start(), stringify_wide_numbers, language_options, - current_nesting_level + 1, canonicalize_zero)); + ZETASQL_ASSIGN_OR_RETURN(JSONValue json_member_value, + ToJsonHelper(value.start(), stringify_wide_numbers, + language_options, current_nesting_level + 1, + canonicalize_zero, unsupported_fields)); JSONValueRef member_value_ref = json_value_ref.GetMember("start"); member_value_ref.Set(std::move(json_member_value)); - ZETASQL_ASSIGN_OR_RETURN( - json_member_value, - ToJsonHelper(value.end(), stringify_wide_numbers, language_options, - current_nesting_level + 1, canonicalize_zero)); + ZETASQL_ASSIGN_OR_RETURN(json_member_value, + ToJsonHelper(value.end(), stringify_wide_numbers, + language_options, current_nesting_level + 1, + canonicalize_zero, unsupported_fields)); member_value_ref = json_value_ref.GetMember("end"); member_value_ref.Set(std::move(json_member_value)); return json_value; } - default: - return ::zetasql_base::UnimplementedErrorBuilder() - << "Unsupported argument type " - << value.type()->ShortTypeName(language_options.product_mode()) - << " for TO_JSON"; + default: { + if (unsupported_fields == UnsupportedFields::FAIL) { + return ::zetasql_base::UnimplementedErrorBuilder() + << "Unsupported argument type " + << value.type()->ShortTypeName(language_options.product_mode()) + << " for TO_JSON"; + } + if (unsupported_fields == UnsupportedFields::IGNORE) { + return JSONValue(); + } + ZETASQL_RET_CHECK_EQ(unsupported_fields, UnsupportedFields::PLACEHOLDER); + // The object of unsupported type will be replaced with a json string, + // and no error will be triggered. + return JSONValue(absl::StrCat( + "Unsupported: ", + value.type()->ShortTypeName(language_options.product_mode()))); + } } } @@ -321,9 +355,11 @@ absl::StatusOr ToJsonHelper(const Value& value, absl::StatusOr ToJson(const Value& value, bool stringify_wide_numbers, const LanguageOptions& language_options, - bool canonicalize_zero) { + bool canonicalize_zero, + UnsupportedFields unsupported_fields) { return ToJsonHelper(value, stringify_wide_numbers, language_options, - /*current_nesting_level=*/0, canonicalize_zero); + /*current_nesting_level=*/0, canonicalize_zero, + unsupported_fields); } } // namespace functions diff --git a/zetasql/public/functions/to_json.h b/zetasql/public/functions/to_json.h index 368964755..ef3253c52 100644 --- a/zetasql/public/functions/to_json.h +++ b/zetasql/public/functions/to_json.h @@ -19,9 +19,11 @@ #ifndef ZETASQL_PUBLIC_FUNCTIONS_TO_JSON_H_ #define ZETASQL_PUBLIC_FUNCTIONS_TO_JSON_H_ +#include "zetasql/public/functions/unsupported_fields.pb.h" #include "zetasql/public/json_value.h" #include "zetasql/public/value.h" #include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "zetasql/base/status.h" namespace zetasql { @@ -31,21 +33,25 @@ namespace functions { // json object causing out of stack space. const int kNestingLevelStackCheckThreshold = 10; -// Constructs JSONValue from with option of -// which defines how numeric values outside of DOUBLE -// type domain are encoded in the generated JSON document. -// All non-double numerics are encoded as strings if -// is true. Otherwise, JSON number -// type is used to represent all values of ZetaSQL number types, including -// values outside of DOUBLE domain. -// If true, the sign on a signed zero is removed when converting numeric type -// to string. +// Constructs JSONValue from with options of: +// - stringify_wide_numbers: which defines how numeric values outside of +// DOUBLE type domain are encoded in the generated JSON document. +// All non-double numerics are encoded as strings if +// stringify_wide_numbers is true. Otherwise, JSON number +// type is used to represent all values of ZetaSQL number types, +// including values outside of DOUBLE domain. +// - canonicalize_zero: if true, the sign on a signed zero is removed +// when converting numeric type to string. +// - unsupported_fields: +// - FAIL (default): fail the query for any unsupported field +// - IGNORE: treat unsupported fields as non-existent +// - PLACEHOLDER: replace value with descriptive message for the type // TODO : remove canonicalize_zero flag when all // engines have rolled out this new behavior. -absl::StatusOr ToJson(const Value& value, - bool stringify_wide_numbers, - const LanguageOptions& language_options, - bool canonicalize_zero = false); +absl::StatusOr ToJson( + const Value& value, bool stringify_wide_numbers, + const LanguageOptions& language_options, bool canonicalize_zero = false, + UnsupportedFields unsupported_fields = UnsupportedFields::FAIL); } // namespace functions } // namespace zetasql diff --git a/zetasql/public/functions/to_json_test.cc b/zetasql/public/functions/to_json_test.cc index 34287a8e4..847075f29 100644 --- a/zetasql/public/functions/to_json_test.cc +++ b/zetasql/public/functions/to_json_test.cc @@ -26,9 +26,12 @@ #include "zetasql/common/internal_value.h" #include "zetasql/base/testing/status_matchers.h" #include "zetasql/compliance/functions_testlib.h" +#include "zetasql/public/functions/unsupported_fields.pb.h" #include "zetasql/public/json_value.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" +#include "zetasql/public/types/type_factory.h" +#include "zetasql/public/types/value_equality_check_options.h" #include "zetasql/public/value.h" #include "zetasql/testing/test_function.h" #include "zetasql/testing/test_value.h" @@ -37,6 +40,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/escaping.h" +#include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "zetasql/base/map_util.h" @@ -44,6 +48,9 @@ namespace zetasql { namespace functions { namespace { +constexpr UnsupportedFields kUnsupportFieldsDefault = UnsupportedFields::FAIL; +constexpr absl::StatusCode kUnimplemented = absl::StatusCode::kUnimplemented; + TEST(ToJsonTest, Compliance) { const std::vector tests = GetFunctionTestsToJson(); const QueryParamsWithResult::FeatureSet default_feature_set = { @@ -102,12 +109,61 @@ TEST(ToJsonTest, Compliance) { } } +TEST(ToJsonTest, UnsupportedFieldsArg) { + std::vector tests; + + const QueryParamsWithResult::FeatureSet default_feature_set = { + FEATURE_NAMED_ARGUMENTS, FEATURE_JSON_TYPE, + FEATURE_TO_JSON_UNSUPPORTED_FIELDS}; + + for (const FunctionTestCall& test : tests) { + if (std::any_of(test.params.params().begin(), test.params.params().end(), + [](const Value& param) { return param.is_null(); })) { + continue; + } + const Value& input_value = test.params.param(0); + const bool stringify_wide_numbers = test.params.params().size() >= 2 + ? test.params.param(1).bool_value() + : false; + UnsupportedFields unsupported_fields = + static_cast(test.params.param(2).enum_value()); + SCOPED_TRACE(absl::Substitute("$0('$1', '$2')", test.function_name, + input_value.ShortDebugString(), + stringify_wide_numbers)); + zetasql::LanguageOptions language_options; + if (test.params.results().size() == 1 && + zetasql_base::ContainsKey(test.params.results().begin()->first, + FEATURE_JSON_STRICT_NUMBER_PARSING)) { + language_options.EnableLanguageFeature( + FEATURE_JSON_STRICT_NUMBER_PARSING); + } + absl::StatusOr output = + ToJson(input_value, stringify_wide_numbers, language_options, + /*canonicalize_zero=*/true, unsupported_fields); + + const QueryParamsWithResult::Result* result = + zetasql_base::FindOrNull(test.params.results(), default_feature_set); + const Value expected_result_value = + result == nullptr ? test.params.results().begin()->second.result + : result->result; + const absl::Status expected_status = + result == nullptr ? test.params.results().begin()->second.status + : result->status; + + if (expected_status.ok()) { + EXPECT_EQ(expected_result_value, values::Json(std::move(output.value()))); + } else { + EXPECT_EQ(output.status().code(), expected_status.code()); + } + } +} + TEST(ToJsonTest, LegacyCanonicalizeZeroDouble) { zetasql::LanguageOptions language_options; - absl::StatusOr output = - ToJson(Value::Double(-0.0), /*stringify_wide_numbers=*/false, - language_options, /*canonicalize_zero=*/false); + absl::StatusOr output = ToJson( + Value::Double(-0.0), /*stringify_wide_numbers=*/false, language_options, + /*canonicalize_zero=*/false, kUnsupportFieldsDefault); ZETASQL_ASSERT_OK(output); EXPECT_EQ(values::Json(std::move(output.value())), values::Json(JSONValue(-0.0))); @@ -115,9 +171,9 @@ TEST(ToJsonTest, LegacyCanonicalizeZeroDouble) { TEST(ToJsonTest, LegacyCanonicalizeZeroFloat) { zetasql::LanguageOptions language_options; - absl::StatusOr output = - ToJson(Value::Float(-0.0f), /*stringify_wide_numbers=*/false, - language_options, /*canonicalize_zero=*/false); + absl::StatusOr output = ToJson( + Value::Float(-0.0f), /*stringify_wide_numbers=*/false, language_options, + /*canonicalize_zero=*/false, kUnsupportFieldsDefault); ZETASQL_ASSERT_OK(output); EXPECT_EQ(values::Json(std::move(output.value())), values::Json(JSONValue(-0.0))); diff --git a/zetasql/public/functions/unsupported_fields.proto b/zetasql/public/functions/unsupported_fields.proto new file mode 100644 index 000000000..41ebd68a9 --- /dev/null +++ b/zetasql/public/functions/unsupported_fields.proto @@ -0,0 +1,35 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +syntax = "proto2"; + +package zetasql.functions; + +option java_package = "com.google.zetasql.functions"; +option java_outer_classname = "ZetaSQLUnsupportedFields"; + +// Enum that defines how TO_JSON() handles fields of unsupported types. +// See (broken link):safe-to-json for details. +enum UnsupportedFields { + // Return error if unsupported type is encountered. + FAIL = 0; + + // Ignore all fields of unsupported types. + IGNORE = 1; + + // Render a human-readable string to replace the unsupported field. + PLACEHOLDER = 2; +} diff --git a/zetasql/public/input_argument_type.cc b/zetasql/public/input_argument_type.cc index 641fd2632..e4b8ff2cc 100644 --- a/zetasql/public/input_argument_type.cc +++ b/zetasql/public/input_argument_type.cc @@ -155,18 +155,35 @@ std::string InputArgumentType::UserFacingName(ProductMode product_mode) const { } std::string InputArgumentType::DebugString(bool verbose) const { - if (is_lambda()) { - return "LAMBDA"; + switch (category_) { + case kRelation: + return absl::StrCat( + "RELATION", (is_pipe_input_table_ ? "(is_pipe_input_table)" : "")); + case kModel: + return "MODEL"; + case kConnection: + return "CONNECTION"; + case kDescriptor: + return "DESCRIPTOR"; + case kLambda: + return "LAMBDA"; + case kSequence: + return "SEQUENCE"; + case kUntypedNull: + return absl::StrCat(verbose ? "untyped" : "", "NULL"); + case kUntypedEmptyArray: + return absl::StrCat(verbose ? "untyped" : "", "empty array"); + + case kTypedExpression: + case kTypedLiteral: + case kTypedParameter: + case kUntypedParameter: + // These expression types are handled below. + break; } std::string prefix; - if (is_untyped_null()) { - absl::StrAppend(&prefix, verbose ? "untyped" : "", "NULL"); - return prefix; - } else if (is_untyped_empty_array()) { - absl::StrAppend(&prefix, verbose ? "untyped" : "", "empty array"); - return prefix; - } else if (literal_value_.has_value()) { + if (literal_value_.has_value()) { if (literal_value_.value().is_null()) { absl::StrAppend(&prefix, "null "); } else if (type()->IsSimpleType()) { @@ -200,7 +217,10 @@ std::string InputArgumentType::ArgumentsToString( argument_names.remove_prefix(1); } + // Mark pipe input table arguments to try to make error messages clearer + // when a pipe input table is inserted somewhere in the argument list. absl::StrAppend(&arguments_string, (first ? "" : ", "), + argument.is_pipe_input_table() ? "pipe_input:" : "", !argument_name.empty() ? argument_name : "", !argument_name.empty() ? " => " : "", argument.UserFacingName(product_mode)); @@ -217,10 +237,11 @@ std::string InputArgumentType::ArgumentsToString( // static InputArgumentType InputArgumentType::RelationInputArgumentType( - const TVFRelation& relation_input_schema) { + const TVFRelation& relation_input_schema, bool is_pipe_input_table) { InputArgumentType type; type.category_ = kRelation; type.relation_input_schema_.reset(new TVFRelation(relation_input_schema)); + type.is_pipe_input_table_ = is_pipe_input_table; return type; } diff --git a/zetasql/public/input_argument_type.h b/zetasql/public/input_argument_type.h index 2ff134076..f2098787c 100644 --- a/zetasql/public/input_argument_type.h +++ b/zetasql/public/input_argument_type.h @@ -152,6 +152,7 @@ class InputArgumentType { bool is_connection() const { return category_ == kConnection; } bool is_lambda() const { return category_ == kLambda; } bool is_sequence() const { return category_ == kSequence; } + bool is_descriptor() const { return category_ == kDescriptor; } bool is_default_argument_value() const { return is_default_argument_value_; @@ -208,8 +209,11 @@ class InputArgumentType { // table-valued functions. For more information, see table_valued_function.h. // 'relation_input_schema' specifies the schema for the provided input // relation when function is called. + // 'is_pipe_input_table' indicates a TVF argument that came from the + // pipe input in pipe CALL. This is currently used only for error messages. static InputArgumentType RelationInputArgumentType( - const TVFRelation& relation_input_schema); + const TVFRelation& relation_input_schema, + bool is_pipe_input_table = false); // Constructor for model arguments. Only for use when analyzing // table-valued functions. 'model_arg' specifies the model object for the @@ -244,6 +248,7 @@ class InputArgumentType { ABSL_DCHECK(has_relation_input_schema()); return *relation_input_schema_; } + bool is_pipe_input_table() const { return is_pipe_input_table_; } // Determines equality/inequality of two InputArgumentTypes, considering Type // equality via Type::Equals() and whether they are literal or NULL. @@ -271,7 +276,13 @@ class InputArgumentType { : category_(category), type_(type) {} Category category_ = kUntypedNull; + + // Note: type_ is filled in by use of the default constructor under + // factory methods for categories that shouldn't have types. + // This would ideally be fixed but some function resolving code + // currently looks at types and fails if they aren't present. const Type* type_ = nullptr; + std::optional literal_value_; // only set for kTypedLiteral. // True if this InputArgumentType was constructed from a default function @@ -290,6 +301,10 @@ class InputArgumentType { // need for one TVFRelation instance to exist. std::shared_ptr relation_input_schema_; + // Indicates a TVF argument that came from the pipe input in pipe CALL. + // This is currently used only for error messages. + bool is_pipe_input_table_ = false; + // This is only non-NULL for table-valued functions. It holds the model // argument. This is a shared pointer only because the InputArgumentType is // copyable and there is only need for one TVFModelArgument instance to exist. diff --git a/zetasql/public/input_argument_type_test.cc b/zetasql/public/input_argument_type_test.cc index 2f3ba948d..e1a70c779 100644 --- a/zetasql/public/input_argument_type_test.cc +++ b/zetasql/public/input_argument_type_test.cc @@ -23,6 +23,7 @@ #include "zetasql/base/testing/status_matchers.h" #include "zetasql/public/id_string.h" +#include "zetasql/public/table_valued_function.h" #include "zetasql/public/type.h" #include "zetasql/testdata/test_schema.pb.h" #include "gmock/gmock.h" @@ -267,5 +268,33 @@ TEST(InputArgumentTypeTest, ArgumentAlias) { EXPECT_THAT(input_argument_type.argument_alias(), Optional(alias)); } +TEST(InputArgumentTypeTest, Relation) { + TVFRelation relation(/*columns=*/{}); + InputArgumentType arg = + InputArgumentType::RelationInputArgumentType(relation); + EXPECT_TRUE(arg.is_relation()); + EXPECT_TRUE(arg.has_relation_input_schema()); + EXPECT_EQ(arg.relation_input_schema(), relation); + EXPECT_FALSE(arg.is_pipe_input_table()); + EXPECT_EQ(arg.DebugString(), "RELATION"); + EXPECT_EQ(arg.UserFacingName(PRODUCT_EXTERNAL), "TABLE<>"); + + InputArgumentType arg2 = InputArgumentType::RelationInputArgumentType( + relation, /*is_pipe_input_table=*/true); + EXPECT_TRUE(arg2.is_relation()); + EXPECT_TRUE(arg2.has_relation_input_schema()); + EXPECT_EQ(arg2.relation_input_schema(), relation); + EXPECT_TRUE(arg2.is_pipe_input_table()); + EXPECT_EQ(arg2.DebugString(), "RELATION(is_pipe_input_table)"); + EXPECT_EQ(arg2.UserFacingName(PRODUCT_EXTERNAL), "TABLE<>"); + + EXPECT_EQ(InputArgumentType::ArgumentsToString({arg, arg2}, PRODUCT_EXTERNAL, + {"", ""}), + "TABLE<>, pipe_input:TABLE<>"); + EXPECT_EQ(InputArgumentType::ArgumentsToString({arg, arg2}, PRODUCT_EXTERNAL, + {"n1", "n2"}), + "n1 => TABLE<>, pipe_input:n2 => TABLE<>"); +} + } // namespace } // namespace zetasql diff --git a/zetasql/public/json_value.cc b/zetasql/public/json_value.cc index 08e44b5e4..b79910675 100644 --- a/zetasql/public/json_value.cc +++ b/zetasql/public/json_value.cc @@ -551,6 +551,10 @@ std::string JSONValueConstRef::GetString() const { return impl_->value.get(); } +const std::string& JSONValueConstRef::GetStringRef() const { + return impl_->value.get_ref(); +} + bool JSONValueConstRef::GetBoolean() const { return impl_->value.get(); } size_t JSONValueConstRef::GetObjectSize() const { diff --git a/zetasql/public/json_value.h b/zetasql/public/json_value.h index bdc61ee48..6330e3176 100644 --- a/zetasql/public/json_value.h +++ b/zetasql/public/json_value.h @@ -173,6 +173,11 @@ class JSONValueConstRef { // Returns a JSON string value. // Requires IsString() to be true. Otherwise, the call results in ABSL_LOG(FATAL). std::string GetString() const; + // Returns a JSON string value. + // Requires IsString() to be true. Otherwise, the call results in ABSL_LOG(FATAL). + // This function returns a const ref, avoiding a copy compared to the function + // above. The underlying JSONValue must outlive the returned reference. + const std::string& GetStringRef() const; // Returns a JSON boolean value. // Requires IsBoolean() to be true. Otherwise, the call results in ABSL_LOG(FATAL). bool GetBoolean() const; diff --git a/zetasql/public/json_value_test.cc b/zetasql/public/json_value_test.cc index e7c15007a..3656c1185 100644 --- a/zetasql/public/json_value_test.cc +++ b/zetasql/public/json_value_test.cc @@ -16,13 +16,11 @@ #include "zetasql/public/json_value.h" -#include #include #include #include #include -#include #include #include #include @@ -34,8 +32,10 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/flat_hash_map.h" +#include "zetasql/base/check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "absl/types/span.h" @@ -185,6 +185,7 @@ TEST(JSONValueTest, StringValue) { JSONValue value(kStringValue); ASSERT_TRUE(value.GetConstRef().IsString()); EXPECT_EQ(kStringValue, value.GetConstRef().GetString()); + EXPECT_EQ(kStringValue, value.GetConstRef().GetStringRef()); } { @@ -201,6 +202,7 @@ TEST(JSONValueTest, StringValue) { EXPECT_FALSE(ref.IsNumber()); EXPECT_EQ(kStringValue, ref.GetString()); + EXPECT_EQ(kStringValue, ref.GetStringRef()); EXPECT_DEATH(ref.GetBoolean(), "type must be boolean, but is string"); } } diff --git a/zetasql/public/language_options.cc b/zetasql/public/language_options.cc index f2c30ff24..5c5f72c77 100644 --- a/zetasql/public/language_options.cc +++ b/zetasql/public/language_options.cc @@ -77,6 +77,8 @@ LanguageOptions::GetLanguageFeaturesForVersion(LanguageVersion version) { features.insert(FEATURE_V_1_4_JSON_ARRAY_VALUE_EXTRACTION_FUNCTIONS); features.insert(FEATURE_V_1_4_JSON_MORE_VALUE_EXTRACTION_FUNCTIONS); features.insert(FEATURE_V_1_4_CREATE_FUNCTION_LANGUAGE_WITH_CONNECTION); + features.insert(FEATURE_V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS); + features.insert(FEATURE_V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS); ABSL_FALLTHROUGH_INTENDED; case VERSION_1_3: // NO CHANGES SHOULD HAPPEN INSIDE THE VERSIONS BELOW, which are @@ -270,7 +272,7 @@ void LanguageOptions::EnableMaximumLanguageFeatures(bool for_development) { const LanguageOptions::KeywordSet& LanguageOptions::GetReservableKeywords() { static auto* reservable_keywords = new KeywordSet{ - "QUALIFY", + "QUALIFY", "MATCH_RECOGNIZE", }; return *reservable_keywords; } diff --git a/zetasql/public/numeric_value.h b/zetasql/public/numeric_value.h index 49cdac395..73bcfe04b 100644 --- a/zetasql/public/numeric_value.h +++ b/zetasql/public/numeric_value.h @@ -36,6 +36,7 @@ #include "absl/base/port.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "absl/types/compare.h" #include "absl/types/optional.h" #include "zetasql/base/status_builder.h" @@ -165,7 +166,9 @@ class NumericValue final { bool operator>(NumericValue rh) const; bool operator>=(NumericValue rh) const; bool operator<=(NumericValue rh) const; - +#ifdef __cpp_impl_three_way_comparison + std::strong_ordering operator<=>(NumericValue rh) const; +#endif // Math functions. NumericValue Negate() const; NumericValue Abs() const; @@ -670,7 +673,9 @@ class BigNumericValue final { bool operator>(const BigNumericValue& rh) const; bool operator>=(const BigNumericValue& rh) const; bool operator<=(const BigNumericValue& rh) const; - +#ifdef __cpp_impl_three_way_comparison + std::strong_ordering operator<=>(const BigNumericValue& rh) const; +#endif // Math functions. absl::StatusOr Negate() const; absl::StatusOr Abs() const; @@ -1168,6 +1173,12 @@ inline bool NumericValue::operator<=(NumericValue rh) const { return as_packed_int() <= rh.as_packed_int(); } +#ifdef __cpp_impl_three_way_comparison +inline std::strong_ordering NumericValue::operator<=>(NumericValue rh) const { + return as_packed_int() <=> rh.as_packed_int(); +} +#endif // __cpp_impl_three_way_comparison + inline std::string NumericValue::ToString() const { std::string result; AppendToString(&result); @@ -1358,6 +1369,13 @@ inline bool BigNumericValue::operator<=(const BigNumericValue& rh) const { return value_ <= rh.value_; } +#ifdef __cpp_impl_three_way_comparison +inline std::strong_ordering BigNumericValue::operator<=>( + const BigNumericValue& rh) const { + return value_ <=> rh.value_; +} +#endif + inline absl::StatusOr BigNumericValue::Negate() const { FixedInt<64, 4> result = value_; if (ABSL_PREDICT_TRUE(!result.NegateOverflow())) { diff --git a/zetasql/public/numeric_value_test.cc b/zetasql/public/numeric_value_test.cc index 715599123..6e4cf1169 100644 --- a/zetasql/public/numeric_value_test.cc +++ b/zetasql/public/numeric_value_test.cc @@ -1181,6 +1181,9 @@ void TestComparisonOperators( EXPECT_EQ(values[i] > values[j], i > j); EXPECT_EQ(values[i] <= values[j], i <= j); EXPECT_EQ(values[i] >= values[j], i >= j); +#ifdef __cpp_impl_three_way_comparison + EXPECT_EQ(values[i] <=> values[j], i <=> j); +#endif } } } diff --git a/zetasql/public/options.proto b/zetasql/public/options.proto index ea0046cfe..4e6d27788 100644 --- a/zetasql/public/options.proto +++ b/zetasql/public/options.proto @@ -78,6 +78,7 @@ extend google.protobuf.EnumValueOptions { // // LanguageOptions::EnableMaximumLanguageFeaturesForDevelopment() enables all // features with 'ideally_enabled == true'. +// Next id: 14065 message LanguageFeatureOptions { // Indicates whether a feature is enabled in the idealized ZetaSQL. (One // reason to disable a feature is if it exists only to support backwards @@ -108,8 +109,8 @@ message LanguageFeatureOptions { // All optional features are off by default. Some features have a negative // meaning, so turning them on will remove a feature or enable an error. enum LanguageFeature { - reserved 36 to 39, 44 to 46, 48, 55, 74, 99, 110, 13046, 14001, 14024, 999003, - 999004; + reserved 36 to 39, 44 to 46, 48, 55, 74, 99, 108, 110, 13046, 14001, 14024, + 999003, 999004; // CROSS-VERSION FEATURES // @@ -643,6 +644,20 @@ enum LanguageFeature { (language_feature_options).ideally_enabled = false ]; + // Enables piped query syntax. See (broken link). + FEATURE_PIPES = 101; + + // Enables STATIC_DESCRIBE pipe operator. + // This is separate from FEATURE_PIPES because this operator creates a new + // resolved AST node, which may require engine support. + // So far, this is used primarily internally, for analyzer tests. + FEATURE_PIPE_STATIC_DESCRIBE = 112; + + // Enables ASSERT pipe operator. + // This is separate from FEATURE_PIPES because this operator creates a new + // resolved AST node, which may require engine support. + FEATURE_PIPE_ASSERT = 113; + // Enables the ability to specify generated columns as populated by default. // Example: // CREATE TABLE t (a GENERATED BY DEFAULT AS 1); @@ -669,14 +684,6 @@ enum LanguageFeature { FEATURE_DISABLE_TEXTMAPPER_PARSER = 107 [(language_feature_options).ideally_enabled = false]; - // Prevents the PIVOT rewriter from producing errors when used in conjunction - // with functions whose NULL handling behavior is unknown, where the PIVOT - // rewriter cannot guarantee that the query returns correct results. Enabling - // this feature may allow queries which produce wrong results. - // Context: b/307602611 - FEATURE_DISABLE_PIVOT_REWRITER_UDA_ERRORS = 108 - [(language_feature_options).ideally_enabled = false]; - // Enables support for IDENTITY columns in CREATE TABLE. // Example: // CREATE TABLE t ( @@ -699,6 +706,18 @@ enum LanguageFeature { // Background: b/259962379. FEATURE_TEMPLATED_SQL_FUNCTION_RESOLVE_WITH_TYPED_ARGS = 114; + // Enables support for PARTITION BY in CREATE INDEX. + // Example: + // CREATE VECTOR INDEX index ON t(database) PARTITION BY date_col; + // See (broken link) for more details. + FEATURE_CREATE_INDEX_PARTITION_BY = 117 + [(language_feature_options).in_development = true]; + + // Enables TO_JSON(unsupported_fields=>@p). + // See (broken link):safe-to-json for more details. + FEATURE_TO_JSON_UNSUPPORTED_FIELDS = 119 + [(language_feature_options).in_development = true]; + // -> Add more cross-version features here. // -> DO NOT add more versioned features into versions that are frozen. // New features should be added for the *next* version number. @@ -1339,6 +1358,19 @@ enum LanguageFeature { // REPLACE_FIELDS(REPLACE_FIELDS(x, a as oneof_a), b as oneof_b). FEATURE_V_1_4_REPLACE_FIELDS_ALLOW_MULTI_ONEOF = 14060; + // Register KLL_QUANTILES functions with _FLOAT64 suffixed function names as + // primary and _DOUBLE suffixed function names as alias in internal mode. This + // is to support engines which does not support DOUBLE as datatype like BQ, + // Spanner. + FEATURE_V_1_4_KLL_FLOAT64_PRIMARY_WITH_DOUBLE_ALIAS = 14064; + + // Disallows PIVOT and UNPIVOT on array scans. + // This should always be the case but as per b/359591559, existing code used + // to just ignore PIVOT/UNPIVOT. When this feature is disabled, only a warning + // is produced. + // TODO: remove this flag and always generate an error. + FEATURE_V_1_4_DISALLOW_PIVOT_AND_UNPIVOT_ON_ARRAY_SCANS = 14066; + // EXPERIMENTAL FEATURES - // Do not add features in this section. Use the in_development annotation // instead. @@ -1382,7 +1414,7 @@ message ResolvedASTRewriteOptions { // We support these rewrites to allow syntactic improvements which generate new // node types to be quickly and easily available to engines without needing each // engine to implement support for the new node types. -// Next ID: 28 +// Next ID: 29 enum ResolvedASTRewrite { reserved 13, 15, 21; @@ -1429,10 +1461,7 @@ enum ResolvedASTRewrite { // * Argument references inside WITH subqueries will result in an // kUnimplemented error. // TODO Fix this case when releasing TVF rewrites. - REWRITE_INLINE_SQL_FUNCTIONS = 10 [ - (rewrite_options).default_enabled = false, - (rewrite_options).in_development = true - ]; + REWRITE_INLINE_SQL_FUNCTIONS = 10 [(rewrite_options).default_enabled = false]; // Inline SQL TVFs (Table Valued Functions). // Experimental, functionality may change before generally available. @@ -1508,10 +1537,7 @@ enum ResolvedASTRewrite { // implement the function logic. Aggregate functions add expressions to // `ResolvedAggregateScan` and potentially cause the addition of a // `ResolvedProjectScan` both before and after the `ResolvedAggregateScan`. - REWRITE_INLINE_SQL_UDAS = 22 [ - (rewrite_options).default_enabled = false, - (rewrite_options).in_development = true - ]; + REWRITE_INLINE_SQL_UDAS = 22 [(rewrite_options).default_enabled = false]; // Rewrites rollup and cube to grouping sets, more specifically rewrites // ResolvedRollup and ResolvedCube to ResolvedGroupingSet. This rewriter is @@ -1533,6 +1559,21 @@ enum ResolvedASTRewrite { // See (broken link). REWRITE_AGGREGATION_THRESHOLD = 26 [(rewrite_options).default_enabled = false]; + + // Supports rewriting ResolvedPipeAssertScan. + // See (broken link). + REWRITE_PIPE_ASSERT = 27 [ + (rewrite_options).default_enabled = false, + (rewrite_options).in_development = true + ]; + + // Rewrites all builtin zetasql aggregates (ARRAY_AGG, STRING_AGG, and + // ARRAY_CONCAT_AGG) with an ORDER BY or LIMIT clause into an ARRAY_AGG + // without an ORDER BY or LIMIT clause and an ARRAY subquery. This rewriter is + // only useful if the engine doesn't support the V_1_1_ORDER_BY_IN_AGGREGATE + // and V_1_1_LIMIT_IN_AGGREGATE features. + REWRITE_ORDER_BY_AND_LIMIT_IN_AGGREGATE = 28 + [(rewrite_options).default_enabled = false]; } // ZetaSQL rewriter options. diff --git a/zetasql/public/parse_helpers.cc b/zetasql/public/parse_helpers.cc index e60f9a19d..973d3533b 100644 --- a/zetasql/public/parse_helpers.cc +++ b/zetasql/public/parse_helpers.cc @@ -31,7 +31,10 @@ #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_resume_location.h" #include "zetasql/resolved_ast/resolved_node_kind.pb.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "zetasql/base/ret_check.h" namespace zetasql { @@ -84,6 +87,8 @@ ResolvedNodeKind GetStatementKind(ASTNodeKind node_kind) { return RESOLVED_CALL_STMT; case AST_CREATE_CONSTANT_STATEMENT: return RESOLVED_CREATE_CONSTANT_STMT; + case AST_CREATE_CONNECTION_STATEMENT: + return RESOLVED_CREATE_CONNECTION_STMT; case AST_CREATE_DATABASE_STATEMENT: return RESOLVED_CREATE_DATABASE_STMT; case AST_CREATE_FUNCTION_STATEMENT: @@ -173,6 +178,8 @@ ResolvedNodeKind GetStatementKind(ASTNodeKind node_kind) { return RESOLVED_GRANT_STMT; case AST_REVOKE_STATEMENT: return RESOLVED_REVOKE_STMT; + case AST_ALTER_CONNECTION_STATEMENT: + return RESOLVED_ALTER_CONNECTION_STMT; case AST_ALTER_DATABASE_STATEMENT: return RESOLVED_ALTER_DATABASE_STMT; case AST_ALTER_SCHEMA_STATEMENT: @@ -276,6 +283,7 @@ absl::Status GetNextStatementProperties( statement_properties->statement_category = StatementProperties::SELECT; break; case AST_ALTER_ALL_ROW_ACCESS_POLICIES_STATEMENT: + case AST_ALTER_CONNECTION_STATEMENT: case AST_ALTER_DATABASE_STATEMENT: case AST_ALTER_ENTITY_STATEMENT: case AST_ALTER_MATERIALIZED_VIEW_STATEMENT: @@ -293,6 +301,7 @@ absl::Status GetNextStatementProperties( case AST_CREATE_EXTERNAL_TABLE_STATEMENT: case AST_CREATE_FUNCTION_STATEMENT: case AST_CREATE_INDEX_STATEMENT: + case AST_CREATE_CONNECTION_STATEMENT: case AST_CREATE_MATERIALIZED_VIEW_STATEMENT: case AST_CREATE_APPROX_VIEW_STATEMENT: case AST_CREATE_MODEL_STATEMENT: @@ -388,4 +397,38 @@ absl::Status GetNextStatementProperties( return absl::OkStatus(); } +absl::StatusOr> GetTopLevelTableNameFromDDLStatement( + absl::string_view sql, const LanguageOptions& language_options) { + bool unused_at_end_of_input; + ParseResumeLocation resume_location = + ParseResumeLocation::FromStringView(sql); + return GetTopLevelTableNameFromNextDDLStatement( + sql, resume_location, &unused_at_end_of_input, language_options); +} + +absl::StatusOr> +GetTopLevelTableNameFromNextDDLStatement( + absl::string_view sql, ParseResumeLocation& resume_location, + bool* at_end_of_input, const LanguageOptions& language_options) { + std::unique_ptr parser_output; + ZETASQL_RETURN_IF_ERROR(ParseNextStatement(&resume_location, + ParserOptions(language_options), + &parser_output, at_end_of_input)); + ZETASQL_RET_CHECK(parser_output != nullptr); + ZETASQL_RET_CHECK(parser_output->statement() != nullptr); + const ASTStatement* statement = parser_output->statement(); + switch (statement->node_kind()) { + case AST_CREATE_TABLE_STATEMENT: + return statement->GetAsOrDie() + ->name() + ->ToIdentifierVector(); + default: + return zetasql_base::UnimplementedErrorBuilder() + << "Unsupported AST node type in " + "GetTopLevelTableNameFromNextDDLStatement: " + << ASTNode::NodeKindToString(statement->node_kind()); + break; + } +} + } // namespace zetasql diff --git a/zetasql/public/parse_helpers.h b/zetasql/public/parse_helpers.h index ccfa45e86..823ab571e 100644 --- a/zetasql/public/parse_helpers.h +++ b/zetasql/public/parse_helpers.h @@ -136,6 +136,23 @@ absl::Status GetNextStatementProperties( const LanguageOptions& language_options, StatementProperties* statement_properties); +// Returns the target table name of a DDL statement as an identifier path. +// +// `language_options` impacts parsing behavior. Returns an error if the +// statement does not parse or is not a supported DDL statement kind. +// +// Currently the only supported DDL statement kind is CREATE TABLE. +absl::StatusOr > GetTopLevelTableNameFromDDLStatement( + absl::string_view sql, const LanguageOptions& language_options); + +// Same as GetTopLevelTableNameFromDDLStatement, but determines the top level +// table name for the next statement starting from `resume_location`. +// If successful, updates both `at_end_of_input` and `resume_location`. +absl::StatusOr > +GetTopLevelTableNameFromNextDDLStatement( + absl::string_view sql, ParseResumeLocation& resume_location, + bool* at_end_of_input, const LanguageOptions& language_options); + } // namespace zetasql #endif // ZETASQL_PUBLIC_PARSE_HELPERS_H_ diff --git a/zetasql/public/parse_helpers_test.cc b/zetasql/public/parse_helpers_test.cc index d075df5e8..ef4e45bed 100644 --- a/zetasql/public/parse_helpers_test.cc +++ b/zetasql/public/parse_helpers_test.cc @@ -22,14 +22,18 @@ #include "zetasql/common/status_payload_utils.h" #include "zetasql/base/testing/status_matchers.h" +#include "zetasql/public/language_options.h" #include "zetasql/public/parse_resume_location.h" +#include "zetasql/public/strings.h" #include "zetasql/resolved_ast/resolved_node_kind.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/container/btree_map.h" +#include "absl/status/statusor.h" #include "absl/strings/match.h" #include "zetasql/base/status.h" +using ::testing::ContainerEq; using ::testing::HasSubstr; using ::zetasql_base::testing::StatusIs; @@ -524,4 +528,151 @@ TEST(GetNextStatementKindTest, DefineMacroStmt) { &statement_properties)); } +struct GetTopLevelTableNameFromDDLStatementTestCase { + // The SQL string to test + std::string sql; + + // The expected top level table name to be extracted from as an + // identifier path string. This is only set if the test case is expected to + // succeed. + std::string table_name; + + // A substring of the expected error message. This is only set if the test + // case is expected to fail. + std::string error_message; +}; + +TEST(GetTopLevelTableNameFromDDLStatementTest, BasicStatements) { + // Success test cases + std::vector success_test_cases = + { + {"CREATE TABLE apple AS SELECT 1", "apple", ""}, + {"CREATE TEMP TABLE BANANA (A INT64);", "BANANA", ""}, + {"CREATE TABLE pear AS SELECT 1", "pear", ""}, + {"CREATE TEMP TABLE Mango.Peach (A INT64);", "Mango.Peach", ""}, + {"CREATE TABLE `🍝`.foo.bar AS SELECT 1;", "`🍝`.foo.bar", ""}, + }; + for (const GetTopLevelTableNameFromDDLStatementTestCase& test_case : + success_test_cases) { + ParseResumeLocation parse_resume_location = + ParseResumeLocation::FromString(test_case.sql); + ZETASQL_ASSERT_OK_AND_ASSIGN( + std::vector table_name, + GetTopLevelTableNameFromDDLStatement(test_case.sql, LanguageOptions())); + EXPECT_EQ(IdentifierPathToString(table_name), test_case.table_name); + } + + // Failure test cases + std::vector failure_test_cases = + { + {"CREATE TEMP FUNCTION foo() RETURNS INT64 AS (1);", "", + "Unsupported AST node type"}, + {"SELECT * FROM FRUITS;", "", "Unsupported AST node type"}, + {"CREATE TABLE CREATE;", "", "Syntax error"}, + {"CREATE TABLE my.table.path AS SELECT SELECT SELECT", "", + "Syntax error"}, + }; + for (const GetTopLevelTableNameFromDDLStatementTestCase& test_case : + failure_test_cases) { + EXPECT_THAT( + GetTopLevelTableNameFromDDLStatement(test_case.sql, LanguageOptions()) + .status() + .message(), + HasSubstr(test_case.error_message)); + } +} + +TEST(GetTopLevelTableNameFromDDLStatementTest, MultiStatementsTest) { + { + // Test that loops through all the statements in a multi-statement string + // to collect all the top level DDL table names. + LanguageOptions language_options; + std::string sql = + "CREATE TABLE Foo AS (SELECT 1 AS number UNION ALL SELECT 2 AS " + "number);CREATE TABLE Bar AS (SELECT number * 2 AS doubled_number FROM " + "Foo);CREATE TABLE Baz AS (SELECT Foo.number, Bar.doubled_number, " + "Foo.number * Bar.doubled_number AS product FROM Foo INNER JOIN Bar);"; + ParseResumeLocation parse_resume_location = + ParseResumeLocation::FromString(sql); + bool at_end_of_input = false; + int statement_count = 0; + std::vector table_names; + while (!at_end_of_input) { + ZETASQL_ASSERT_OK_AND_ASSIGN( + std::vector table_name, + GetTopLevelTableNameFromNextDDLStatement( + sql, parse_resume_location, &at_end_of_input, language_options)); + table_names.push_back(IdentifierPathToString(table_name)); + statement_count++; + } + EXPECT_EQ(statement_count, 3); + EXPECT_THAT(table_names, + ContainerEq(std::vector({"Foo", "Bar", "Baz"}))); + } + { + // Error: Only CREATE TABLE is supported, but 2nd statement is a query. + LanguageOptions language_options; + std::string sql = + "CREATE TABLE Foo AS (SELECT 1 AS number UNION ALL SELECT 2 AS " + "number);SELECT * FROM Foo;CREATE TABLE Baz AS (SELECT Foo.number, " + "Bar.doubled_number, " + "Foo.number * Bar.doubled_number AS product FROM Foo INNER JOIN Bar);"; + ParseResumeLocation parse_resume_location = + ParseResumeLocation::FromString(sql); + bool at_end_of_input = false; + int statement_count = 0; + std::vector table_names; + absl::StatusOr> table_name_or_status; + while (!at_end_of_input) { + table_name_or_status = GetTopLevelTableNameFromNextDDLStatement( + sql, parse_resume_location, &at_end_of_input, language_options); + if (table_name_or_status.ok()) { + table_names.push_back( + IdentifierPathToString(table_name_or_status.value())); + statement_count++; + } else { + at_end_of_input = true; + } + } + EXPECT_EQ(statement_count, 1); + EXPECT_THAT(table_names, ContainerEq(std::vector({"Foo"}))); + EXPECT_FALSE(table_name_or_status.ok()); + EXPECT_THAT(table_name_or_status.status(), + StatusIs(absl::StatusCode::kUnimplemented, + HasSubstr("Unsupported AST node type"))); + } + { + // Error: Final statement has a syntax error. + LanguageOptions language_options; + std::string sql = + "CREATE TABLE Foo AS (SELECT 1 AS number UNION ALL SELECT 2 AS " + "number);CREATE TABLE Bar AS (SELECT number * 2 AS doubled_number FROM " + "Foo);CREATE TABLE CREATE TABLE CREATE TABLE"; + ParseResumeLocation parse_resume_location = + ParseResumeLocation::FromString(sql); + bool at_end_of_input = false; + int statement_count = 0; + std::vector table_names; + absl::StatusOr> table_name_or_status; + while (!at_end_of_input) { + table_name_or_status = GetTopLevelTableNameFromNextDDLStatement( + sql, parse_resume_location, &at_end_of_input, language_options); + if (table_name_or_status.ok()) { + table_names.push_back( + IdentifierPathToString(table_name_or_status.value())); + statement_count++; + } else { + at_end_of_input = true; + } + } + EXPECT_EQ(statement_count, 2); + EXPECT_THAT(table_names, + ContainerEq(std::vector({"Foo", "Bar"}))); + EXPECT_FALSE(table_name_or_status.ok()); + EXPECT_THAT(table_name_or_status.status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Syntax error"))); + } +} + } // namespace zetasql diff --git a/zetasql/public/parse_location.h b/zetasql/public/parse_location.h index b3ed00e79..14a29ae23 100644 --- a/zetasql/public/parse_location.h +++ b/zetasql/public/parse_location.h @@ -183,6 +183,12 @@ class ParseLocationRange { // Returns true if both start and end ParseLocationPoint are valid. bool IsValid() const { return start().IsValid() && end().IsValid(); } + // Returns true if the range is empty. + bool IsEmpty() const { + ABSL_DCHECK(IsValid()); + return start() == end(); + } + // Identify whether `following_location` immediately follows this range // with no space (white space, comment, another token) in between. bool IsAdjacentlyFollowedBy( diff --git a/zetasql/public/parse_tokens.cc b/zetasql/public/parse_tokens.cc index 037b30195..ff9e91b4b 100644 --- a/zetasql/public/parse_tokens.cc +++ b/zetasql/public/parse_tokens.cc @@ -29,7 +29,7 @@ #include "zetasql/parser/bison_parser.bison.h" #include "zetasql/parser/bison_parser_mode.h" #include "zetasql/parser/keywords.h" -#include "zetasql/parser/token_disambiguator.h" +#include "zetasql/parser/lookahead_transformer.h" #include "zetasql/public/error_helpers.h" #include "zetasql/public/functions/convert_string.h" #include "zetasql/public/parse_location.h" @@ -255,7 +255,7 @@ absl::Status GetParseTokens(const ParseTokenOptions& options, auto arena = std::make_unique(/*block_size=*/4096); ZETASQL_ASSIGN_OR_RETURN( auto tokenizer, - parser::DisambiguatorLexer::Create( + parser::LookaheadTransformer::Create( mode, resume_location->filename(), resume_location->input(), resume_location->byte_position(), options.language_options, /*macro_catalog=*/nullptr, arena.get())); diff --git a/zetasql/public/simple_catalog.cc b/zetasql/public/simple_catalog.cc index 1253cac31..18f399655 100644 --- a/zetasql/public/simple_catalog.cc +++ b/zetasql/public/simple_catalog.cc @@ -1219,11 +1219,12 @@ absl::Status SimpleCatalog::SerializeImpl( for (const auto& entry : functions) { const Function* const function = entry.second; - // TODO: in case we have a function with an alias we serialize it - // twice here (first for main entry and second time for an alias). Thus - // when we try to deserialize it we fail, because all entries are identical - // and we still insert an alias entry using main function name as a key. - // To fix it we should serialize only main entry. + // Skip serializing the alias entry as the main entry will be serialized, + // this prevents duplicate function entries in the catalog proto. + if (zetasql_base::CaseCompare(entry.first, + function->alias_name()) == 0) { + continue; + } if (!(ignore_builtin && function->IsZetaSQLBuiltin())) { ZETASQL_RETURN_IF_ERROR(function->Serialize(file_descriptor_set_map, proto->add_custom_function())); @@ -1351,6 +1352,15 @@ absl::Status SimpleCatalog::GetFunctions( return absl::OkStatus(); } +absl::Status SimpleCatalog::GetModels( + absl::flat_hash_set* output) const { + ZETASQL_RET_CHECK_NE(output, nullptr); + ZETASQL_RET_CHECK(output->empty()); + absl::MutexLock lock(&mutex_); + InsertValuesFromMap(models_, output); + return absl::OkStatus(); +} + absl::Status SimpleCatalog::GetTableValuedFunctions( absl::flat_hash_set* output) const { ZETASQL_RET_CHECK_NE(output, nullptr); diff --git a/zetasql/public/simple_catalog.h b/zetasql/public/simple_catalog.h index 88339105a..7ef54f8c2 100644 --- a/zetasql/public/simple_catalog.h +++ b/zetasql/public/simple_catalog.h @@ -489,6 +489,8 @@ class SimpleCatalog : public EnumerableCatalog { absl::flat_hash_set* output) const override; absl::Status GetFunctions( absl::flat_hash_set* output) const override; + absl::Status GetModels( + absl::flat_hash_set* output) const override; absl::Status GetTableValuedFunctions( absl::flat_hash_set* output) const override; diff --git a/zetasql/public/simple_catalog_util.cc b/zetasql/public/simple_catalog_util.cc index fd23c8fa3..e8ccc1a32 100644 --- a/zetasql/public/simple_catalog_util.cc +++ b/zetasql/public/simple_catalog_util.cc @@ -25,18 +25,28 @@ #include "zetasql/public/analyzer.h" #include "zetasql/public/analyzer_options.h" #include "zetasql/public/analyzer_output.h" +#include "zetasql/public/catalog.h" #include "zetasql/public/function.h" #include "zetasql/public/function.pb.h" +#include "zetasql/public/function_signature.h" #include "zetasql/public/simple_catalog.h" #include "zetasql/public/sql_function.h" +#include "zetasql/public/sql_tvf.h" #include "zetasql/public/sql_view.h" +#include "zetasql/public/table_valued_function.h" #include "zetasql/public/templated_sql_function.h" +#include "zetasql/public/templated_sql_tvf.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "zetasql/resolved_ast/resolved_ast_enums.pb.h" #include "zetasql/resolved_ast/resolved_node_kind.pb.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" +#include "absl/types/span.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_macros.h" @@ -51,7 +61,7 @@ MakeFunctionFromCreateFunctionImpl( if (function_options.has_value()) { options = *function_options; } else { - // Use-defined functions often use CamelCase. Upper casing that makes it + // User-defined functions often use CamelCase. Upper casing that makes it // unreadable. options.set_uses_upper_case_sql_name(false); } @@ -73,12 +83,20 @@ MakeFunctionFromCreateFunctionImpl( create_function_stmt.argument_name_list(), &create_function_stmt.aggregate_expression_list(), /*parse_resume_location=*/std::nullopt)); - } else { + } else if (create_function_stmt.language() == "SQL") { function = std::make_unique( create_function_stmt.name_path(), create_function_stmt.signature(), create_function_stmt.argument_name_list(), ParseResumeLocation::FromStringView(create_function_stmt.code()), function_mode, options); + } else { + // The group arg is just used for distinguishing classes of functions + // in some error messages. + std::vector signatures = { + create_function_stmt.signature()}; + function = std::make_unique(create_function_stmt.name_path(), + /*group=*/"External_function", + function_mode, signatures, options); } function->set_sql_security(create_function_stmt.sql_security()); @@ -90,11 +108,12 @@ absl::Status AddFunctionFromCreateFunction( bool allow_persistent_function, std::optional function_options, std::unique_ptr& analyzer_output, - SimpleCatalog& catalog) { + Catalog& resolving_catalog, SimpleCatalog& catalog) { ZETASQL_RET_CHECK(analyzer_options.language().SupportsStatementKind( RESOLVED_CREATE_FUNCTION_STMT)); - ZETASQL_RETURN_IF_ERROR(AnalyzeStatement(create_sql_stmt, analyzer_options, &catalog, - catalog.type_factory(), &analyzer_output)) + ZETASQL_RETURN_IF_ERROR(AnalyzeStatement(create_sql_stmt, analyzer_options, + &resolving_catalog, catalog.type_factory(), + &analyzer_output)) << create_sql_stmt; const ResolvedStatement* resolved = analyzer_output->resolved_statement(); ZETASQL_RET_CHECK(resolved->Is()); @@ -121,6 +140,71 @@ absl::StatusOr> MakeFunctionFromCreateFunction( std::move(function_options)); } +absl::Status AddTVFFromCreateTableFunction( + absl::string_view create_tvf_stmt, const AnalyzerOptions& analyzer_options, + bool allow_persistent, + std::unique_ptr& analyzer_output, + SimpleCatalog& catalog) { + ZETASQL_RET_CHECK(analyzer_options.language().SupportsStatementKind( + RESOLVED_CREATE_TABLE_FUNCTION_STMT)); + ZETASQL_RETURN_IF_ERROR(AnalyzeStatement(create_tvf_stmt, analyzer_options, &catalog, + catalog.type_factory(), &analyzer_output)) + << create_tvf_stmt; + const ResolvedStatement* resolved = analyzer_output->resolved_statement(); + ZETASQL_RET_CHECK(resolved->Is()); + const ResolvedCreateTableFunctionStmt* stmt = + resolved->GetAs(); + if (!allow_persistent) { + ZETASQL_RET_CHECK_EQ(stmt->create_scope(), + ResolvedCreateStatementEnums::CREATE_TEMP); + } + + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr tvf, + MakeTVFFromCreateTableFunction(*stmt)); + + ZETASQL_RET_CHECK(catalog.AddOwnedTableValuedFunctionIfNotPresent(&tvf)); + + return absl::OkStatus(); +} + +absl::StatusOr> +MakeTVFFromCreateTableFunction(const ResolvedCreateTableFunctionStmt& stmt) { + std::unique_ptr tvf; + if (stmt.query() != nullptr) { + ZETASQL_RET_CHECK(!stmt.signature().IsTemplated()); + ZETASQL_RET_CHECK_EQ(stmt.language(), "SQL"); + std::unique_ptr sql_tvf; + ZETASQL_RETURN_IF_ERROR(SQLTableValuedFunction::Create(&stmt, &sql_tvf)); + tvf = std::move(sql_tvf); + } else if (stmt.language() == "SQL" && !stmt.code().empty()) { + ZETASQL_RET_CHECK(stmt.signature().IsTemplated()); + tvf = std::make_unique( + stmt.name_path(), stmt.signature(), stmt.argument_name_list(), + ParseResumeLocation::FromStringView(stmt.code())); + } else { + const FunctionArgumentType& result_type = stmt.signature().result_type(); + ZETASQL_RET_CHECK(result_type.IsRelation()); + ZETASQL_RET_CHECK(result_type.options().has_relation_input_schema()) + << "Only TVFs with fixed output table schemas are supported"; + + tvf = std::make_unique( + stmt.name_path(), stmt.signature(), + result_type.options().relation_input_schema()); + } + + // Give an error if any options were set that weren't handled above. + // Some fields are okay to ignore here so we mark them accessed. + for (const auto& col : stmt.output_column_list()) { + col->MarkFieldsAccessed(); + } + if (stmt.query() != nullptr) { + stmt.query()->MarkFieldsAccessed(); + } + ZETASQL_RETURN_IF_ERROR(stmt.CheckFieldsAccessed()); + + return tvf; +} + absl::Status AddViewFromCreateView( absl::string_view create_view_stmt, const AnalyzerOptions& analyzer_options, bool allow_non_temp, std::unique_ptr& analyzer_output, @@ -157,4 +241,59 @@ absl::Status AddViewFromCreateView( return absl::OkStatus(); } +absl::Status AddTableFromCreateTable( + absl::string_view create_table_stmt, + const AnalyzerOptions& analyzer_options, bool allow_non_temp, + std::unique_ptr& analyzer_output, SimpleTable*& table, + SimpleCatalog& catalog) { + table = nullptr; + + ZETASQL_RET_CHECK(analyzer_options.language().SupportsStatementKind( + RESOLVED_CREATE_TABLE_STMT)); + ZETASQL_RETURN_IF_ERROR(AnalyzeStatement(create_table_stmt, analyzer_options, + &catalog, catalog.type_factory(), + &analyzer_output)) + << create_table_stmt; + const ResolvedStatement* resolved = analyzer_output->resolved_statement(); + ZETASQL_RET_CHECK(resolved->Is()); + const ResolvedCreateTableStmt* stmt = + resolved->GetAs(); + if (!allow_non_temp) { + ZETASQL_RET_CHECK_EQ(stmt->create_scope(), + ResolvedCreateStatementEnums::CREATE_TEMP); + } + + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr created_table, + MakeTableFromCreateTable(*stmt)); + + SimpleTable* table_ptr = created_table.get(); + const std::string table_name = created_table->Name(); + ZETASQL_RET_CHECK( + catalog.AddOwnedTableIfNotPresent(table_name, std::move(created_table))); + + // Give an error if any options were set that weren't handled above. + ZETASQL_RETURN_IF_ERROR(stmt->CheckFieldsAccessed()); + + table = table_ptr; + return absl::OkStatus(); +} + +absl::StatusOr> MakeTableFromCreateTable( + const ResolvedCreateTableStmtBase& stmt) { + ZETASQL_RET_CHECK_EQ(stmt.name_path().size(), 1) + << "Table names with sub-catalogs are not supported"; + + std::vector columns; + for (const auto& column_def : stmt.column_definition_list()) { + columns.push_back({column_def->name(), column_def->type()}); + } + auto created_table = std::make_unique( + absl::StrJoin(stmt.name_path(), "."), columns); + + // Give an error if any options were set that weren't handled above. + ZETASQL_RETURN_IF_ERROR(stmt.CheckFieldsAccessed()); + + return created_table; +} + } // namespace zetasql diff --git a/zetasql/public/simple_catalog_util.h b/zetasql/public/simple_catalog_util.h index 743c62dc2..4bc102607 100644 --- a/zetasql/public/simple_catalog_util.h +++ b/zetasql/public/simple_catalog_util.h @@ -19,20 +19,23 @@ #include #include +#include #include "zetasql/public/analyzer_options.h" #include "zetasql/public/analyzer_output.h" +#include "zetasql/public/catalog.h" #include "zetasql/public/function.h" #include "zetasql/public/simple_catalog.h" +#include "zetasql/resolved_ast/resolved_ast.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/string_view.h" namespace zetasql { // Compiles `create_sql_stmt` and adds the resulting function to `catalog`. // -// `create_sql_stmt`: Must be a CREATE FUNCTION statement that specifies a SQL -// defined functions. Non-SQL functions are not supported by this utility. +// `create_sql_stmt`: Must be a CREATE FUNCTION statement. // `analyzer_options`: Analyzer options used to analyze `create_sql_stmt`. // `allow_persistent_function`: Unless this is set to true, the utility is // restricted to CREATE TEMP FUNCTION. Use with caution, SimpleCatalog does @@ -42,22 +45,54 @@ namespace zetasql { // `analyzer_output`: Analyzer outputs from compiling `create_sql_stmt`. The // lifetime of `analyzer_output` must exceed the lifetime of `catalog`. // The language options must support RESOLVED_CREATE_FUNCTION_STMT. +// `resolving_catalog`: The catalog to use for resolving names found during +// analysis. This can be the same catalog as `catalog`. // `catalog`: A SimpleCatalog that will own the created SQLFunction* object. absl::Status AddFunctionFromCreateFunction( absl::string_view create_sql_stmt, const AnalyzerOptions& analyzer_options, bool allow_persistent_function, std::optional function_options, std::unique_ptr& analyzer_output, - SimpleCatalog& catalog); + Catalog& resolving_catalog, SimpleCatalog& catalog); // Creates a Function object from a ResolvedCreateFunctionStmt. // `create_function_stmt` must outlive the returned Function. // `function_options` - if provided will be used to construct the Function. -// `group_name` if provided will be used to set the Function group name. absl::StatusOr> MakeFunctionFromCreateFunction( const ResolvedCreateFunctionStmt& create_function_stmt, std::optional function_options = std::nullopt); +// Compiles `create_tvf_stmt` and adds the resulting TVF to `catalog`. +// +// `create_tvf_stmt`: Must be a CREATE TABLE FUNCTION statement, with +// the restrictions described on `MakeTVFFromCreateTableFunction` below. +// `analyzer_options`: Analyzer options used to analyze the statement. +// The language options must support RESOLVED_CREATE_TABLE_FUNCTION_STMT. +// FEATURE_CREATE_TABLE_FUNCTION is always required, and +// FEATURE_TEMPLATE_FUNCTIONS is for templated TVFs. +// `allow_persistent`: Unless this is set to true, the utility is +// restricted to CREATE TEMP. Use with caution, SimpleCatalog does +// not fully support a distinction between temp and persistent functions. +// `analyzer_output`: Analyzer outputs from compililing the statement. The +// lifetime of `analyzer_output` must exceed the lifetime of `catalog`. +// `catalog`: A SimpleCatalog that will own the created TVF. +absl::Status AddTVFFromCreateTableFunction( + absl::string_view create_tvf_stmt, const AnalyzerOptions& analyzer_options, + bool allow_persistent, + std::unique_ptr& analyzer_output, + SimpleCatalog& catalog); + +// Creates a TableValuedFunction object from a ResolvedCreateTableFunctionStmt. +// `stmt` must outlive the returned object. +// This supports: +// Non-templated SQL TVFs, returning SQLTableValuedFunction. +// Templated SQL TVFs, returning TemplatedSQLTVF. +// Non-SQL TVFs with fixed output schemas, returning FixedOutputSchemaTVF. +// Other TVFs are not handled since they require code to resolve the +// output schema. +absl::StatusOr> +MakeTVFFromCreateTableFunction(const ResolvedCreateTableFunctionStmt& stmt); + // Adds a `Table` object to `catalog` for the view defined by // `create_view_stmt`. // @@ -67,13 +102,36 @@ absl::StatusOr> MakeFunctionFromCreateFunction( // `analyzer_output`: Analyzer outputs from compiling `create_view_stmt`. The // lifetime of `analyzer_output` must exceed the lifetime of `catalog`. // `analyzer_options.language()` must support -// `RESOLVED_CREATE_FUNCTION_STMT`. -// `catalog`: A SimpleCatalog that will own the created SQLFunction* object. +// `RESOLVED_CREATE_VIEW_STMT`. +// `catalog`: A SimpleCatalog that will own the created SQLView* object. absl::Status AddViewFromCreateView( absl::string_view create_view_stmt, const AnalyzerOptions& analyzer_options, bool allow_non_temp, std::unique_ptr& analyzer_output, SimpleCatalog& catalog); +// Adds a `Table` object to `catalog` for the table defined by +// `create_table_stmt`. +// +// `create_table_stmt`: Must be a CREATE TABLE statement (without AS SELECT). +// `analyzer_options`: Analyzer options used to analyze `create_table_stmt`. +// `analyzer_options.language()` must support `RESOLVED_CREATE_TABLE_STMT`. +// `allow_non_temp`: If false, require `CREATE TEMP`. +// `analyzer_output`: Analyzer outputs from compiling `create_table_stmt`. The +// lifetime of `analyzer_output` must exceed the lifetime of `catalog`. +// `table`: The table created inside `catalog`. +// `catalog`: A SimpleCatalog that will own the created SQLView* object. +absl::Status AddTableFromCreateTable( + absl::string_view create_table_stmt, + const AnalyzerOptions& analyzer_options, bool allow_non_temp, + std::unique_ptr& analyzer_output, SimpleTable*& table, + SimpleCatalog& catalog); + +// Construct a SimpleTable from a ResolvedCreateTableStmtBase. +// This works for ResolvedCreateTableStmt and ResolvedCreateTableAsSelectStmt +// but doesn't process the query or set up table contents for either of them. +absl::StatusOr> MakeTableFromCreateTable( + const ResolvedCreateTableStmtBase& create_table_stmt); + } // namespace zetasql #endif // ZETASQL_PUBLIC_SIMPLE_CATALOG_UTIL_H_ diff --git a/zetasql/public/simple_catalog_util_test.cc b/zetasql/public/simple_catalog_util_test.cc index ea927932a..1b8ebea9e 100644 --- a/zetasql/public/simple_catalog_util_test.cc +++ b/zetasql/public/simple_catalog_util_test.cc @@ -18,22 +18,29 @@ #include #include +#include #include "zetasql/base/testing/status_matchers.h" #include "zetasql/public/analyzer.h" #include "zetasql/public/analyzer_options.h" +#include "zetasql/public/catalog.h" #include "zetasql/public/function.h" #include "zetasql/public/function_signature.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/simple_catalog.h" +#include "zetasql/public/table_valued_function.h" #include "zetasql/public/types/type.h" +#include "zetasql/public/value.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "zetasql/resolved_ast/resolved_node_kind.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/status/status.h" +using ::testing::MatchesRegex; using ::testing::Not; using ::zetasql_base::testing::IsOk; +using ::zetasql_base::testing::StatusIs; namespace zetasql { @@ -43,49 +50,82 @@ TEST(SimpleCatalogUtilTest, AddFunctionFromCreateFunctionTest) { std::unique_ptr analyzer_output; // Invalid analyzer options - EXPECT_THAT(AddFunctionFromCreateFunction( - "CREATE TEMP FUNCTION Basic() AS (1)", analyzer_options, - /*allow_persistent_function=*/false, - /*function_options=*/std::nullopt, analyzer_output, simple), - Not(IsOk())); + EXPECT_THAT( + AddFunctionFromCreateFunction( + "CREATE TEMP FUNCTION Basic() AS (1)", analyzer_options, + /*allow_persistent_function=*/false, + /*function_options=*/std::nullopt, analyzer_output, simple, simple), + Not(IsOk())); analyzer_options.mutable_language()->AddSupportedStatementKind( RESOLVED_CREATE_FUNCTION_STMT); ZETASQL_EXPECT_OK(AddFunctionFromCreateFunction( "CREATE TEMP FUNCTION Basic() AS (1)", analyzer_options, /*allow_persistent_function=*/false, /*function_options=*/std::nullopt, - analyzer_output, simple)); + analyzer_output, simple, simple)); + const Function* function; + ZETASQL_EXPECT_OK(simple.FindFunction({"Basic"}, &function)); + EXPECT_EQ(function->FullName(), "Lazy_resolution_function:Basic"); // Duplicate - EXPECT_THAT(AddFunctionFromCreateFunction( - "CREATE TEMP FUNCTION Basic() AS (1)", analyzer_options, - /*allow_persistent_function=*/false, - /*function_options=*/std::nullopt, analyzer_output, simple), - Not(IsOk())); + EXPECT_THAT( + AddFunctionFromCreateFunction( + "CREATE TEMP FUNCTION Basic() AS (1)", analyzer_options, + /*allow_persistent_function=*/false, + /*function_options=*/std::nullopt, analyzer_output, simple, simple), + Not(IsOk())); // Invalid persistent function. - EXPECT_THAT(AddFunctionFromCreateFunction( - "CREATE FUNCTION Persistent() AS (1)", analyzer_options, - /*allow_persistent_function=*/false, - /*function_options=*/std::nullopt, analyzer_output, simple), - Not(IsOk())); + EXPECT_THAT( + AddFunctionFromCreateFunction( + "CREATE FUNCTION Persistent() AS (1)", analyzer_options, + /*allow_persistent_function=*/false, + /*function_options=*/std::nullopt, analyzer_output, simple, simple), + Not(IsOk())); ZETASQL_EXPECT_OK(AddFunctionFromCreateFunction( "CREATE FUNCTION Persistent() AS (1)", analyzer_options, /*allow_persistent_function=*/true, /*function_options=*/std::nullopt, - analyzer_output, simple)); + analyzer_output, simple, simple)); // Analysis failure - EXPECT_THAT(AddFunctionFromCreateFunction( - "CREATE TEMP FUNCTION Template(arg ANY TYPE) AS (arg)", - analyzer_options, /*allow_persistent_function=*/false, - /*function_options=*/std::nullopt, analyzer_output, simple), - Not(IsOk())); + EXPECT_THAT( + AddFunctionFromCreateFunction( + "CREATE TEMP FUNCTION Template(arg ANY TYPE) AS (arg)", + analyzer_options, /*allow_persistent_function=*/false, + /*function_options=*/std::nullopt, analyzer_output, simple, simple), + Not(IsOk())); analyzer_options.mutable_language()->EnableLanguageFeature( FEATURE_TEMPLATE_FUNCTIONS); ZETASQL_EXPECT_OK(AddFunctionFromCreateFunction( "CREATE TEMP FUNCTION Template(arg ANY TYPE) AS (arg)", analyzer_options, /*allow_persistent_function=*/false, /*function_options=*/std::nullopt, - analyzer_output, simple)); + analyzer_output, simple, simple)); + ZETASQL_EXPECT_OK(simple.FindFunction({"Template"}, &function)); + EXPECT_EQ(function->FullName(), "Templated_SQL_Function:Template"); + + // Different resolving catalog. + std::unique_ptr constant; + ZETASQL_ASSERT_OK( + SimpleConstant::Create({"TestConstant"}, Value::Int32(42), &constant)); + SimpleCatalog resolving_catalog("resolving"); + resolving_catalog.AddOwnedConstant("TestConstant", std::move(constant)); + ZETASQL_EXPECT_OK(AddFunctionFromCreateFunction( + "CREATE TEMP FUNCTION MyFunc() RETURNS INT32 AS (TestConstant)", + analyzer_options, + /*allow_persistent_function=*/false, /*function_options=*/std::nullopt, + analyzer_output, resolving_catalog, simple)); + ZETASQL_EXPECT_OK(simple.FindFunction({"MyFunc"}, &function)); + EXPECT_EQ(function->FullName(), "Lazy_resolution_function:MyFunc"); + EXPECT_THAT(resolving_catalog.FindFunction({"MyFunc"}, &function), + Not(IsOk())); + + ZETASQL_EXPECT_OK(AddFunctionFromCreateFunction( + "CREATE TEMP FUNCTION NonSQL(x INT64) RETURNS DOUBLE LANGUAGE C", + analyzer_options, + /*allow_persistent_function=*/false, /*function_options=*/std::nullopt, + analyzer_output, simple, simple)); + ZETASQL_EXPECT_OK(simple.FindFunction({"NonSQL"}, &function)); + EXPECT_EQ(function->FullName(), "External_function:NonSQL"); } TEST(SimpleCatalogUtilTest, MakeFunctionFromCreateFunctionBasic) { @@ -159,4 +199,151 @@ TEST(SimpleCatalogUtilTest, MakeFunctionFromCreateFunctionAgg) { {create_function_stmt->signature()})); } +TEST(SimpleCatalogUtilTest, AddTableFromCreateTable) { + SimpleCatalog catalog("simple"); + SimpleTable* table; + AnalyzerOptions analyzer_options; + std::unique_ptr analyzer_output; + + const char* create_t1 = "CREATE TEMP TABLE t1 (x INT64)"; + + // Invalid analyzer options + EXPECT_THAT(AddTableFromCreateTable(create_t1, analyzer_options, + /*allow_non_temp=*/false, analyzer_output, + table, catalog), + Not(IsOk())); + + analyzer_options.mutable_language()->AddSupportedStatementKind( + RESOLVED_CREATE_TABLE_STMT); + ZETASQL_EXPECT_OK(AddTableFromCreateTable(create_t1, analyzer_options, + /*allow_non_temp=*/false, analyzer_output, + table, catalog)); + + // Duplicate table. + EXPECT_THAT(AddTableFromCreateTable(create_t1, analyzer_options, + /*allow_non_temp=*/false, analyzer_output, + table, catalog), + Not(IsOk())); + + const char* create_t2 = "CREATE TABLE t2 (x INT64)"; + + // Invalid persistent table. + EXPECT_THAT(AddTableFromCreateTable(create_t2, analyzer_options, + /*allow_non_temp=*/false, analyzer_output, + table, catalog), + Not(IsOk())); + + ZETASQL_EXPECT_OK(AddTableFromCreateTable(create_t2, analyzer_options, + /*allow_non_temp=*/true, analyzer_output, + table, catalog)); + + // Check the table got created correctly. + const Table* found_table; + ZETASQL_EXPECT_OK(catalog.FindTable({"t2"}, &found_table)); + EXPECT_EQ(table, found_table); + EXPECT_EQ(found_table->Name(), "t2"); + EXPECT_EQ(found_table->FullName(), "t2"); + EXPECT_EQ(found_table->NumColumns(), 1); + const Column* found_column = found_table->GetColumn(0); + EXPECT_EQ(found_column->Name(), "x"); + EXPECT_EQ(found_column->GetType()->DebugString(), "INT64"); + + // Check we get an error if the CREATE has any modifiers that aren't + // handled by AddTableFromCreateTable. + const char* create_t3 = "CREATE TEMP TABLE t3 (x INT64) OPTIONS(opt=2)"; + + EXPECT_THAT(AddTableFromCreateTable(create_t3, analyzer_options, + /*allow_non_temp=*/false, analyzer_output, + table, catalog), + StatusIs(absl::StatusCode::kUnimplemented)); +} + +TEST(SimpleCatalogUtilTest, AddTVFFromCreateTableFunction) { + SimpleCatalog catalog("simple"); + AnalyzerOptions analyzer_options; + std::unique_ptr analyzer_output; + + // Non-templated SQL TVF, with TEMP. + const char* create_tvf1 = + "CREATE TEMP TABLE FUNCTION tvf1(x INT64) AS (SELECT x)"; + + // Invalid analyzer options + EXPECT_THAT(AddTVFFromCreateTableFunction(create_tvf1, analyzer_options, + /*allow_persistent=*/false, + analyzer_output, catalog), + Not(IsOk())); + + analyzer_options.mutable_language()->AddSupportedStatementKind( + RESOLVED_CREATE_TABLE_FUNCTION_STMT); + analyzer_options.mutable_language()->EnableLanguageFeature( + FEATURE_CREATE_TABLE_FUNCTION); + analyzer_options.mutable_language()->EnableLanguageFeature( + FEATURE_TEMPLATE_FUNCTIONS); + ZETASQL_EXPECT_OK(AddTVFFromCreateTableFunction(create_tvf1, analyzer_options, + /*allow_persistent=*/false, + analyzer_output, catalog)); + + // Check the TVF got created correctly. + const TableValuedFunction* found_tvf; + ZETASQL_EXPECT_OK(catalog.FindTableValuedFunction({"tvf1"}, &found_tvf)); + EXPECT_EQ(found_tvf->Name(), "tvf1"); + EXPECT_EQ(found_tvf->FullName(), "tvf1"); + EXPECT_EQ(found_tvf->NumSignatures(), 1); + EXPECT_EQ(found_tvf->GetSignature(0)->DebugString(), + "(INT64 x) -> TABLE"); + + // Duplicate TVF. + EXPECT_THAT(AddTVFFromCreateTableFunction(create_tvf1, analyzer_options, + /*allow_persistent=*/false, + analyzer_output, catalog), + Not(IsOk())); + + // Templated SQL TVF, without TEMP. + const char* create_tvf2 = + "CREATE TABLE FUNCTION tvf2(t ANY TABLE) AS (SELECT * FROM t)"; + + // Invalid non-TEMP table. + EXPECT_THAT(AddTVFFromCreateTableFunction(create_tvf2, analyzer_options, + /*allow_persistent=*/false, + analyzer_output, catalog), + Not(IsOk())); + + // Allowed if allow_persistent is true. + ZETASQL_EXPECT_OK(AddTVFFromCreateTableFunction(create_tvf2, analyzer_options, + /*allow_persistent=*/true, + analyzer_output, catalog)); + + // Non-SQL TVF with a fixed output schema. + const char* create_tvf3 = + "CREATE TABLE FUNCTION tvf3(t ANY TABLE) RETURNS TABLE"; + + ZETASQL_EXPECT_OK(AddTVFFromCreateTableFunction(create_tvf3, analyzer_options, + /*allow_persistent=*/true, + analyzer_output, catalog)); + + // Non-SQL TVF without a fixed output schema. + const char* create_tvf4 = "CREATE TABLE FUNCTION tvf4(t ANY TABLE)"; + + EXPECT_THAT( + AddTVFFromCreateTableFunction(create_tvf4, analyzer_options, + /*allow_persistent=*/true, analyzer_output, + catalog), + StatusIs( + absl::StatusCode::kInternal, + MatchesRegex( + ".*Only TVFs with fixed output table schemas are supported.*"))); + + // Check we get an error if the CREATE has any modifiers that aren't + // handled by AddTVFFromCreateTableFunction. + const char* create_tvf5 = + "CREATE TABLE FUNCTION tvf2(t ANY TABLE) " + "OPTIONS (opt=5) " + "AS (SELECT * FROM t)"; + + EXPECT_THAT(AddTVFFromCreateTableFunction(create_tvf5, analyzer_options, + /*allow_persistent=*/true, + analyzer_output, catalog), + StatusIs(absl::StatusCode::kUnimplemented)); +} + } // namespace zetasql diff --git a/zetasql/public/strings_test.cc b/zetasql/public/strings_test.cc index 3e12351fe..ec978b8d1 100644 --- a/zetasql/public/strings_test.cc +++ b/zetasql/public/strings_test.cc @@ -61,7 +61,7 @@ constexpr char kUnicodeNotAllowedInBytes1[] = constexpr char kUnicodeNotAllowedInBytes2[] = "Unicode escape sequence \\U cannot be used in bytes literals"; -static void TestIdentifier(const std::string& orig) { +static void TestIdentifier(absl::string_view orig) { const std::string quoted = ToIdentifierLiteral(orig); std::string unquoted; std::string error_string; @@ -73,7 +73,7 @@ static void TestIdentifier(const std::string& orig) { EXPECT_EQ(orig, unquoted) << "quoted: " << quoted; } -static void TestAlwaysQuotedIdentifier(const std::string& orig) { +static void TestAlwaysQuotedIdentifier(absl::string_view orig) { const std::string quoted = ToAlwaysQuotedIdentifierLiteral(orig); EXPECT_THAT(quoted, testing::StartsWith("`")); EXPECT_THAT(quoted, testing::EndsWith("`")); @@ -118,8 +118,8 @@ static void TestRawString(const std::string& unquoted) { // string mentioned in the test case. // This method compares the unescaped against its round trip version // i.e. after carrying out escaping followed by unescaping on it. -static void TestBytesEscaping(const std::string& unquoted, - const std::string& quoted) { +static void TestBytesEscaping(absl::string_view unquoted, + absl::string_view quoted) { std::string unescaped; ZETASQL_EXPECT_OK(UnescapeBytes(unquoted, &unescaped)) << quoted; const std::string escaped = EscapeBytes(unescaped); @@ -198,7 +198,7 @@ static void TestRawBytes(const std::string& unquoted) { absl::StrCat("br\"\"\"", unquoted, "\"\"\"")); } -static void TestParseString(const std::string& orig) { +static void TestParseString(absl::string_view orig) { std::string parsed; ZETASQL_EXPECT_OK(ParseStringLiteral(orig, &parsed)) << orig; } @@ -208,7 +208,7 @@ static void TestParseBytes(const std::string& orig) { ZETASQL_EXPECT_OK(ParseBytesLiteral(orig, &parsed)) << orig; } -static void TestStringEscaping(const std::string& orig) { +static void TestStringEscaping(absl::string_view orig) { const std::string escaped = EscapeString(orig); std::string unescaped; ZETASQL_EXPECT_OK(UnescapeString(escaped, &unescaped)) << orig; @@ -864,8 +864,8 @@ static void ExpectParsedBytes(const std::string& expected, } } -static void ExpectParsedIdentifier(const std::string& expected, - const std::string& quoted) { +static void ExpectParsedIdentifier(absl::string_view expected, + absl::string_view quoted) { std::string parsed; std::string error_string; int error_offset; diff --git a/zetasql/public/table_name_resolver.cc b/zetasql/public/table_name_resolver.cc index 7ca6c95ed..782d36882 100644 --- a/zetasql/public/table_name_resolver.cc +++ b/zetasql/public/table_name_resolver.cc @@ -139,6 +139,12 @@ class TableNameResolver { const ASTQuery* query, const AliasSet& visible_aliases, AliasSet* new_aliases = std::make_unique().get()); + // If non-null, range variables this query exports will be added to + // 'new_aliases'. + absl::Status FindInFromQuery(const ASTFromQuery* from_query, + const AliasSet& visible_aliases, + AliasSet* new_aliases); + // 'visible_aliases' are the aliases that can be resolved inside the query. // Range variables this query exports will be added to 'new_aliases'. absl::Status FindInQueryExpression(const ASTQueryExpression* query_expr, @@ -146,6 +152,10 @@ class TableNameResolver { const AliasSet& visible_aliases, AliasSet* new_aliases); + absl::Status FindInAliasedQueryExpression( + const ASTAliasedQueryExpression* aliased_query, + const AliasSet& visible_aliases, AliasSet* new_aliases); + absl::Status FindInSelect(const ASTSelect* select, const ASTOrderBy* order_by, const AliasSet& orig_visible_aliases); @@ -306,6 +316,12 @@ absl::Status TableNameResolver::FindInStatement(const ASTStatement* statement) { } break; + case AST_CREATE_CONNECTION_STATEMENT: + if (analyzer_options_->language().SupportsStatementKind( + RESOLVED_CREATE_CONNECTION_STMT)) { + return absl::OkStatus(); + } + break; case AST_CREATE_DATABASE_STATEMENT: if (analyzer_options_->language().SupportsStatementKind( RESOLVED_CREATE_DATABASE_STMT)) { @@ -786,6 +802,12 @@ absl::Status TableNameResolver::FindInStatement(const ASTStatement* statement) { return absl::OkStatus(); } break; + case AST_ALTER_CONNECTION_STATEMENT: + if (analyzer_options_->language().SupportsStatementKind( + RESOLVED_ALTER_CONNECTION_STMT)) { + return absl::OkStatus(); + } + break; case AST_ALTER_PRIVILEGE_RESTRICTION_STATEMENT: if (analyzer_options_->language().SupportsStatementKind( RESOLVED_ALTER_PRIVILEGE_RESTRICTION_STMT)) { @@ -1247,6 +1269,26 @@ absl::Status TableNameResolver::FindInQuery(const ASTQuery* query, query->query_expr(), query->order_by(), visible_aliases, /*new_aliases=*/&local_visible_aliases)); + // We need to handle any pipe operators that can include table references + // outside expressions. Operators with just expressions are handled by + // the default case. + // TODO Will need to handle ASTPipeSetOperation here. + for (const ASTPipeOperator* pipe_operator : query->pipe_operator_list()) { + switch (pipe_operator->node_kind()) { + case AST_PIPE_CALL: + ZETASQL_RETURN_IF_ERROR(FindInTVF(pipe_operator->GetAs()->tvf(), + visible_aliases, &local_visible_aliases)); + break; + case AST_PIPE_JOIN: + ZETASQL_RETURN_IF_ERROR(FindInJoin(pipe_operator->GetAs()->join(), + visible_aliases, &local_visible_aliases)); + break; + default: + ZETASQL_RETURN_IF_ERROR(FindInExpressionsUnder(pipe_operator, visible_aliases)); + break; + } + } + new_aliases->insert(local_visible_aliases.begin(), local_visible_aliases.end()); @@ -1277,6 +1319,15 @@ absl::Status TableNameResolver::FindInQueryExpression( ZETASQL_RETURN_IF_ERROR( FindInQuery(query_expr->GetAs(), visible_aliases)); break; + case AST_ALIASED_QUERY_EXPRESSION: + ZETASQL_RETURN_IF_ERROR(FindInAliasedQueryExpression( + query_expr->GetAs(), visible_aliases, + new_aliases)); + break; + case AST_FROM_QUERY: + ZETASQL_RETURN_IF_ERROR(FindInFromQuery(query_expr->GetAs(), + visible_aliases, new_aliases)); + break; default: return MakeSqlErrorAt(query_expr) << "Unhandled query_expr:\n" << query_expr->DebugString(); @@ -1288,6 +1339,36 @@ absl::Status TableNameResolver::FindInQueryExpression( return absl::OkStatus(); } +absl::Status TableNameResolver::FindInAliasedQueryExpression( + const ASTAliasedQueryExpression* aliased_query, + const AliasSet& visible_aliases, AliasSet* new_aliases) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + + // Ignore the new_aliases from the contained query. Produce the + // assigned range variable instead. + ZETASQL_RETURN_IF_ERROR(FindInQuery(aliased_query->query(), visible_aliases)); + + new_aliases->insert(aliased_query->alias()->GetAsString()); + + return absl::OkStatus(); +} + +absl::Status TableNameResolver::FindInFromQuery(const ASTFromQuery* from_query, + const AliasSet& visible_aliases, + AliasSet* new_aliases) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + const ASTFromClause* from_clause = from_query->from_clause(); + ZETASQL_RET_CHECK(from_clause != nullptr); + ZETASQL_RET_CHECK(from_clause->table_expression() != nullptr); + AliasSet local_visible_aliases = visible_aliases; + ZETASQL_RETURN_IF_ERROR(FindInTableExpression(from_clause->table_expression(), + visible_aliases, + &local_visible_aliases)); + new_aliases->insert(local_visible_aliases.begin(), + local_visible_aliases.end()); + return absl::OkStatus(); +} + absl::Status TableNameResolver::FindInSelect( const ASTSelect* select, const ASTOrderBy* order_by, @@ -1348,6 +1429,8 @@ absl::Status TableNameResolver::FindInTableExpression( case AST_TVF: return FindInTVF(table_expr->GetAs(), external_visible_aliases, local_visible_aliases); + case AST_PIPE_JOIN_LHS_PLACEHOLDER: + return absl::OkStatus(); default: return MakeSqlErrorAt(table_expr) << "Unhandled node type in from clause: " diff --git a/zetasql/public/table_name_resolver_test.cc b/zetasql/public/table_name_resolver_test.cc index 873314266..4f778092f 100644 --- a/zetasql/public/table_name_resolver_test.cc +++ b/zetasql/public/table_name_resolver_test.cc @@ -22,6 +22,7 @@ #include "zetasql/base/testing/status_matchers.h" #include "zetasql/parser/parser.h" #include "zetasql/public/analyzer.h" +#include "zetasql/public/analyzer_options.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_resume_location.h" #include "zetasql/public/simple_catalog.h" diff --git a/zetasql/public/templated_sql_tvf.h b/zetasql/public/templated_sql_tvf.h index 883321cfd..74a8547da 100644 --- a/zetasql/public/templated_sql_tvf.h +++ b/zetasql/public/templated_sql_tvf.h @@ -66,9 +66,7 @@ class TemplatedSQLTVF : public TableValuedFunction { const std::vector& arg_name_list, const ParseResumeLocation& parse_resume_location, TableValuedFunctionOptions tvf_options = {}) - : TableValuedFunction(function_name_path, - signature, - tvf_options), + : TableValuedFunction(function_name_path, signature, tvf_options), arg_name_list_(arg_name_list), parse_resume_location_(parse_resume_location) {} @@ -181,10 +179,22 @@ class TemplatedSQLTVFSignature : public TVFSignature { // This contains the fully-resolved function body in context of the actual // concrete types of the provided arguments to the function call. + // + // The returned pointer will be invalid after calling the + // `set_resolved_templated_query`. const ResolvedQueryStmt* resolved_templated_query() const { return resolved_templated_query_.get(); } + // Replaces the resolved function body. + // + // The returned pointer from `resolved_templated_query` will be invalid after + // calling this method. + void set_resolved_templated_query( + std::unique_ptr resolved_templated_query) { + resolved_templated_query_ = std::move(resolved_templated_query); + } + // The list of names of all the function arguments, in the same order that // they appear in the function signature. const std::vector& GetArgumentNames() const { diff --git a/zetasql/public/testing/BUILD b/zetasql/public/testing/BUILD index 6a940a0b2..ce30d6bbb 100644 --- a/zetasql/public/testing/BUILD +++ b/zetasql/public/testing/BUILD @@ -33,3 +33,27 @@ cc_library( "@com_google_file_based_test_driver//file_based_test_driver:test_case_options", ], ) + +cc_library( + name = "error_matchers", + testonly = 1, + hdrs = ["error_matchers.h"], + deps = [ + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:status_matchers", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest", + ], +) + +cc_test( + name = "error_matchers_test", + srcs = ["error_matchers_test.cc"], + deps = [ + ":error_matchers", + "//zetasql/base/testing:zetasql_gtest_main", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings:string_view", + ], +) diff --git a/zetasql/public/testing/error_matchers.h b/zetasql/public/testing/error_matchers.h new file mode 100644 index 000000000..f53367fb0 --- /dev/null +++ b/zetasql/public/testing/error_matchers.h @@ -0,0 +1,258 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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 file contains a library of test matchers for matching ZetaSQL error +// messages in unit tests. Using matchers controlled by the ZetaSQL library +// helps prevent brittle tests that depend on the exact wording of error +// messages. +// +// Additional matchers can be added to this file as needed, but please make +// sure that added matchers are written in a way so that they aren't brittle in +// the same way that matching the error test directly would be. In particular, +// avoid matchers that accept a error messages fragment as a string argument. +// +// Example usage: +// +// TEST(MyTest, MyTest) { +// zetasql::SimpleCatalog catalog = InitializeCatalog(); +// // MyFunction only accepts a named argument. +// EXPECT_THAT( +// PrepareQuery("SELECT MyFunction(arg=>1)"), ::testing::IsOk()); +// EXPECT_THAT( +// PrepareQuery("SELECT MyFunction(1)"), +// zetasql::IsNamedArgumentSuppliedPositionallyError()); +// } + +#ifndef ZETASQL_PUBLIC_TESTING_ERROR_MATCHERS_H_ +#define ZETASQL_PUBLIC_TESTING_ERROR_MATCHERS_H_ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/status/status_matchers.h" +#include "absl/status/statusor.h" +#include "absl/strings/ascii.h" +#include "absl/strings/string_view.h" + +namespace zetasql { + +namespace internal { + +inline absl::string_view GetMessage(const absl::Status& status) { + return status.message(); +} + +template +inline absl::string_view GetMessage(const absl::StatusOr& status) { + return status.status().message(); +} + +} // namespace internal + +// Matches an error message indicating that no function signature matches the +// arguments supplied to the function. +// +// The argument is the function name. If the error matched reports a function +// name then the matcher will assert that the reported name matches the argument +// in a case insensitive manner. +// +// Typically users should prefer to match the entire Status or StatusOr using +// IsFunctionSignatureMismatchError. The Message variation is useful for +// testing cases where the StatusCode has been changed by software layers above +// the ZetaSQL library. +MATCHER_P( + IsFunctionSignatureMismatchErrorMessage, function_name, + "is an error indicating no function signatures matches the arguments") { + auto name_matcher = + ::testing::HasSubstr(absl::AsciiStrToUpper(function_name)); + auto message_matcher = ::testing::ContainsRegex("No matching signature for"); + return ExplainMatchResult(name_matcher, absl::AsciiStrToUpper(arg), + result_listener) && + ExplainMatchResult(message_matcher, arg, result_listener); +} + +// Matches an error Status indicating that no function signature matches the +// arguments supplied to the function. +// +// The argument is the function name. If the error matched reports a function +// name then the matcher will assert that the reported name matches the argument +// in a case insensitive manner. +MATCHER_P( + IsFunctionSignatureMismatchError, function_name, + "is an error indicating no function signatures matches the arguments") { + return ExplainMatchResult( + absl_testing::StatusIs( + absl::StatusCode::kInvalidArgument, + IsFunctionSignatureMismatchErrorMessage(function_name)), + arg, result_listener); +} + +// Matches an error message indicating that no operator signature matches the +// arguments supplied to the operator. +// +// The argument is the operator name. If the error matched reports an operator +// name then the matcher will assert that the reported name matches the argument +// in a case insensitive manner. +// +// Typically users should prefer to match the entire Status or StatusOr using +// IsOperatorSignatureMismatchError. The Message variation is useful for +// testing cases where the StatusCode has been changed by software layers above +// the ZetaSQL library. +MATCHER_P( + IsOperatorSignatureMismatchErrorMessage, operator_name, + "is an error indicating no operator signatures matches the arguments") { + auto name_matcher = + ::testing::HasSubstr(absl::AsciiStrToUpper(operator_name)); + auto message_matcher = + ::testing::HasSubstr("No matching signature for operator"); + return ExplainMatchResult(name_matcher, absl::AsciiStrToUpper(arg), + result_listener) && + ExplainMatchResult(message_matcher, arg, result_listener); +} + +// Matches an error Status indicating that no operator signature matches the +// arguments supplied to the operator. +// +// The argument is the operator name. If the error matched reports an operator +// name then the matcher will assert that the reported name matches the argument +// in a case insensitive manner. +MATCHER_P( + IsOperatorSignatureMismatchError, operator_name, + "is an error indicating no operator signatures matches the arguments") { + return ExplainMatchResult( + absl_testing::StatusIs( + absl::StatusCode::kInvalidArgument, + IsOperatorSignatureMismatchErrorMessage(operator_name)), + arg, result_listener); +} + +// Matches an error message indicating that no function signature has the right +// number of arguments. +// +// The argument is the function name. If the error matched reports a function +// name then the matcher will assert that the reported name matches the argument +// in a case insensitive manner. +MATCHER_P(IsWrongNumberOfArgsErrorMessage, function_name, + "is an error message indicating the number of arguments does not " + "match any signature for the function") { + auto name_matcher = + ::testing::HasSubstr(absl::AsciiStrToUpper(function_name)); + auto message_matcher = ::testing::AnyOf( + ::testing::ContainsRegex( + "Number of arguments does not match for (aggregate )?function"), + ::testing::ContainsRegex("Signature accepts at most \\d+ " + "arguments?, found \\d+ argument"), + ::testing::ContainsRegex("Signature requires at least \\d+ " + "arguments?, found \\d+ argument")); + return ExplainMatchResult(name_matcher, absl::AsciiStrToUpper(arg), + result_listener) && + ExplainMatchResult(message_matcher, arg, result_listener); +} + +// Matches an error Status indicating that no function signature has the right +// number of arguments. +// +// The argument is the function name. If the error matched reports a function +// name then the matcher will assert that the reported name matches the argument +// in a case insensitive manner. +MATCHER_P(IsWrongNumberOfArgsError, function_name, + "is an error Status indicating the number of arguments does not " + "match any signature for the function") { + return ExplainMatchResult( + absl_testing::StatusIs(absl::StatusCode::kInvalidArgument, + IsWrongNumberOfArgsErrorMessage(function_name)), + arg, result_listener); +} + +MATCHER( + IsNamedArgumentSuppliedPositionallyError, + "is an error indicating a named-only argument is supplied positionally") { + auto matcher = ::testing::AnyOf( + absl_testing::StatusIs( + absl::StatusCode::kInvalidArgument, + ::testing::HasSubstr( + "Positional argument is invalid because this function restricts " + "that this argument is referred to by name")), + absl_testing::StatusIs( + absl::StatusCode::kInvalidArgument, + ::testing::ContainsRegex( + "Positional argument at .* is invalid because argument .* " + "can only be referred to by name"))); + return ExplainMatchResult(matcher, arg, result_listener); +} + +// Matches an error message indicating that the supplied named argument is not +// found. +// +// The argument is the name of the named argument. If the error matched reports +// an argument name then the matcher will assert that the reported name matches +// the argument in a case insensitive manner. +MATCHER_P(IsNamedArgumentNotFoundErrorMessage, arg_name, + "is an error message indicating a named argument is not found") { + auto name_matcher = ::testing::HasSubstr(absl::AsciiStrToUpper(arg_name)); + auto message_matcher = ::testing::AnyOf( + ::testing::ContainsRegex( + "Named argument .* not found in signature for call to.*"), + ::testing::ContainsRegex( + "Named argument .* does not exist in signature")); + if (ExplainMatchResult(name_matcher, absl::AsciiStrToUpper(arg), + result_listener) && + ExplainMatchResult(message_matcher, arg, result_listener)) { + return true; + } + // Before the improved function signature mismatch error messages, sometimes + // we produced a "wrong number of arguments" error. + return ExplainMatchResult( + ::testing::ContainsRegex( + "Number of arguments does not match for (aggregate )?function"), + arg, result_listener); +} + +// Matches an error Status indicating that the supplied named argument is not +// found. +// +// The argument is the name of the named argument. If the error matched reports +// an argument name then the matcher will assert that the reported name matches +// the argument in a case insensitive manner. +MATCHER_P(IsNamedArgumentNotFoundError, arg_name, + "is an error status indicating a named-only argument is not found") { + return ExplainMatchResult( + absl_testing::StatusIs(absl::StatusCode::kInvalidArgument, + IsNamedArgumentNotFoundErrorMessage(arg_name)), + arg, result_listener); +} + +// Matches an error indicating that a ZetaSQL statement kind is not supported. +// +// The argument is the statement kind name. If the error matched reports a +// statement kind name then the matcher will assert that the reported name +// matches the argument in a case insensitive manner. +MATCHER_P(IsStatementNotSupportedError, statement_kind_name, + "is an error indicating the statement kind is not supported") { + auto kind_matcher = + ::testing::HasSubstr(absl::AsciiStrToUpper(statement_kind_name)); + auto status_matcher = + absl_testing::StatusIs(absl::StatusCode::kInvalidArgument, + ::testing::HasSubstr("Statement not supported:")); + return ExplainMatchResult(kind_matcher, + absl::AsciiStrToUpper(internal::GetMessage(arg)), + result_listener) && + ExplainMatchResult(status_matcher, arg, result_listener); +} + +} // namespace zetasql + +#endif // ZETASQL_PUBLIC_TESTING_ERROR_MATCHERS_H_ diff --git a/zetasql/public/testing/error_matchers_test.cc b/zetasql/public/testing/error_matchers_test.cc new file mode 100644 index 000000000..ee343f746 --- /dev/null +++ b/zetasql/public/testing/error_matchers_test.cc @@ -0,0 +1,112 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/public/testing/error_matchers.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/status/status.h" +#include "absl/strings/string_view.h" + +namespace zetasql { + +using ::testing::Not; + +TEST(ErrorMatchersTest, NamedArgumentSuppliedPositionallyMatches) { + EXPECT_THAT(absl::InvalidArgumentError( + "Positional argument is invalid because this function " + "restricts that this argument is referred to by name"), + IsNamedArgumentSuppliedPositionallyError()); + EXPECT_THAT(absl::InvalidArgumentError( + R"(No matching signature for function SPANNER:SNIPPET + Argument types: STRING, STRING, BOOL, STRING, INT64 + Signature: SNIPPET(STRING, STRING, [enhance_query => BOOL], [language_tag => STRING], [max_snippet_width => INT64], [max_snippets => INT64], [content_type => STRING]) + Positional argument at 3 is invalid because argument `enhance_query` can only be referred to by name [at 2:14] + SELECT SNIPPET(a.Summary, 'foo', true, "", 10) + ^)"), + IsNamedArgumentSuppliedPositionallyError()); +} + +TEST(ErrorMatchersTest, WrongNumberOfArgs) { + EXPECT_THAT(absl::InvalidArgumentError( + "Number of arguments does not match for function foo"), + IsWrongNumberOfArgsError("foo")); + EXPECT_THAT( + absl::InvalidArgumentError( + "Number of arguments does not match for aggregate function foo"), + IsWrongNumberOfArgsError("foo")); +} + +TEST(ErrorMatchersTest, FiunctionSignatureMismatch) { + absl::string_view error_message = "No matching signature for function foo"; + EXPECT_THAT(error_message, IsFunctionSignatureMismatchErrorMessage("foo")); + EXPECT_THAT(absl::InvalidArgumentError(error_message), + IsFunctionSignatureMismatchError("foo")); +} + +TEST(ErrorMatchersTest, FiunctionSignatureMismatchAggregate) { + absl::string_view error_message = + "No matching signature for aggregate function foo"; + EXPECT_THAT(error_message, IsFunctionSignatureMismatchErrorMessage("foo")); + EXPECT_THAT(absl::InvalidArgumentError(error_message), + IsFunctionSignatureMismatchError("foo")); +} + +TEST(ErrorMatchersTest, OperatorSignatureMismatch) { + absl::string_view error_message = "No matching signature for operator +"; + EXPECT_THAT(error_message, IsOperatorSignatureMismatchErrorMessage("+")); + EXPECT_THAT(absl::InvalidArgumentError(error_message), + IsOperatorSignatureMismatchError("+")); +} + +TEST(ErrorMatchersTest, IsNamedArgumentNotFoundError) { + absl::Status error = absl::InvalidArgumentError( + "Named argument `foo` not found in signature for call to function bar"); + EXPECT_THAT(error, IsNamedArgumentNotFoundError("foo")); + EXPECT_THAT(error, IsNamedArgumentNotFoundError("bar")); +} + +TEST(ErrorMatchersTest, DoesNotMatch) { + absl::Status error = + absl::InvalidArgumentError("Statement not supported: ExplainStatement"); + EXPECT_THAT(error, IsStatementNotSupportedError("EXPLAIN")); + EXPECT_THAT(error, IsStatementNotSupportedError("Explain")); + EXPECT_THAT(error, Not(IsStatementNotSupportedError("Select"))); +} + +TEST(ErrorMatchersTest, IsNamedArgumentNotFoundErrorMessage) { + constexpr absl::string_view kErrorMessage = + "No matching signature for aggregate function COUNT(INT64, STRING, " + "STRING); \n Signature: COUNT(T2, [contribution_bounds_per_group => " + "STRUCT]) -> INT64\n Named argument " + "`bounding_algorithm_type` does not exist in signature [at 2:12]"; + EXPECT_THAT(kErrorMessage, + IsNamedArgumentNotFoundErrorMessage("bounding_algorithm_type")); +} + +TEST(ErrorMatchersTest, IsNamedArgumentNotFoundErrorMessageOld) { + // Before the improved function signature mismatch error messages, sometimes + // we produced an error reporting wrong number of arguments in this case. + constexpr absl::string_view kErrorMessage = + "No matching signature for aggregate function COUNT(INT64, STRING, " + "STRING); \n Signature: COUNT(T2, [contribution_bounds_per_group => " + "STRUCT]) -> INT64\n Named argument " + "`bounding_algorithm_type` does not exist in signature [at 2:12]"; + EXPECT_THAT(kErrorMessage, + IsNamedArgumentNotFoundErrorMessage("bounding_algorithm_type")); +} + +} // namespace zetasql diff --git a/zetasql/public/types/BUILD b/zetasql/public/types/BUILD index 2b42fdcbe..64eab6a48 100644 --- a/zetasql/public/types/BUILD +++ b/zetasql/public/types/BUILD @@ -26,7 +26,6 @@ cc_library( "extended_type.cc", "internal_utils.cc", "internal_utils.h", - "list_backed_type.cc", "map_type.cc", "proto_type.cc", "range_type.cc", @@ -103,6 +102,7 @@ cc_library( "//zetasql/public/functions:normalize_mode_cc_proto", "//zetasql/public/functions:range_sessionize_mode_cc_proto", "//zetasql/public/functions:rounding_mode_cc_proto", + "//zetasql/public/functions:unsupported_fields_cc_proto", "//zetasql/public/proto:type_annotation_cc_proto", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/base:core_headers", @@ -147,6 +147,7 @@ cc_library( "//zetasql/public:uuid_value", "//zetasql/public:value_content", "@com_google_absl//absl/strings:cord", + "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", "@com_google_absl//absl/types:variant", diff --git a/zetasql/public/types/array_type.cc b/zetasql/public/types/array_type.cc index efba3a034..8ed537821 100644 --- a/zetasql/public/types/array_type.cc +++ b/zetasql/public/types/array_type.cc @@ -17,17 +17,24 @@ #include "zetasql/public/types/array_type.h" #include +#include +#include #include #include #include +#include #include #include #include "zetasql/base/logging.h" #include "zetasql/common/errors.h" +#include "zetasql/common/float_margin.h" +#include "zetasql/common/thread_stack.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/type.pb.h" +#include "zetasql/public/types/collation.h" +#include "zetasql/public/types/container_type.h" #include "zetasql/public/types/list_backed_type.h" #include "zetasql/public/types/proto_type.h" #include "zetasql/public/types/struct_type.h" @@ -37,13 +44,13 @@ #include "zetasql/public/types/value_representations.h" #include "zetasql/public/value.pb.h" #include "zetasql/public/value_content.h" -#include "absl/container/flat_hash_map.h" +#include "absl/base/attributes.h" +#include "absl/container/flat_hash_set.h" #include "absl/hash/hash.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/substitute.h" -#include "zetasql/base/compact_reference_counted.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_macros.h" @@ -386,9 +393,9 @@ bool ArrayType::EqualElementMultiSet( y.GetAs()->value(); std::string* reason = options.reason; using CountMap = - absl::flat_hash_map; + std::unordered_map; MultisetValueContentContainerElementHasher hasher(options.float_margin, element_type()); @@ -540,56 +547,79 @@ absl::Status ArrayType::DeserializeValueContent(const ValueProto& value_proto, "value content is maintained in the Value class"); } -std::string ArrayType::GetFormatPrefix( - const ValueContent& value_content, - const FormatValueContentOptions& options) const { - std::string prefix; - switch (options.mode) { - case Type::FormatValueContentOptions::Mode::kDebug: { - const internal::ValueContentOrderedListRef* container_ref = - value_content.GetAs(); - if (options.verbose) { - const internal::ValueContentOrderedList* container = - container_ref->value(); - if (container->num_elements() == 0) { - prefix.append(CapitalizedName()); - } else { - prefix.append("Array"); - } - } - prefix.push_back('['); - if (options.include_array_ordereness && - container_ref->value()->num_elements() > 1) { - if (container_ref->preserves_order()) { - absl::StrAppend(&prefix, "known order: "); - } else { - absl::StrAppend(&prefix, "unknown order: "); - } - } +void ArrayType::FormatValueContentDebugModeImpl( + const internal::ValueContentOrderedListRef* container_ref, + const FormatValueContentOptions& options, std::string* result) const { + const internal::ValueContentOrderedList* container = container_ref->value(); - break; - } - case Type::FormatValueContentOptions::Mode::kSQLLiteral: { - prefix.push_back('['); - break; - } - case Type::FormatValueContentOptions::Mode::kSQLExpression: { - prefix.append( - TypeName(options.product_mode, options.use_external_float32)); - prefix.push_back('['); - break; + if (options.verbose) { + absl::StrAppend( + result, container->num_elements() == 0 ? CapitalizedName() : "Array"); + } + absl::StrAppend(result, "["); + if (options.include_array_ordereness && container->num_elements() > 1) { + absl::StrAppend(result, container_ref->preserves_order() + ? "known order: " + : "unknown order: "); + } + + for (int i = 0; i < container->num_elements(); ++i) { + const internal::NullableValueContent& element_value_content = + container->element(i); + if (i > 0) { + absl::StrAppend(result, ", "); } + + absl::StrAppend(result, + DebugFormatNullableValueContentForContainer( + element_value_content, element_type(), options)); } - return prefix; + absl::StrAppend(result, "]"); } -char ArrayType::GetFormatClosingCharacter( - const Type::FormatValueContentOptions& options) const { - return ']'; +void ArrayType::FormatValueContentSqlModeImpl( + const internal::ValueContentOrderedListRef* container_ref, + const FormatValueContentOptions& options, std::string* result) const { + const internal::ValueContentOrderedList* container = container_ref->value(); + + if (options.mode == Type::FormatValueContentOptions::Mode::kSQLExpression) { + absl::StrAppend( + result, TypeName(options.product_mode, options.use_external_float32)); + } + absl::StrAppend(result, "["); + + for (int i = 0; i < container->num_elements(); ++i) { + const internal::NullableValueContent& element_value_content = + container->element(i); + if (i > 0) { + absl::StrAppend(result, ", "); + } + absl::StrAppend(result, + FormatNullableValueContent(element_value_content, + element_type(), options)); + } + absl::StrAppend(result, "]"); } -const Type* ArrayType::GetElementType(int index) const { - return element_type(); +std::string ArrayType::FormatValueContent( + const ValueContent& value, const FormatValueContentOptions& options) const { + if (!ThreadHasEnoughStack()) { + return std::string(kFormatValueContentOutOfStackError); + } + + const internal::ValueContentOrderedListRef* container_ref = + value.GetAs(); + std::string result; + + switch (options.mode) { + case Type::FormatValueContentOptions::Mode::kDebug: + FormatValueContentDebugModeImpl(container_ref, options, &result); + return result; + case Type::FormatValueContentOptions::Mode::kSQLLiteral: + case Type::FormatValueContentOptions::Mode::kSQLExpression: + FormatValueContentSqlModeImpl(container_ref, options, &result); + return result; + } } } // namespace zetasql diff --git a/zetasql/public/types/array_type.h b/zetasql/public/types/array_type.h index 63c887963..cdc466733 100644 --- a/zetasql/public/types/array_type.h +++ b/zetasql/public/types/array_type.h @@ -25,6 +25,7 @@ #include "zetasql/public/type.pb.h" #include "zetasql/public/types/list_backed_type.h" #include "zetasql/public/types/type.h" +#include "zetasql/public/types/value_representations.h" #include "absl/hash/hash.h" #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -115,20 +116,9 @@ class ArrayType : public ListBackedType { return sizeof(*this); } - std::string GetFormatPrefix( - const ValueContent& value_content, - const Type::FormatValueContentOptions& options) const override; - - char GetFormatClosingCharacter( - const Type::FormatValueContentOptions& options) const override; - - const Type* GetElementType(int index) const override; - - std::string GetFormatElementPrefix( - const int index, const bool is_null, - const FormatValueContentOptions& options) const override { - return ""; - } + std::string FormatValueContent( + const ValueContent& value, + const FormatValueContentOptions& options) const override; private: ArrayType(const TypeFactory* factory, const Type* element_type); @@ -163,6 +153,12 @@ class ArrayType : public ListBackedType { ValueProto* value_proto) const override; absl::Status DeserializeValueContent(const ValueProto& value_proto, ValueContent* value) const override; + void FormatValueContentDebugModeImpl( + const internal::ValueContentOrderedListRef* container_ref, + const FormatValueContentOptions& options, std::string* result) const; + void FormatValueContentSqlModeImpl( + const internal::ValueContentOrderedListRef* container_ref, + const FormatValueContentOptions& options, std::string* result) const; const Type* const element_type_; diff --git a/zetasql/public/types/container_type.h b/zetasql/public/types/container_type.h index 797294728..d5c818a23 100644 --- a/zetasql/public/types/container_type.h +++ b/zetasql/public/types/container_type.h @@ -22,10 +22,13 @@ #include #include +#include "zetasql/common/thread_stack.h" #include "zetasql/public/types/type.h" #include "zetasql/public/types/value_equality_check_options.h" #include "zetasql/public/types/value_representations.h" +#include "zetasql/base/check.h" #include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" namespace zetasql { @@ -114,6 +117,10 @@ class ContainerType : public Type { std::string FormatNullableValueContent( const internal::NullableValueContent element, const Type* type, const FormatValueContentOptions& options) const { + if (!ThreadHasEnoughStack()) { + return std::string(kFormatValueContentOutOfStackError); + } + if (element.is_null()) { return options.as_literal() ? "NULL" @@ -133,6 +140,44 @@ class ContainerType : public Type { format_options.mode = Type::FormatValueContentOptions::Mode::kDebug; return format_options; } + + // Formats a NullableValueContent in debug mode. If verbose mode is set, the + // the type name is prepended to the formatted content. Ex: String("abc") or + // Int(5). + // + // Since some types prepend the type name explicitly as part of + // FormatValueContent in verbose debug mode, this function ensures the type + // name is not prepended twice. + std::string DebugFormatNullableValueContentForContainer( + const internal::NullableValueContent& nullable_content, const Type* type, + const FormatValueContentOptions& options) const { + ABSL_DCHECK_EQ(options.mode, Type::FormatValueContentOptions::Mode::kDebug) + << "This function should only be called in debug formatting mode"; + + std::string value_str = + FormatNullableValueContent(nullable_content, type, options); + + if (!options.verbose) { + return value_str; + } + + // If the value is not null, and the type has already included the type + // name, don't prepend it again. + if (!nullable_content.is_null() && + type->VerboseDebugFormatValueContentHasTypeName()) { + return value_str; + } + + return type->AddCapitalizedTypePrefix(value_str, + nullable_content.is_null()); + } + + bool VerboseDebugFormatValueContentHasTypeName() const override { + return true; + }; + + static constexpr absl::string_view kFormatValueContentOutOfStackError = + "... "; }; } // namespace zetasql diff --git a/zetasql/public/types/enum_type.cc b/zetasql/public/types/enum_type.cc index 06e15a6d7..fd5a3c6e7 100644 --- a/zetasql/public/types/enum_type.cc +++ b/zetasql/public/types/enum_type.cc @@ -257,7 +257,8 @@ bool EnumType::IsSupportedType(const LanguageOptions& language_options) const { if (language_options.product_mode() == ProductMode::PRODUCT_EXTERNAL && !Equivalent(types::DatePartEnumType()) && !Equivalent(types::NormalizeModeEnumType()) && - !Equivalent(types::RoundingModeEnumType())) { + !Equivalent(types::RoundingModeEnumType()) && + !Equivalent(types::UnsupportedFieldsEnumType())) { return false; } diff --git a/zetasql/public/types/enum_type_test.cc b/zetasql/public/types/enum_type_test.cc index 7678db022..32a05dd9f 100644 --- a/zetasql/public/types/enum_type_test.cc +++ b/zetasql/public/types/enum_type_test.cc @@ -386,6 +386,13 @@ TEST(EnumTypeTest, EnumTypeIsSupported) { proto_base_enabled)); EXPECT_TRUE(types::RangeSessionizeModeEnumType()->IsSupportedType( range_type_enabled)); + + EXPECT_TRUE( + types::UnsupportedFieldsEnumType()->IsSupportedType(product_external)); + EXPECT_TRUE( + types::UnsupportedFieldsEnumType()->IsSupportedType(product_internal)); + EXPECT_TRUE( + types::UnsupportedFieldsEnumType()->IsSupportedType(proto_base_enabled)); } } // namespace zetasql diff --git a/zetasql/public/types/list_backed_type.cc b/zetasql/public/types/list_backed_type.cc deleted file mode 100644 index 0298abe7d..000000000 --- a/zetasql/public/types/list_backed_type.cc +++ /dev/null @@ -1,94 +0,0 @@ -// -// Copyright 2019 Google LLC -// -// Licensed 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 -// -// 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. -// - -#include "zetasql/public/types/list_backed_type.h" - -#include -#include - -#include "zetasql/public/types/value_representations.h" -#include "zetasql/public/value_content.h" - -namespace zetasql { - -// Format container in non-recursive way. The users might give a deeply -// nested struct and cause stack overflow crashes for recursive methods -// TODO: Refactor for MAP type support. MapType is a container but -// does not inherit from the (now incorrectly named) ContainerType. Investigate -// factoring the heap-based stack structure out into a common supertype. -std::string ListBackedType::FormatValueContent( - const ValueContent& value_content, - const Type::FormatValueContentOptions& options) const { - std::string result; - struct Entry { - const ValueContent value_content; - const ListBackedType* container_type; - int next_child_index; - }; - std::stack stack; - stack.push(Entry{value_content, this, 0}); - while (!stack.empty()) { - const Entry top = stack.top(); - const ListBackedType* container_type = top.container_type; - internal::ValueContentOrderedListRef* container_ref = - top.value_content.GetAs(); - const internal::ValueContentOrderedList* container = container_ref->value(); - // If we're just getting started printing container, then - // print this container prefix (e.g. "[" for array or "{" for struct) - if (top.next_child_index == 0) { - result.append( - container_type->GetFormatPrefix(top.value_content, options)); - } - const size_t num_children = container->num_elements(); - int child_index = top.next_child_index; - while (true) { - if (child_index >= num_children) { - result.push_back(container_type->GetFormatClosingCharacter(options)); - stack.pop(); - break; - } - if (child_index != 0) { - result.append(", "); - } - const internal::NullableValueContent child = - container->element(child_index); - - const Type* child_type = container_type->GetElementType(child_index); - result.append(container_type->GetFormatElementPrefix( - child_index, child.is_null(), options)); - ++child_index; - if (!child.is_null() && (child_type->kind() == TYPE_STRUCT || - child_type->kind() == TYPE_ARRAY)) { - stack.top().next_child_index = child_index; - stack.push(Entry{child.value_content(), - static_cast(child_type), 0}); - break; - } - - std::string element_str = - FormatNullableValueContent(child, child_type, options); - if (options.mode == Type::FormatValueContentOptions::Mode::kDebug && - options.verbose) { - element_str = - child_type->AddCapitalizedTypePrefix(element_str, child.is_null()); - } - result.append(element_str); - } - } - return result; -} - -} // namespace zetasql diff --git a/zetasql/public/types/list_backed_type.h b/zetasql/public/types/list_backed_type.h index 385f64e38..4467b1525 100644 --- a/zetasql/public/types/list_backed_type.h +++ b/zetasql/public/types/list_backed_type.h @@ -37,35 +37,6 @@ class ListBackedType : public ContainerType { ListBackedType(const TypeFactory* factory, TypeKind kind) : ContainerType(factory, kind) {} - // FormatValueContent is defined for ContainerType. - // Instead of providing implementation of FormatValueContent method, child - // classes need to provide implementations of formatting methods defined - // below. - std::string FormatValueContent( - const ValueContent& value, - const FormatValueContentOptions& options) const override; - - // When formatting a value of a container type, how should the content start? - // E.g. array may start with "[", and struct with "{" - virtual std::string GetFormatPrefix( - const ValueContent& value_content, - const FormatValueContentOptions& options) const = 0; - - // When formatting a value of a container type, how should the content end? - // E.g. array may end with "]", and struct with "}" - virtual char GetFormatClosingCharacter( - const FormatValueContentOptions& options) const = 0; - - // Get type of the index-th element of the container - virtual const Type* GetElementType(int index) const = 0; - - // When formatting a value of a container type, should the element have a - // prefix? E.g. when formatting a struct we may want to prepend each element - // with the name of the struct field: "{foo: 1, bar: 2}" - virtual std::string GetFormatElementPrefix( - int index, bool is_null, - const FormatValueContentOptions& options) const = 0; - friend struct MultisetValueContentContainerElementHasher; friend struct HashableValueContentContainerElementIgnoringFloat; }; diff --git a/zetasql/public/types/map_type.cc b/zetasql/public/types/map_type.cc index 75e446e0c..43876692c 100644 --- a/zetasql/public/types/map_type.cc +++ b/zetasql/public/types/map_type.cc @@ -413,7 +413,14 @@ std::string MapType::FormatValueContent( return "Map printing not yet implemented."; } - std::string result = "{"; + std::string result; + + if (options.verbose) { + absl::StrAppend(&result, "Map{"); + } else { + result = "{"; + } + internal::ValueContentMap* value_content_map = value.GetAs()->value(); @@ -422,10 +429,12 @@ std::string MapType::FormatValueContent( *value_content_map, ", ", [options, this](std::string* out, const auto& map_entry) { auto& [key, value] = map_entry; - std::string key_str = FormatNullableValueContent( - key, this->key_type_, options); - std::string value_str = FormatNullableValueContent( - value, this->value_type_, options); + std::string key_str = + DebugFormatNullableValueContentForContainer( + key, this->key_type_, options); + std::string value_str = + DebugFormatNullableValueContentForContainer( + value, this->value_type_, options); absl::StrAppend(out, key_str, ": ", value_str); })); absl::StrAppend(&result, "}"); diff --git a/zetasql/public/types/map_type_test.cc b/zetasql/public/types/map_type_test.cc index e5f9790c0..29500f7cf 100644 --- a/zetasql/public/types/map_type_test.cc +++ b/zetasql/public/types/map_type_test.cc @@ -57,8 +57,14 @@ using zetasql_base::testing::IsOkAndHolds; using zetasql_base::testing::StatusIs; using MapTestAllSimpleTypes = testing::TestWithParam; +struct MapTestFormatValueContentDebugModeTestCase { + Value value; + std::string expected_debug_string; + std::string expected_verbose_debug_string; +}; + using MapTestFormatValueContentDebugMode = - testing::TestWithParam>; + testing::TestWithParam; } // namespace @@ -363,25 +369,30 @@ TEST(MapTest, MapTypeWithEnumValid) { INSTANTIATE_TEST_SUITE_P( MapTest, MapTestFormatValueContentDebugMode, - testing::ValuesIn(std::initializer_list>{ + testing::ValuesIn(std::initializer_list< + MapTestFormatValueContentDebugModeTestCase>{ { test_values::Map({{"a", true}}), R"({"a": true})", + R"(Map{String("a"): Bool(true)})", }, { test_values::Map( {{"a", true}, {"b", false}, {"c", Value::NullBool()}}), R"({"a": true, "b": false, "c": NULL})", + R"(Map{String("a"): Bool(true), String("b"): Bool(false), String("c"): Bool(NULL)})", }, { test_values::Map({{"foobar", Value::Int32(1)}, {"zoobar", Value::Int32(2)}}), R"({"foobar": 1, "zoobar": 2})", + R"(Map{String("foobar"): Int32(1), String("zoobar"): Int32(2)})", }, { test_values::Map({{"a", test_values::Array({Value::Int32(1), Value::Int32(2)})}}), - R"({"a": Array[Int32(1), Int32(2)]})", + R"({"a": [1, 2]})", + R"(Map{String("a"): Array[Int32(1), Int32(2)]})", }, { test_values::Map( @@ -391,6 +402,7 @@ INSTANTIATE_TEST_SUITE_P( {{"b", test_values::Map( {{"c", Value::Int32(1)}})}})}})}}), R"({"nested": {"a": {"b": {"c": 1}}}})", + R"(Map{String("nested"): Map{String("a"): Map{String("b"): Map{String("c"): Int32(1)}}}})", }, { test_values::Map( @@ -400,20 +412,27 @@ INSTANTIATE_TEST_SUITE_P( test_values::Map( {{"a", test_values::Map( {{"b", Value::Int32(1)}})}})}})}}), - R"({"nested": Struct{field:Map>({"a": {"b": 1}})}})", + R"({"nested": {field:{"a": {"b": 1}}}})", + R"(Map{String("nested"): Struct{field:Map{String("a"): Map{String("b"): Int32(1)}}}})", }, })); TEST_P(MapTestFormatValueContentDebugMode, FormatValueContentDebugMode) { - auto& [map_value, expected_format_str] = GetParam(); + auto& [map_value, expected_format_str, expected_verbose_format_str] = + GetParam(); Type::FormatValueContentOptions options; options.mode = Type::FormatValueContentOptions::Mode::kDebug; - options.verbose = true; + options.verbose = false; EXPECT_EQ( map_value.type()->FormatValueContent(map_value.GetContent(), options), expected_format_str); + + options.verbose = true; + EXPECT_EQ( + map_value.type()->FormatValueContent(map_value.GetContent(), options), + expected_verbose_format_str); } TEST(MapTest, FormatValueContentDebugModeEmptyMap) { @@ -421,15 +440,20 @@ TEST(MapTest, FormatValueContentDebugModeEmptyMap) { Type::FormatValueContentOptions options; options.mode = Type::FormatValueContentOptions::Mode::kDebug; - options.verbose = true; ZETASQL_ASSERT_OK_AND_ASSIGN( const Type* map_type, factory.MakeMapType(factory.get_string(), factory.get_int64())); ZETASQL_ASSERT_OK_AND_ASSIGN(Value map_value, Value::MakeMap(map_type, {})); + + options.verbose = false; EXPECT_EQ(map_type->FormatValueContent(map_value.GetContent(), options), "{}"); + + options.verbose = true; + EXPECT_EQ(map_type->FormatValueContent(map_value.GetContent(), options), + "Map{}"); } TEST(MapTest, MakeMapWithLanguageOptions) { diff --git a/zetasql/public/types/range_type.cc b/zetasql/public/types/range_type.cc index 4bf9bb793..a9483453d 100644 --- a/zetasql/public/types/range_type.cc +++ b/zetasql/public/types/range_type.cc @@ -21,6 +21,7 @@ #include "zetasql/base/logging.h" #include "zetasql/common/errors.h" +#include "zetasql/common/thread_stack.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/strings.h" @@ -83,8 +84,6 @@ bool RangeType::IsSupportedType(const LanguageOptions& language_options) const { element_type_->IsSupportedType(language_options); } -const Type* RangeType::GetElementType(int i) const { return element_type(); } - RangeType::RangeType(const TypeFactory* factory, const Type* element_type) : ListBackedType(factory, TYPE_RANGE), element_type_(element_type) { // Also blocked in TypeFactory::MakeRangeType. @@ -190,6 +189,10 @@ std::string RangeType::FormatNullableValueContent( std::string RangeType::FormatValueContent( const ValueContent& value, const Type::FormatValueContentOptions& options) const { + if (!ThreadHasEnoughStack()) { + return std::string(kFormatValueContentOutOfStackError); + } + const internal::ValueContentOrderedList* container = value.GetAs()->value(); const internal::NullableValueContent& start = container->element(0); @@ -199,6 +202,10 @@ std::string RangeType::FormatValueContent( absl::StrCat("[", FormatNullableValueContent(start, options), ", ", FormatNullableValueContent(end, options), ")"); if (options.mode == Type::FormatValueContentOptions::Mode::kDebug) { + if (options.verbose) { + return absl::StrCat("Range", boundaries); + } + return boundaries; } return absl::StrCat(TypeName(options.product_mode), " ", diff --git a/zetasql/public/types/range_type.h b/zetasql/public/types/range_type.h index 2803b6498..4d9969b89 100644 --- a/zetasql/public/types/range_type.h +++ b/zetasql/public/types/range_type.h @@ -88,27 +88,9 @@ class RangeType : public ListBackedType { return sizeof(*this); } - std::string GetFormatPrefix( - const ValueContent& value_content, - const Type::FormatValueContentOptions& options) const override { - if (options.mode == Type::FormatValueContentOptions::Mode::kDebug) { - return "Range("; - } - return absl::StrCat(TypeName(options.product_mode), "["); - } - - char GetFormatClosingCharacter( - const Type::FormatValueContentOptions& options) const override { - return ')'; - } - - const Type* GetElementType(int index) const override; - - std::string GetFormatElementPrefix( - const int index, const bool is_null, - const FormatValueContentOptions& options) const override { - return ""; - } + std::string FormatValueContent( + const ValueContent& value, + const FormatValueContentOptions& options) const override; private: RangeType(const TypeFactory* factory, const Type* element_type); @@ -150,9 +132,6 @@ class RangeType : public ListBackedType { std::string FormatNullableValueContent( const internal::NullableValueContent& element, const Type::FormatValueContentOptions& options) const; - std::string FormatValueContent( - const ValueContent& value, - const FormatValueContentOptions& options) const override; bool ValueContentEquals( const ValueContent& x, const ValueContent& y, const ValueEqualityCheckOptions& options) const override; diff --git a/zetasql/public/types/struct_type.cc b/zetasql/public/types/struct_type.cc index 968b60511..92327d245 100644 --- a/zetasql/public/types/struct_type.cc +++ b/zetasql/public/types/struct_type.cc @@ -29,6 +29,7 @@ #include "zetasql/base/logging.h" #include "zetasql/common/errors.h" +#include "zetasql/common/thread_stack.h" #include "zetasql/common/unicode_utils.h" #include "zetasql/public/language_options.h" #include "zetasql/public/options.pb.h" @@ -538,52 +539,73 @@ absl::Status StructType::DeserializeValueContent(const ValueProto& value_proto, "its value content is maintained in the Value class"); } -std::string StructType::GetFormatPrefix( - const ValueContent& value_content, - const Type::FormatValueContentOptions& options) const { - std::string prefix; - switch (options.mode) { - case Type::FormatValueContentOptions::Mode::kDebug: - if (options.verbose) { - prefix.append(CapitalizedName()); - } - prefix.push_back('{'); - break; - case Type::FormatValueContentOptions::Mode::kSQLLiteral: - if (num_fields() <= 1) { - prefix.append("STRUCT"); - } - prefix.push_back('('); - break; - case Type::FormatValueContentOptions::Mode::kSQLExpression: - prefix.append( - TypeName(options.product_mode, options.use_external_float32)); - prefix.push_back('('); - break; +void StructType::FormatValueContentDebugModeImpl( + const internal::ValueContentOrderedList* container, + const FormatValueContentOptions& options, std::string* result) const { + if (options.verbose) { + absl::StrAppend(result, CapitalizedName()); } - return prefix; -} + absl::StrAppend(result, "{"); -char StructType::GetFormatClosingCharacter( - const Type::FormatValueContentOptions& options) const { - return options.mode == Type::FormatValueContentOptions::Mode::kDebug ? '}' - : ')'; -} + for (int i = 0; i < container->num_elements(); ++i) { + const internal::NullableValueContent& field_value_content = + container->element(i); + if (i > 0) { + absl::StrAppend(result, ", "); + } -const Type* StructType::GetElementType(int index) const { - return fields()[index].type; + if (!field(i).name.empty()) { + absl::StrAppend(result, field(i).name); + absl::StrAppend(result, ":"); + } + absl::StrAppend(result, DebugFormatNullableValueContentForContainer( + field_value_content, field(i).type, options)); + } + absl::StrAppend(result, "}"); } -std::string StructType::GetFormatElementPrefix( - const int index, const bool is_null, - const FormatValueContentOptions& options) const { - if (options.mode == FormatValueContentOptions::Mode::kDebug) { - const std::string& field_name = fields()[index].name; - if (!field_name.empty()) { - return absl::StrCat(field_name, ":"); +void StructType::FormatValueContentSqlModeImpl( + const internal::ValueContentOrderedList* container, + const FormatValueContentOptions& options, std::string* result) const { + if (options.mode == Type::FormatValueContentOptions::Mode::kSQLExpression) { + absl::StrAppend( + result, TypeName(options.product_mode, options.use_external_float32)); + } else if (num_fields() <= 1) { + absl::StrAppend(result, "STRUCT"); + } + absl::StrAppend(result, "("); + + for (int i = 0; i < container->num_elements(); ++i) { + const internal::NullableValueContent& field_value_content = + container->element(i); + if (i > 0) { + absl::StrAppend(result, ", "); } + absl::StrAppend(result, FormatNullableValueContent(field_value_content, + field(i).type, options)); + } + absl::StrAppend(result, ")"); +} + +std::string StructType::FormatValueContent( + const ValueContent& value, const FormatValueContentOptions& options) const { + if (!ThreadHasEnoughStack()) { + return std::string(kFormatValueContentOutOfStackError); + } + + const internal::ValueContentOrderedList* container = + value.GetAs()->value(); + std::string result; + + switch (options.mode) { + case Type::FormatValueContentOptions::Mode::kDebug: + FormatValueContentDebugModeImpl(container, options, &result); + return result; + case Type::FormatValueContentOptions::Mode::kSQLLiteral: + case Type::FormatValueContentOptions::Mode::kSQLExpression: + FormatValueContentSqlModeImpl(container, options, &result); + return result; } - return ""; } } // namespace zetasql diff --git a/zetasql/public/types/struct_type.h b/zetasql/public/types/struct_type.h index adc7a1fdf..32ce7f29a 100644 --- a/zetasql/public/types/struct_type.h +++ b/zetasql/public/types/struct_type.h @@ -27,6 +27,7 @@ #include "zetasql/public/type.pb.h" #include "zetasql/public/types/list_backed_type.h" #include "zetasql/public/types/type.h" +#include "zetasql/public/types/value_representations.h" #include "zetasql/base/case.h" #include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" @@ -148,17 +149,8 @@ class StructType : public ListBackedType { int64_t GetEstimatedOwnedMemoryBytesSize() const override ABSL_NO_THREAD_SAFETY_ANALYSIS; - std::string GetFormatPrefix( - const ValueContent& value_content, - const Type::FormatValueContentOptions& options) const override; - - char GetFormatClosingCharacter( - const Type::FormatValueContentOptions& options) const override; - - const Type* GetElementType(int index) const override; - - std::string GetFormatElementPrefix( - int index, bool is_null, + std::string FormatValueContent( + const ValueContent& value, const FormatValueContentOptions& options) const override; private: @@ -207,13 +199,19 @@ class StructType : public ListBackedType { ValueProto* value_proto) const override; absl::Status DeserializeValueContent(const ValueProto& value_proto, ValueContent* value) const override; + void FormatValueContentDebugModeImpl( + const internal::ValueContentOrderedList* container, + const FormatValueContentOptions& options, std::string* result) const; + void FormatValueContentSqlModeImpl( + const internal::ValueContentOrderedList* container, + const FormatValueContentOptions& options, std::string* result) const; const std::vector fields_; // The deepest nesting depth in the type tree rooted at this StructType, i.e., // the maximum nesting_depth of the field types, plus 1 for the StructType - // itself. If all fields are simple types, then this is 1. - // This field is not serialized. It is recalculated during deserialization. + // itself. If all fields are simple types, then this is 1. This field is not + // serialized. It is recalculated during deserialization. const int nesting_depth_; // Lazily built map from name to struct field index. Ambiguous lookups are diff --git a/zetasql/public/types/type.h b/zetasql/public/types/type.h index 40ceaa24a..5664d44ad 100644 --- a/zetasql/public/types/type.h +++ b/zetasql/public/types/type.h @@ -661,6 +661,12 @@ class Type { // (such as element types of arrays or field types of structs). virtual int64_t GetEstimatedOwnedMemoryBytesSize() const = 0; + // Type subclasses should return true if and only if the Type's stringified + // name is included when formatting the ValueContent in verbose debug mode. + virtual bool VerboseDebugFormatValueContentHasTypeName() const { + return false; + }; + // Formatting options that can be provided to FormatValueContent. struct FormatValueContentOptions { enum class Mode { @@ -682,6 +688,21 @@ class Type { kSQLExpression, }; + template + friend void AbslStringify(Sink& sink, Mode m) { + switch (m) { + case Mode::kDebug: + sink.Append("kDebug"); + break; + case Mode::kSQLLiteral: + sink.Append("kSQLLiteral"); + break; + case Mode::kSQLExpression: + sink.Append("kSQLExpression"); + break; + } + } + // The getters below are here mostly for historical reasons: originally // internal zetasql::Value formatting functions were using these two flags // to figure out which formatting mode is requested. New types should not diff --git a/zetasql/public/types/type_factory.cc b/zetasql/public/types/type_factory.cc index d8f81527d..d3d373518 100644 --- a/zetasql/public/types/type_factory.cc +++ b/zetasql/public/types/type_factory.cc @@ -39,6 +39,7 @@ #include "zetasql/public/functions/normalize_mode.pb.h" #include "zetasql/public/functions/range_sessionize_mode.pb.h" #include "zetasql/public/functions/rounding_mode.pb.h" +#include "zetasql/public/functions/unsupported_fields.pb.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/proto/type_annotation.pb.h" #include "zetasql/public/proto/wire_format_annotation.pb.h" @@ -1082,12 +1083,23 @@ static const EnumType* s_rounding_mode_enum_type() { const EnumType* enum_type; ZETASQL_CHECK_OK(internal::TypeFactoryHelper::MakeOpaqueEnumType( // Crash OK s_type_factory(), functions::RoundingMode_descriptor(), &enum_type, - {})); + /*catalog_name_path=*/{})); return enum_type; }(); return s_rounding_mode_enum_type; } +static const EnumType* s_unsupported_fields_enum_type() { + static const EnumType* s_unsupported_fields_enum_type = [] { + const EnumType* enum_type; + ZETASQL_CHECK_OK(internal::TypeFactoryHelper::MakeOpaqueEnumType( // Crash OK + s_type_factory(), functions::UnsupportedFields_descriptor(), &enum_type, + /*catalog_name_path=*/{})); + return enum_type; + }(); + return s_unsupported_fields_enum_type; +} + static const Type* s_uuid_type() { static const Type* s_uuid_type = new SimpleType(s_type_factory(), TYPE_UUID); return s_uuid_type; @@ -1098,7 +1110,7 @@ static const EnumType* GetArrayFindModeEnumType() { const EnumType* enum_type; ZETASQL_CHECK_OK(internal::TypeFactoryHelper::MakeOpaqueEnumType( // Crash OK s_type_factory(), functions::ArrayFindEnums::ArrayFindMode_descriptor(), - &enum_type, {})); + &enum_type, /*catalog_name_path=*/{})); return enum_type; }(); return s_array_find_mode_enum_type; @@ -1110,7 +1122,7 @@ static const EnumType* GetDifferentialPrivacyReportFormatEnumType() { ZETASQL_CHECK_OK(internal::TypeFactoryHelper::MakeOpaqueEnumType( // Crash OK s_type_factory(), functions::DifferentialPrivacyEnums::ReportFormat_descriptor(), - &enum_type, {})); + &enum_type, /*catalog_name_path=*/{})); return enum_type; }(); return s_differential_privacy_report_format_enum_type; @@ -1124,7 +1136,7 @@ static const EnumType* GetDifferentialPrivacyGroupSelectionStrategyEnumType() { s_type_factory(), functions::DifferentialPrivacyEnums:: GroupSelectionStrategy_descriptor(), - &enum_type, {})); + &enum_type, /*catalog_name_path=*/{})); return enum_type; }(); return s_differential_privacy_group_selection_strategy_enum_type; @@ -1136,7 +1148,7 @@ static const EnumType* GetRangeSessionizeModeEnumType() { ZETASQL_CHECK_OK(internal::TypeFactoryHelper::MakeOpaqueEnumType( // Crash OK s_type_factory(), functions::RangeSessionizeEnums::RangeSessionizeMode_descriptor(), - &enum_type, {})); + &enum_type, /*catalog_name_path=*/{})); return enum_type; }(); return s_range_sessionize_option_enum_type; @@ -1325,6 +1337,9 @@ const EnumType* DifferentialPrivacyGroupSelectionStrategyEnumType() { const EnumType* RangeSessionizeModeEnumType() { return GetRangeSessionizeModeEnumType(); } +const EnumType* UnsupportedFieldsEnumType() { + return s_unsupported_fields_enum_type(); +} const Type* UuidType() { return s_uuid_type(); } const ArrayType* Int32ArrayType() { return s_int32_array_type(); } diff --git a/zetasql/public/types/type_factory.h b/zetasql/public/types/type_factory.h index a9d816f8a..e97b7fcd1 100644 --- a/zetasql/public/types/type_factory.h +++ b/zetasql/public/types/type_factory.h @@ -687,6 +687,10 @@ const EnumType* ArrayZipModeEnumType(); // This is an opaque enum type. const EnumType* RangeSessionizeModeEnumType(); +// Accessor for the ZetaSQL enum Type (functions::UnsupportedFields) +// that represents how does TO_JSON handle fields of unsupported types. +const EnumType* UnsupportedFieldsEnumType(); + // Return a type of 'type_kind' if 'type_kind' is a simple type, otherwise // returns nullptr. This is similar to TypeFactory::MakeSimpleType, but doesn't // require TypeFactory. diff --git a/zetasql/public/types/value_representations.h b/zetasql/public/types/value_representations.h index ade39282e..9ab45f7fb 100644 --- a/zetasql/public/types/value_representations.h +++ b/zetasql/public/types/value_representations.h @@ -33,6 +33,7 @@ #include "zetasql/public/uuid_value.h" #include "zetasql/public/value_content.h" #include "absl/strings/cord.h" +#include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "absl/types/span.h" #include "absl/types/variant.h" @@ -348,7 +349,7 @@ class JSONRef final JSONRef& operator=(const JSONRef&) = delete; // Returns the json document representation if the value is represented - // through the document object. Otherwrise, returns null. + // through the document object. Otherwise, returns null. std::optional document() { JSONValue* document = std::get_if(&value_); if (document != nullptr) { diff --git a/zetasql/public/value.h b/zetasql/public/value.h index 9d671056f..9a51b0fb9 100644 --- a/zetasql/public/value.h +++ b/zetasql/public/value.h @@ -48,6 +48,7 @@ #include "zetasql/public/value.pb.h" #include "zetasql/public/value_content.h" #include "absl/base/attributes.h" +#include "absl/base/macros.h" #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" #include "absl/status/statusor.h" diff --git a/zetasql/public/value_test.cc b/zetasql/public/value_test.cc index b3c3239a9..5e710180f 100644 --- a/zetasql/public/value_test.cc +++ b/zetasql/public/value_test.cc @@ -2431,6 +2431,73 @@ TEST_F(ValueTest, ArrayOfStructsOfStringsFormatting) { R"(ARRAY>[STRUCT("5938", "longFunctionInvocation, 2"), STRUCT("5938", "longFunctionInvocation, 2"), CAST(NULL AS STRUCT)])"); } +TEST_F(ValueTest, NestedStructContainerStringFormatting) { + Value v = Array({Struct( + {{"a", Struct({{"q", Int64(1)}})}, + {"b", Map({{Struct({{"a", Int64(1)}}), + Array({Struct({{"r", Range(Date(1), Date(2))}})})}})}})}); + + EXPECT_EQ(v.DebugString(/*verbose=*/true), + "Array[Struct{a:Struct{q:Int64(1)}, b:Map{Struct{a:Int64(1)}: " + "Array[Struct{r:Range[Date(1970-01-02), Date(1970-01-03))}]}}]"); + EXPECT_EQ(v.DebugString(), + R"([{a:{q:1}, b:{{a:1}: [{r:[1970-01-02, 1970-01-03)}]}}])"); + + EXPECT_EQ(v.Format(), + R"(ARRAY, + b MAP, ARRAY>>> + >> +[{ + {1}, + {{a:1}: [{r:[1970-01-02, 1970-01-03)}]} + }])"); + EXPECT_EQ(v.Format(/*print_top_level_type=*/false), + R"(ARRAY, + b MAP, ARRAY>>> + >> +[{ + {1}, + {{a:1}: [{r:[1970-01-02, 1970-01-03)}]} + }])"); + + // TODO: b/320552039 - Add test for GetSQLLiteral and GetSQL once MAP supports + // this. +} + +TEST_F(ValueTest, NestedRangeContainerStringFormatting) { + const Type* date_range_type = MakeRangeType(DateType()); + const auto* date_range_array_type = MakeArrayType(date_range_type); + const auto* struct_of_date_range_array_type = MakeStructType( + {{"a", date_range_array_type}, {"b", date_range_array_type}}); + ZETASQL_ASSERT_OK_AND_ASSIGN(const Value struct_of_date_range_array, + Value::MakeStruct(struct_of_date_range_array_type, + {Array({Range(Date(1), Date(2))}), + Value::Null(date_range_array_type)})); + EXPECT_EQ(struct_of_date_range_array.DebugString(/*verbose=*/true), + "Struct{a:Array[Range[Date(1970-01-02), Date(1970-01-03))], " + "b:Array>(NULL)}"); + EXPECT_EQ(struct_of_date_range_array.DebugString(), + R"({a:[[1970-01-02, 1970-01-03)], b:NULL})"); + EXPECT_EQ(struct_of_date_range_array.Format(), + R"(STRUCT, b ARRAY<>>{ + ARRAY>[[1970-01-02, 1970-01-03)], + ARRAY>(NULL) +})"); + EXPECT_EQ(struct_of_date_range_array.Format(/*print_top_level_type=*/false), + R"({ + ARRAY>[[1970-01-02, 1970-01-03)], + ARRAY>(NULL) +})"); + EXPECT_EQ( + struct_of_date_range_array.GetSQLLiteral(ProductMode::PRODUCT_EXTERNAL), + R"raw(([RANGE "[1970-01-02, 1970-01-03)"], NULL))raw"); + EXPECT_EQ( + struct_of_date_range_array.GetSQL(ProductMode::PRODUCT_EXTERNAL), + R"raw(STRUCT>, b ARRAY>>(ARRAY>[RANGE "[1970-01-02, 1970-01-03)"], CAST(NULL AS ARRAY>)))raw"); +} + TEST(MapValueTest, MapConstructionInitializerList) { ZETASQL_ASSERT_OK_AND_ASSIGN(const Type* map_type, MakeMapType(Int64Type(), Int64Type())); @@ -4218,7 +4285,7 @@ TEST_F(ValueTest, RangeOfDatesFormatting) { const Value range_d_regular = Range(Value::Date(300), Value::Date(301)); // Regular range EXPECT_EQ(range_d_regular.DebugString(/*verbose=*/true), - R"([Date(1970-10-28), Date(1970-10-29)))"); + "Range[Date(1970-10-28), Date(1970-10-29))"); EXPECT_EQ(range_d_regular.DebugString(), R"([1970-10-28, 1970-10-29))"); EXPECT_EQ(range_d_regular.Format(), R"(RANGE[1970-10-28, 1970-10-29))"); EXPECT_EQ(range_d_regular.Format(/*print_top_level_type=*/false), @@ -4232,7 +4299,7 @@ TEST_F(ValueTest, RangeOfDatesFormatting) { const Value range_d_unbounded_start = Range(Value::UnboundedStartDate(), Value::Date(301)); EXPECT_EQ(range_d_unbounded_start.DebugString(/*verbose=*/true), - R"([Date(NULL), Date(1970-10-29)))"); + R"(Range[Date(NULL), Date(1970-10-29)))"); EXPECT_EQ(range_d_unbounded_start.DebugString(), R"([NULL, 1970-10-29))"); EXPECT_EQ(range_d_unbounded_start.Format(), R"(RANGE[NULL, 1970-10-29))"); @@ -4247,7 +4314,7 @@ TEST_F(ValueTest, RangeOfDatesFormatting) { const Value range_d_unbounded_end = Range(Value::Date(300), Value::UnboundedEndDate()); EXPECT_EQ(range_d_unbounded_end.DebugString(/*verbose=*/true), - R"([Date(1970-10-28), Date(NULL)))"); + R"(Range[Date(1970-10-28), Date(NULL)))"); EXPECT_EQ(range_d_unbounded_end.DebugString(), R"([1970-10-28, NULL))"); EXPECT_EQ(range_d_unbounded_end.Format(), R"(RANGE[1970-10-28, NULL))"); EXPECT_EQ(range_d_unbounded_end.Format(/*print_top_level_type=*/false), @@ -4261,7 +4328,7 @@ TEST_F(ValueTest, RangeOfDatesFormatting) { const Value range_d_unbounded_all = Range(Value::UnboundedStartDate(), Value::UnboundedEndDate()); EXPECT_EQ(range_d_unbounded_all.DebugString(/*verbose=*/true), - R"([Date(NULL), Date(NULL)))"); + R"(Range[Date(NULL), Date(NULL)))"); EXPECT_EQ(range_d_unbounded_all.DebugString(), R"([NULL, NULL))"); EXPECT_EQ(range_d_unbounded_all.Format(), R"(RANGE[NULL, NULL))"); EXPECT_EQ(range_d_unbounded_all.Format(/*print_top_level_type=*/false), @@ -4280,9 +4347,9 @@ TEST_F(ValueTest, RangeOfDatetimesFormatting) { DatetimeValue::FromYMDHMSAndNanos(2022, 9, 13, 16, 37, 11, 1))); // Regular range - EXPECT_EQ( - range_dt_regular.DebugString(/*verbose=*/true), - R"([Datetime(2022-09-13 16:36:11.000000001), Datetime(2022-09-13 16:37:11.000000001)))"); + EXPECT_EQ(range_dt_regular.DebugString(/*verbose=*/true), + "Range[Datetime(2022-09-13 16:36:11.000000001), " + "Datetime(2022-09-13 16:37:11.000000001))"); EXPECT_EQ( range_dt_regular.DebugString(), R"([2022-09-13 16:36:11.000000001, 2022-09-13 16:37:11.000000001))"); @@ -4309,7 +4376,7 @@ TEST_F(ValueTest, RangeOfDatetimesFormatting) { Value::Datetime( DatetimeValue::FromYMDHMSAndNanos(2022, 9, 13, 16, 37, 11, 1))); EXPECT_EQ(range_dt_unbounded_start.DebugString(/*verbose=*/true), - R"([Datetime(NULL), Datetime(2022-09-13 16:37:11.000000001)))"); + "Range[Datetime(NULL), Datetime(2022-09-13 16:37:11.000000001))"); EXPECT_EQ(range_dt_unbounded_start.DebugString(), R"([NULL, 2022-09-13 16:37:11.000000001))"); EXPECT_EQ(range_dt_unbounded_start.Format(), @@ -4335,7 +4402,7 @@ TEST_F(ValueTest, RangeOfDatetimesFormatting) { DatetimeValue::FromYMDHMSAndNanos(2022, 9, 13, 16, 36, 11, 1)), Value::UnboundedEndDatetime()); EXPECT_EQ(range_dt_unbounded_end.DebugString(/*verbose=*/true), - R"([Datetime(2022-09-13 16:36:11.000000001), Datetime(NULL)))"); + "Range[Datetime(2022-09-13 16:36:11.000000001), Datetime(NULL))"); EXPECT_EQ(range_dt_unbounded_end.DebugString(), R"([2022-09-13 16:36:11.000000001, NULL))"); EXPECT_EQ(range_dt_unbounded_end.Format(), @@ -4359,7 +4426,7 @@ TEST_F(ValueTest, RangeOfDatetimesFormatting) { const Value range_dt_unbounded_all = Range(Value::UnboundedStartDatetime(), Value::UnboundedEndDatetime()); EXPECT_EQ(range_dt_unbounded_all.DebugString(/*verbose=*/true), - R"([Datetime(NULL), Datetime(NULL)))"); + "Range[Datetime(NULL), Datetime(NULL))"); EXPECT_EQ(range_dt_unbounded_all.DebugString(), R"([NULL, NULL))"); EXPECT_EQ(range_dt_unbounded_all.Format(), R"(RANGE[NULL, NULL))"); EXPECT_EQ(range_dt_unbounded_all.Format(/*print_top_level_type=*/false), @@ -4381,9 +4448,9 @@ TEST_F(ValueTest, FormatRangeOfTimestamps) { // Regular range const Value range_ts_regular = Range(Value::Timestamp(tmin), Value::Timestamp(tmax)); - EXPECT_EQ( - range_ts_regular.DebugString(/*verbose=*/true), - R"([Timestamp(0001-01-01 00:00:00+00), Timestamp(9999-12-31 23:59:59.999999999+00)))"); + EXPECT_EQ(range_ts_regular.DebugString(/*verbose=*/true), + "Range[Timestamp(0001-01-01 00:00:00+00), Timestamp(9999-12-31 " + "23:59:59.999999999+00))"); EXPECT_EQ(range_ts_regular.DebugString(), R"([0001-01-01 00:00:00+00, 9999-12-31 23:59:59.999999999+00))"); EXPECT_EQ(range_ts_regular.Format(), @@ -4408,7 +4475,7 @@ TEST_F(ValueTest, FormatRangeOfTimestamps) { Range(Value::UnboundedStartTimestamp(), Value::Timestamp(tmax)); EXPECT_EQ( range_ts_unbounded_start.DebugString(/*verbose=*/true), - R"([Timestamp(NULL), Timestamp(9999-12-31 23:59:59.999999999+00)))"); + "Range[Timestamp(NULL), Timestamp(9999-12-31 23:59:59.999999999+00))"); EXPECT_EQ(range_ts_unbounded_start.DebugString(), R"([NULL, 9999-12-31 23:59:59.999999999+00))"); EXPECT_EQ(range_ts_unbounded_start.Format(), @@ -4432,7 +4499,7 @@ TEST_F(ValueTest, FormatRangeOfTimestamps) { const Value range_ts_unbounded_end = Range(Value::Timestamp(tmin), Value::UnboundedEndTimestamp()); EXPECT_EQ(range_ts_unbounded_end.DebugString(/*verbose=*/true), - R"([Timestamp(0001-01-01 00:00:00+00), Timestamp(NULL)))"); + "Range[Timestamp(0001-01-01 00:00:00+00), Timestamp(NULL))"); EXPECT_EQ(range_ts_unbounded_end.DebugString(), R"([0001-01-01 00:00:00+00, NULL))"); EXPECT_EQ(range_ts_unbounded_end.Format(), @@ -4454,7 +4521,7 @@ TEST_F(ValueTest, FormatRangeOfTimestamps) { const Value range_ts_unbounded_all = Range(Value::UnboundedStartTimestamp(), Value::UnboundedEndTimestamp()); EXPECT_EQ(range_ts_unbounded_all.DebugString(/*verbose=*/true), - R"([Timestamp(NULL), Timestamp(NULL)))"); + "Range[Timestamp(NULL), Timestamp(NULL))"); EXPECT_EQ(range_ts_unbounded_all.DebugString(), R"([NULL, NULL))"); EXPECT_EQ(range_ts_unbounded_all.Format(), R"(RANGE[NULL, NULL))"); EXPECT_EQ(range_ts_unbounded_all.Format(/*print_top_level_type=*/false), @@ -4475,9 +4542,9 @@ TEST_F(ValueTest, FormatArrayOfRanges) { const Value array_of_ranges, Value::MakeArray(range_array_type, {regular_range, range_unbounded_start, range_null})); - EXPECT_EQ( - array_of_ranges.DebugString(/*verbose=*/true), - R"(Array[Range([Date(1970-10-28), Date(1970-10-29))), Range([Date(NULL), Date(1970-01-23))), Range(NULL)])"); + EXPECT_EQ(array_of_ranges.DebugString(/*verbose=*/true), + "Array[Range[Date(1970-10-28), Date(1970-10-29)), " + "Range[Date(NULL), Date(1970-01-23)), Range(NULL)]"); EXPECT_EQ(array_of_ranges.DebugString(), R"([[1970-10-28, 1970-10-29), [NULL, 1970-01-23), NULL])"); EXPECT_EQ(array_of_ranges.Format(), R"(ARRAY>[ @@ -4514,9 +4581,10 @@ TEST_F(ValueTest, FormatStructOfRanges) { const Value struct_of_ranges, Value::MakeStruct(range_struct_type, {range_of_dates, range_of_datetimes, range_of_timestamps})); - EXPECT_EQ( - struct_of_ranges.DebugString(/*verbose=*/true), - R"(Struct{d:Range([Date(1970-10-28), Date(1970-10-29))), dt:Range([Datetime(NULL), Datetime(2022-09-13 16:37:11.000000001))), t:Range(NULL)})"); + EXPECT_EQ(struct_of_ranges.DebugString(/*verbose=*/true), + "Struct{d:Range[Date(1970-10-28), Date(1970-10-29)), " + "dt:Range[Datetime(NULL), Datetime(2022-09-13 " + "16:37:11.000000001)), t:Range(NULL)}"); EXPECT_EQ( struct_of_ranges.DebugString(), R"({d:[1970-10-28, 1970-10-29), dt:[NULL, 2022-09-13 16:37:11.000000001), t:NULL})"); @@ -6112,7 +6180,7 @@ static void StackOverflowTest() { } (void)new_type->DebugString(); - (void)new_array_type->DebugString(true /* detailed */); + (void)new_array_type->DebugString(/*details=*/true); v = values::Struct(new_type, {v}); v = values::Array(new_array_type, {v}); @@ -6120,17 +6188,18 @@ static void StackOverflowTest() { } std::string s = v.DebugString(); ABSL_LOG(INFO) << s; - EXPECT_GT(s.size(), 100) << s; if (ZETASQL_DEBUG_MODE) { - EXPECT_THAT(s, HasSubstr("[{field:[{field:[{field:[{field:5}]}]}]}]")); + EXPECT_THAT(s, AnyOf(HasSubstr("[{field:[{field:[{field:[{field:5}]}]}]}]"), + HasSubstr("... "))); } - s = v.DebugString(true /* verbose */); + s = v.DebugString(/*verbose=*/true); ABSL_LOG(INFO) << s; - EXPECT_GT(s.size(), 200) << s; if (ZETASQL_DEBUG_MODE) { EXPECT_THAT( - s, HasSubstr("Struct{field:Array[Struct{field:Int64(5)}]}]}]}]}]}")); + s, + AnyOf(HasSubstr("Struct{field:Array[Struct{field:Int64(5)}]}]}]}]}]}"), + HasSubstr("... "))); } } diff --git a/zetasql/reference_impl/aggregate_op.cc b/zetasql/reference_impl/aggregate_op.cc index 135fd4c3f..b237be651 100644 --- a/zetasql/reference_impl/aggregate_op.cc +++ b/zetasql/reference_impl/aggregate_op.cc @@ -16,6 +16,7 @@ // This file contains the code for evaluating aggregate functions. +#include #include #include #include @@ -628,7 +629,7 @@ class HavingExtremalValueAccumulator : public IntermediateAggregateAccumulator { use_max_ ? FunctionKind::kMax : FunctionKind::kMin, having_value.type(), /*num_input_fields=*/1, having_value.type()); auto status_or_accumulator = max_function.CreateAccumulator( - /*args=*/{}, /*collator_list=*/{}, context_); + /*args=*/{}, /*params=*/{}, /*collator_list=*/{}, context_); if (!status_or_accumulator.ok()) { *status = status_or_accumulator.status(); return false; @@ -1040,7 +1041,7 @@ AggregateArg::CreateAccumulator(absl::Span params, ZETASQL_ASSIGN_OR_RETURN( std::unique_ptr underlying_accumulator, aggregate_function()->function()->CreateAccumulator( - args, std::move(collator_list), context)); + args, params, std::move(collator_list), context)); // Adapt the underlying AggregateAccumulator to the // IntermediateAggregateAccumulator interface so that we can stack other // intermediate accumulators on top of it. @@ -1777,9 +1778,15 @@ absl::StatusOr> AggregateOp::CreateIterator( AccumulatorList& accumulators = *group_value->mutable_accumulator_list(); std::unique_ptr tuple = group_value->ConsumeKey(); - tuple->AddSlots(2 * static_cast(accumulators.size()) + - num_extra_slots); + size_t num_slots_to_add = num_extra_slots + aggregators().size(); + for (const auto& aggregator : aggregators()) { + if (aggregator->side_effects_variable().is_valid()) { + num_slots_to_add++; + } + } + tuple->AddSlots(static_cast(num_slots_to_add)); + int seen_side_effects_vars = 0; for (int i = 0; i < accumulators.size(); ++i) { AggregateArgAccumulator& accumulator = *accumulators[i].accumulator; absl::StatusOr value = @@ -1787,14 +1794,18 @@ absl::StatusOr> AggregateOp::CreateIterator( ? group_value->accumulator_error(i) : accumulator.GetFinalResult( /*inputs_in_defined_order=*/false); + bool has_side_effects_variable = + aggregators()[i]->side_effects_variable().is_valid(); if (value.ok()) { tuple->mutable_slot(grouping_key_size + i)->SetValue(value.value()); - // No side effects, so set the side effect value to NULL. - tuple - ->mutable_slot(grouping_key_size + - static_cast(accumulators.size()) + i) - ->SetValue(Value::NullBytes()); - } else if (!aggregators()[i]->side_effects_variable().is_valid() || + if (has_side_effects_variable) { + int slot_index = grouping_key_size + + static_cast(accumulators.size()) + + seen_side_effects_vars++; + // No side effects, so set the side effect value to NULL. + tuple->mutable_slot(slot_index)->SetValue(Value::NullBytes()); + } + } else if (!has_side_effects_variable || !ShouldSuppressError( value.status(), ResolvedFunctionCallBase::SAFE_ERROR_MODE)) { @@ -1809,9 +1820,10 @@ absl::StatusOr> AggregateOp::CreateIterator( // does not have literals or values defined. ::google::rpc::Status status_proto; internal::SaveStatusToProto(value.status(), &status_proto); - tuple - ->mutable_slot(grouping_key_size + - static_cast(accumulators.size()) + i) + int slot_index = grouping_key_size + + static_cast(accumulators.size()) + + seen_side_effects_vars++; + tuple->mutable_slot(slot_index) ->SetValue(Value::Bytes(status_proto.SerializeAsCord())); } } @@ -2112,27 +2124,16 @@ absl::StatusOr> GroupRowsOp::CreateIterator( } std::unique_ptr RowsForUdaOp::Create( - std::vector argument_names) { - // Using `new` here because make_unique cannot access the private - // constructor of `RowsForUdaOp`. - return absl::WrapUnique(new RowsForUdaOp(std::move(argument_names))); + std::vector arguments) { + return absl::WrapUnique(new RowsForUdaOp(std::move(arguments))); } -RowsForUdaOp::RowsForUdaOp(std::vector argument_names) { - argument_names_ = std::move(argument_names); +RowsForUdaOp::RowsForUdaOp(std::vector arguments) { + arguments_ = std::move(arguments); } std::unique_ptr RowsForUdaOp::CreateOutputSchema() const { - std::vector vars; - vars.reserve(argument_names_.size()); - for (const std::string& argument_name : argument_names_) { - vars.push_back(VariableId(argument_name)); - } - return std::make_unique(vars); -} - -std::vector RowsForUdaOp::argument_names() const { - return argument_names_; + return std::make_unique(arguments_); } absl::Status RowsForUdaOp::SetSchemasForEvaluation( diff --git a/zetasql/reference_impl/aggregate_op_test.cc b/zetasql/reference_impl/aggregate_op_test.cc index 6b4309a7b..b79f2e7c8 100644 --- a/zetasql/reference_impl/aggregate_op_test.cc +++ b/zetasql/reference_impl/aggregate_op_test.cc @@ -458,7 +458,8 @@ static absl::StatusOr EvalAgg(const BuiltinAggregateFunction& agg, EvaluationContext* context, absl::Span args = {}) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr accumulator, - agg.CreateAccumulator(args, /*collator_list=*/{}, context)); + agg.CreateAccumulator(args, /*params=*/{}, + /*collator_list=*/{}, context)); bool stop_accumulation; absl::Status status; for (const Value& value : values) { @@ -754,7 +755,7 @@ TEST(CreateIteratorTest, AggregateAll) { ASSERT_EQ(data.size(), 1); EXPECT_EQ(Tuple(&iter->Schema(), &data[0]).DebugString(), ""); // Check for the extra slot. - EXPECT_EQ(data[0].num_slots(), 7); + EXPECT_EQ(data[0].num_slots(), 4); // Do it again with cancellation before reading the only output tuple. context.ClearDeadlineAndCancellationState(); @@ -779,7 +780,7 @@ TEST(CreateIteratorTest, AggregateAll) { ASSERT_EQ(data.size(), 1); EXPECT_EQ(Tuple(&iter->Schema(), &data[0]).DebugString(), ""); // Check for the extra slot. - EXPECT_EQ(data[0].num_slots(), 7); + EXPECT_EQ(data[0].num_slots(), 4); // Check that if the memory bound is too low, we get an error. EvaluationContext memory_context(GetIntermediateMemoryEvaluationOptions( @@ -1060,8 +1061,8 @@ TEST(CreateIteratorTest, AggregateOrderBy) { "j:[NULL, 10]," "l:[\"c\", \"b\", \"a\", NULL]>"); // Check for the extra slot. - EXPECT_EQ(data[0].num_slots(), 18); - EXPECT_EQ(data[1].num_slots(), 18); + EXPECT_EQ(data[0].num_slots(), 10); + EXPECT_EQ(data[1].num_slots(), 10); // Do it again with cancellation. context.ClearDeadlineAndCancellationState(); @@ -1085,8 +1086,8 @@ TEST(CreateIteratorTest, AggregateOrderBy) { EXPECT_FALSE(iter->PreservesOrder()); ZETASQL_ASSERT_OK_AND_ASSIGN(data, ReadFromTupleIterator(iter.get())); ASSERT_EQ(data.size(), 2); - EXPECT_EQ(data[0].num_slots(), 18); - EXPECT_EQ(data[1].num_slots(), 18); + EXPECT_EQ(data[0].num_slots(), 10); + EXPECT_EQ(data[1].num_slots(), 10); // Check that if the memory bound is too low, we get an error. EvaluationContext memory_context(GetIntermediateMemoryEvaluationOptions( @@ -1344,8 +1345,8 @@ TEST(CreateIteratorTest, AggregateLimit) { "h:[\"c\", \"b\"]," "i:[\"c\", \"b\", \"a\", NULL]>"); // Check for the extra slot. - EXPECT_EQ(data[0].num_slots(), 14); - EXPECT_EQ(data[1].num_slots(), 14); + EXPECT_EQ(data[0].num_slots(), 8); + EXPECT_EQ(data[1].num_slots(), 8); // Do it again with cancellation. context.ClearDeadlineAndCancellationState(); @@ -1369,8 +1370,8 @@ TEST(CreateIteratorTest, AggregateLimit) { EXPECT_FALSE(iter->PreservesOrder()); ZETASQL_ASSERT_OK_AND_ASSIGN(data, ReadFromTupleIterator(iter.get())); ASSERT_EQ(data.size(), 2); - EXPECT_EQ(data[0].num_slots(), 14); - EXPECT_EQ(data[1].num_slots(), 14); + EXPECT_EQ(data[0].num_slots(), 8); + EXPECT_EQ(data[1].num_slots(), 8); // Check that if the memory bound is too low, we get an error. EvaluationContext memory_context(GetIntermediateMemoryEvaluationOptions( @@ -1473,8 +1474,8 @@ TEST(CreateIteratorTest, AggregateHaving) { EXPECT_EQ(Tuple(&iter->Schema(), &data[1]).DebugString(), ""); // Check for the extra slot. - EXPECT_EQ(data[0].num_slots(), 6); - EXPECT_EQ(data[1].num_slots(), 6); + EXPECT_EQ(data[0].num_slots(), 4); + EXPECT_EQ(data[1].num_slots(), 4); // Do it again with cancellation. context.ClearDeadlineAndCancellationState(); @@ -1498,8 +1499,8 @@ TEST(CreateIteratorTest, AggregateHaving) { EXPECT_FALSE(iter->PreservesOrder()); ZETASQL_ASSERT_OK_AND_ASSIGN(data, ReadFromTupleIterator(iter.get())); ASSERT_EQ(data.size(), 2); - EXPECT_EQ(data[0].num_slots(), 6); - EXPECT_EQ(data[1].num_slots(), 6); + EXPECT_EQ(data[0].num_slots(), 4); + EXPECT_EQ(data[1].num_slots(), 4); // Check that if the memory bound is too low, we get an error. EvaluationContext memory_context(GetIntermediateMemoryEvaluationOptions( diff --git a/zetasql/reference_impl/algebrizer.cc b/zetasql/reference_impl/algebrizer.cc index 2e1258ab8..ece8467c3 100644 --- a/zetasql/reference_impl/algebrizer.cc +++ b/zetasql/reference_impl/algebrizer.cc @@ -499,9 +499,7 @@ GetBuiltinFunctionSignatureIdToKindMap() { absl::StatusOr> Algebrizer::AlgebrizeFunctionCall( const ResolvedFunctionCall* function_call) { - ZETASQL_RETURN_IF_NOT_ENOUGH_STACK( - "Out of stack space due to deeply nested query expression " - "during algebrizing"); + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); std::string name = function_call->function()->FullName(/*include_group=*/false); const ResolvedFunctionCallBase::ErrorMode& error_mode = @@ -1130,12 +1128,21 @@ absl::StatusOr> Algebrizer::AlgebrizeAggregateFn( side_effects_variable); } +absl::StatusOr Algebrizer::AddUdaArgumentVariable( + absl::string_view argument_name) { + ZETASQL_RET_CHECK(!aggregate_args_map_.contains(argument_name)); + VariableId variable = + variable_gen_->GetNewVariableName(std::string(argument_name)); + aggregate_args_map_[argument_name] = variable; + return variable; +} + absl::StatusOr> Algebrizer::AlgebrizeUdaCall( const AnonymizationOptions* anonymization_options, const ResolvedExpr& function_expr, const std::vector& aggregate_exprs, const ResolvedColumnList& aggregate_expr_columns, - std::vector argument_names, + absl::Span argument_infos, const LanguageOptions& language_options, const AlgebrizerOptions& algebrizer_options, TypeFactory* type_factory) { Parameters parameters; @@ -1143,6 +1150,12 @@ absl::StatusOr> Algebrizer::AlgebrizeUdaCall( SystemVariablesAlgebrizerMap system_variables_map; Algebrizer uda_algebrizer(language_options, algebrizer_options, type_factory, ¶meters, &column_map, &system_variables_map); + std::vector variables; + for (const UdaArgumentInfo& argument_info : argument_infos) { + ZETASQL_ASSIGN_OR_RETURN(VariableId variable, uda_algebrizer.AddUdaArgumentVariable( + argument_info.argument_name)); + variables.push_back(variable); + } // Algebrize each aggregate expression and assign new variables. ZETASQL_RET_CHECK_EQ(aggregate_exprs.size(), aggregate_expr_columns.size()); @@ -1168,7 +1181,7 @@ absl::StatusOr> Algebrizer::AlgebrizeUdaCall( // the UDA. The input is a table aware of the UDA argument names that // represents the grouped rows on which to perform the aggregation. std::unique_ptr input = - RowsForUdaOp::Create(std::move(argument_names)); + RowsForUdaOp::Create(std::move(variables)); ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr agg_op, AggregateOp::Create( /*keys=*/{}, std::move(algebrized_aggregate_exprs), @@ -1209,6 +1222,7 @@ Algebrizer::CreateCallbackUserDefinedAggregateFn( absl::StatusOr> Algebrizer::CreateTemplatedUserDefinedAggregateFn( const ResolvedNonScalarFunctionCallBase* aggregate_function, + std::vector>& arguments, const AnonymizationOptions* anonymization_options) { const TemplatedSQLFunction* templated_sql_function = aggregate_function->function()->GetAs(); @@ -1233,15 +1247,18 @@ Algebrizer::CreateTemplatedUserDefinedAggregateFn( aggregate_expr_columns.push_back(agg_expr->column()); } - std::vector argument_names; - for (const std::string& arg_name : - templated_sql_function->GetArgumentNames()) { - argument_names.push_back(arg_name); - } - - std::vector argument_is_aggregate; - for (const auto& arg : aggregate_function->signature().arguments()) { - argument_is_aggregate.push_back(!arg.options().is_not_aggregate()); + ZETASQL_RET_CHECK_EQ(templated_sql_function->GetArgumentNames().size(), + aggregate_function->signature().arguments().size()); + std::vector argument_infos; + for (int i = 0; i < templated_sql_function->GetArgumentNames().size(); ++i) { + bool is_aggregate = !aggregate_function->signature() + .arguments()[i] + .options() + .is_not_aggregate(); + argument_infos.push_back( + {.argument_name = templated_sql_function->GetArgumentNames()[i], + .is_aggregate = is_aggregate, + .expr = is_aggregate ? nullptr : arguments[i].get()}); } // Create a custom UDA evaluator @@ -1251,19 +1268,18 @@ Algebrizer::CreateTemplatedUserDefinedAggregateFn( TypeFactory* type_factory = this->type_factory_; AggregateFunctionEvaluatorFactory AggregateFn = [signature, anonymization_options, function_expr, aggregate_exprs, - aggregate_expr_columns, argument_names, argument_is_aggregate, + aggregate_expr_columns, argument_infos = std::move(argument_infos), language_options, algebrizer_options, type_factory, this](const FunctionSignature& sig) -> absl::StatusOr> { ZETASQL_ASSIGN_OR_RETURN( std::unique_ptr algebrized_uda, AlgebrizeUdaCall(anonymization_options, *function_expr, aggregate_exprs, - aggregate_expr_columns, argument_names, + aggregate_expr_columns, argument_infos, language_options, algebrizer_options, type_factory)); - return MakeUserDefinedAggregateFunctionEvaluator( - std::move(algebrized_uda), std::move(argument_names), - std::move(argument_is_aggregate)); + return MakeUserDefinedAggregateFunctionEvaluator(std::move(algebrized_uda), + std::move(argument_infos)); }; const std::string name = aggregate_function->function()->FullName(false); @@ -1275,10 +1291,10 @@ Algebrizer::CreateTemplatedUserDefinedAggregateFn( absl::StatusOr> Algebrizer::CreateNonTemplatedUserDefinedAggregateFn( const ResolvedNonScalarFunctionCallBase* aggregate_function, + std::vector>& arguments, const AnonymizationOptions* anonymization_options) { const SQLFunction* sql_function = aggregate_function->function()->GetAs(); - std::vector argument_names = sql_function->GetArgumentNames(); std::vector aggregate_exprs; ResolvedColumnList aggregate_expr_columns; for (const std::unique_ptr& agg_expr : @@ -1288,9 +1304,18 @@ Algebrizer::CreateNonTemplatedUserDefinedAggregateFn( } const ResolvedExpr* function_expr = sql_function->FunctionExpression(); - std::vector argument_is_aggregate; - for (const auto& arg : aggregate_function->signature().arguments()) { - argument_is_aggregate.push_back(!arg.options().is_not_aggregate()); + ZETASQL_RET_CHECK_EQ(sql_function->GetArgumentNames().size(), + aggregate_function->signature().arguments().size()); + std::vector argument_infos; + for (int i = 0; i < sql_function->GetArgumentNames().size(); ++i) { + bool is_aggregate = !aggregate_function->signature() + .arguments()[i] + .options() + .is_not_aggregate(); + argument_infos.push_back( + {.argument_name = sql_function->GetArgumentNames()[i], + .is_aggregate = is_aggregate, + .expr = is_aggregate ? nullptr : arguments[i].get()}); } // Create a custom UDA evaluator @@ -1300,19 +1325,18 @@ Algebrizer::CreateNonTemplatedUserDefinedAggregateFn( TypeFactory* type_factory = this->type_factory_; AggregateFunctionEvaluatorFactory AggregateFn = [signature, anonymization_options, function_expr, aggregate_exprs, - aggregate_expr_columns, argument_names, argument_is_aggregate, + aggregate_expr_columns, argument_infos = std::move(argument_infos), language_options, algebrizer_options, type_factory, this](const FunctionSignature& sig) -> absl::StatusOr> { ZETASQL_ASSIGN_OR_RETURN( std::unique_ptr algebrized_uda, AlgebrizeUdaCall(anonymization_options, *function_expr, aggregate_exprs, - aggregate_expr_columns, argument_names, + aggregate_expr_columns, argument_infos, language_options, algebrizer_options, type_factory)); - return MakeUserDefinedAggregateFunctionEvaluator( - std::move(algebrized_uda), std::move(argument_names), - std::move(argument_is_aggregate)); + return MakeUserDefinedAggregateFunctionEvaluator(std::move(algebrized_uda), + std::move(argument_infos)); }; const std::string name = aggregate_function->function()->FullName(false); @@ -1324,6 +1348,7 @@ Algebrizer::CreateNonTemplatedUserDefinedAggregateFn( absl::StatusOr> Algebrizer::CreateUserDefinedAggregateFn( const ResolvedNonScalarFunctionCallBase* aggregate_function, + std::vector>& arguments, const AnonymizationOptions* anonymization_options) { if (aggregate_function->function()->GetAggregateFunctionEvaluatorFactory() != nullptr) { @@ -1331,11 +1356,11 @@ Algebrizer::CreateUserDefinedAggregateFn( anonymization_options); } if (aggregate_function->function()->Is()) { - return CreateNonTemplatedUserDefinedAggregateFn(aggregate_function, - anonymization_options); + return CreateNonTemplatedUserDefinedAggregateFn( + aggregate_function, arguments, anonymization_options); } if (aggregate_function->function()->Is()) { - return CreateTemplatedUserDefinedAggregateFn(aggregate_function, + return CreateTemplatedUserDefinedAggregateFn(aggregate_function, arguments, anonymization_options); } return ::zetasql_base::InvalidArgumentErrorBuilder() @@ -1488,9 +1513,9 @@ Algebrizer::AlgebrizeAggregateFnWithAlgebrizedArguments( if (anonymization_options.has_value()) { anonymization_options_ptr = &(*anonymization_options); } - ZETASQL_ASSIGN_OR_RETURN(function, - CreateUserDefinedAggregateFn(aggregate_function, - anonymization_options_ptr)); + ZETASQL_ASSIGN_OR_RETURN( + function, CreateUserDefinedAggregateFn(aggregate_function, arguments, + anonymization_options_ptr)); break; } case FunctionKind::kCorr: @@ -1676,20 +1701,21 @@ absl::StatusOr> Algebrizer::AlgebrizeGetProtoField( std::unique_ptr last_get_proto_field_expr; bool first = true; for (const ResolvedGetProtoField* get_proto_field : proto_field_path) { - ZETASQL_ASSIGN_OR_RETURN(ProtoFieldRegistry * registry, - AddProtoFieldRegistry(/*id=*/std::nullopt)); - + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr registry, + MakeProtoFieldRegistry(/*id=*/std::nullopt)); ZETASQL_ASSIGN_OR_RETURN( - const ProtoFieldReader* field_reader, - AddProtoFieldReader( + std::unique_ptr field_reader, + MakeProtoFieldReader( /*id=*/std::nullopt, CreateProtoFieldAccessInfo(*get_proto_field), - registry)); + registry.get())); ZETASQL_ASSIGN_OR_RETURN( last_get_proto_field_expr, GetProtoFieldExpr::Create(first ? std::move(base_value_expr) : std::move(last_get_proto_field_expr), - field_reader)); + field_reader.get())); + last_get_proto_field_expr->set_owned_reader(std::move(field_reader), + std::move(registry)); first = false; } return last_get_proto_field_expr; @@ -1755,9 +1781,11 @@ Algebrizer::AlgebrizeGetProtoFieldOfPath( std::get(element); ProtoFieldRegistry* registry = zetasql_base::FindPtrOrNull(proto_field_registry_map_, column_and_field_path); + std::unique_ptr owned_registry; if (registry == nullptr) { - ZETASQL_ASSIGN_OR_RETURN(registry, - AddProtoFieldRegistry(column_and_field_path)); + ZETASQL_ASSIGN_OR_RETURN(owned_registry, + MakeProtoFieldRegistry(column_and_field_path)); + registry = owned_registry.get(); } ProtoFieldAccessInfo access_info = @@ -1771,13 +1799,19 @@ Algebrizer::AlgebrizeGetProtoFieldOfPath( ? nullptr : zetasql_base::FindPtrOrNull(get_proto_field_reader_map_, column_and_field_path); + std::unique_ptr owned_reader; if (reader == nullptr) { - ZETASQL_ASSIGN_OR_RETURN(reader, AddProtoFieldReader(column_and_field_path, - access_info, registry)); + ZETASQL_ASSIGN_OR_RETURN( + owned_reader, + MakeProtoFieldReader(column_and_field_path, access_info, registry)); + reader = owned_reader.get(); } - ZETASQL_ASSIGN_OR_RETURN(base_expr, + ZETASQL_ASSIGN_OR_RETURN(auto new_proto_field_expr, GetProtoFieldExpr::Create(std::move(base_expr), reader)); + new_proto_field_expr->set_owned_reader(std::move(owned_reader), + std::move(owned_registry)); + base_expr = std::move(new_proto_field_expr); } else { ZETASQL_RET_CHECK(std::holds_alternative(element)); const ResolvedGetStructField* get_struct_field = @@ -2052,14 +2086,12 @@ Algebrizer::AlgebrizeStandaloneExpression(const ResolvedExpr* expr) { // Sanity check - WITH map should be cleared as WITH clauses go out of scope. ZETASQL_RET_CHECK(with_map_.empty()); - return WrapWithRootExpr(std::move(value_expr)); + return std::move(value_expr); } absl::StatusOr> Algebrizer::AlgebrizeExpression( const ResolvedExpr* expr) { - ZETASQL_RETURN_IF_NOT_ENOUGH_STACK( - "Out of stack space due to deeply nested query expression " - "during algebrizing"); + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); if (!expr->type()->IsSupportedType(language_options_)) { return ::zetasql_base::InvalidArgumentErrorBuilder() @@ -2293,8 +2325,8 @@ absl::StatusOr> Algebrizer::AlgebrizeExpression( if (argument_ref->argument_kind() == ResolvedArgumentDef::AGGREGATE) { // AGGREGATE arguments in a SQL UDA ZETASQL_ASSIGN_OR_RETURN( - val_op, - DerefExpr::Create(VariableId(argument_ref->name()), expr->type())); + val_op, DerefExpr::Create(aggregate_args_map_[argument_ref->name()], + expr->type())); break; } else { // SCALAR and NOT_AGGREGATE arguments @@ -2940,6 +2972,7 @@ absl::StatusOr> Algebrizer::AlgebrizeWithRefScan( absl::StatusOr> Algebrizer::AlgebrizeArrayScan( const ResolvedArrayScan* array_scan, std::vector* active_conjuncts) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); if (array_scan->input_scan() == nullptr) { ZETASQL_RET_CHECK(array_scan->join_expr() == nullptr); return AlgebrizeArrayScanWithoutJoin(array_scan, active_conjuncts); @@ -3648,7 +3681,7 @@ absl::StatusOr> Algebrizer::AlgebrizeSampleScan( }; ZETASQL_ASSIGN_OR_RETURN(SampleScanOp::Method method, algebrize_method()); - // Algebrize the size, which represents the % likelyhood to include the row if + // Algebrize the size, which represents the % likelihood to include the row if // using BERNOULLI or the # of rows if using RESERVOIR. ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr size, AlgebrizeExpression(sample_scan->size())); @@ -5186,15 +5219,15 @@ Algebrizer::AlgebrizeExceptIntersectScan( } } -absl::StatusOr> Algebrizer::AlgebrizeProjectScan( - const ResolvedProjectScan* resolved_project, +absl::StatusOr> +Algebrizer::AlgebrizeProjectScanInternal( + const ResolvedColumnList& column_list, + absl::Span> expr_list, + const ResolvedScan* input_scan, bool is_ordered, std::vector* active_conjuncts) { RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); // Determine the new columns and their definitions. absl::flat_hash_set defined_columns; - const std::vector>& expr_list = - resolved_project->expr_list(); - const ResolvedColumnList& column_list = resolved_project->column_list(); ZETASQL_RET_CHECK(!column_list.empty()); std::vector> defined_columns_and_exprs; @@ -5218,9 +5251,8 @@ absl::StatusOr> Algebrizer::AlgebrizeProjectScan( input_active_conjuncts.push_back(info); } } - ZETASQL_ASSIGN_OR_RETURN( - std::unique_ptr input, - AlgebrizeScan(resolved_project->input_scan(), &input_active_conjuncts)); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input, + AlgebrizeScan(input_scan, &input_active_conjuncts)); // Assign variables to the new columns and algebrize their definitions. std::vector> arguments; @@ -5245,15 +5277,26 @@ absl::StatusOr> Algebrizer::AlgebrizeProjectScan( // Since 'arguments' is empty we drop the project, but that project might // destroy order so in that case we must update the ordered property of the // relation. - ZETASQL_RETURN_IF_ERROR( - input->set_is_order_preserving(resolved_project->is_ordered())); + ZETASQL_RETURN_IF_ERROR(input->set_is_order_preserving(is_ordered)); return input; } } +absl::StatusOr> Algebrizer::AlgebrizeProjectScan( + const ResolvedProjectScan* resolved_project, + std::vector* active_conjuncts) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + return AlgebrizeProjectScanInternal( + resolved_project->column_list(), resolved_project->expr_list(), + resolved_project->input_scan(), resolved_project->is_ordered(), + active_conjuncts); +} + absl::StatusOr> Algebrizer::AlgebrizeOrderByScan( const ResolvedOrderByScan* scan, std::unique_ptr limit, std::unique_ptr offset) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + ZETASQL_RET_CHECK_EQ(limit == nullptr, offset == nullptr); ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input, @@ -5494,6 +5537,8 @@ absl::StatusOr> Algebrizer::AlgebrizeScanImpl( return AlgebrizeGroupRowsScan(scan->GetAs()); case RESOLVED_TVFSCAN: return AlgebrizeTvfScan(scan->GetAs()); + case RESOLVED_ASSERT_SCAN: + return AlgebrizeAssertScan(scan->GetAs()); case RESOLVED_BARRIER_SCAN: return AlgebrizeBarrierScan(scan->GetAs()); default: @@ -5636,23 +5681,7 @@ Algebrizer::AlgebrizeRootScanAsValueExpr( // Sanity check - WITH map should be cleared as WITH clauses go out of scope. ZETASQL_RET_CHECK(with_map_.empty()); - return WrapWithRootExpr(std::move(value)); -} - -absl::StatusOr> Algebrizer::WrapWithRootExpr( - std::unique_ptr value_expr) { - return RootExpr::Create(std::move(value_expr), GetRootData()); -} - -std::unique_ptr Algebrizer::GetRootData() { - auto root_data = std::make_unique(); - root_data->registries = std::move(proto_field_registries_); - root_data->field_readers = std::move(get_proto_field_readers_); - - proto_field_registries_.clear(); - get_proto_field_readers_.clear(); - - return root_data; + return std::move(value); } absl::StatusOr> @@ -5693,11 +5722,8 @@ Algebrizer::AlgebrizeQueryStatementAsRelation( // Sanity check - WITH map should be cleared as WITH clauses go out of scope. ZETASQL_RET_CHECK(with_map_.empty()); - ZETASQL_ASSIGN_OR_RETURN(relation, - RootOp::Create(std::move(relation), GetRootData())); - ZETASQL_VLOG(2) << "Algebrized tree:\n" << relation->DebugString(true); - return relation; + return std::move(relation); } absl::StatusOr> Algebrizer::AlgebrizeDMLStatement( @@ -5789,7 +5815,7 @@ absl::StatusOr> Algebrizer::AlgebrizeDMLStatement( break; } - return WrapWithRootExpr(std::move(value_expr)); + return std::move(value_expr); } absl::Status Algebrizer::AlgebrizeDescendantsOfDMLStatement( @@ -6123,37 +6149,32 @@ absl::Status Algebrizer::AlgebrizeDefaultAndGeneratedExpressions( return absl::OkStatus(); } -absl::StatusOr Algebrizer::AddProtoFieldRegistry( +absl::StatusOr> +Algebrizer::MakeProtoFieldRegistry( const std::optional& id) { - const int registry_id = static_cast(proto_field_registries_.size()); - - auto registry = std::make_unique(registry_id); + auto registry = std::make_unique(); - ProtoFieldRegistry* ptr = registry.get(); - proto_field_registries_.push_back(std::move(registry)); if (id.has_value()) { - ZETASQL_RET_CHECK(proto_field_registry_map_.emplace(id.value(), ptr).second); + ZETASQL_RET_CHECK( + proto_field_registry_map_.emplace(id.value(), registry.get()).second); } - return ptr; + return std::move(registry); } -absl::StatusOr Algebrizer::AddProtoFieldReader( - const std::optional& id, - const ProtoFieldAccessInfo& access_info, ProtoFieldRegistry* registry) { - const int reader_id = static_cast(get_proto_field_readers_.size()); +absl::StatusOr> +Algebrizer::MakeProtoFieldReader(const std::optional& id, + const ProtoFieldAccessInfo& access_info, + ProtoFieldRegistry* registry) { + auto reader = std::make_unique(access_info, registry); - auto reader = - std::make_unique(reader_id, access_info, registry); - - ProtoFieldReader* ptr = reader.get(); - get_proto_field_readers_.push_back(std::move(reader)); // The check for get_has_bit might not be necessary here because the resolver // may always use BOOL for that case, but it doesn't hurt to be safe. if (id.has_value() && !access_info.field_info.get_has_bit && access_info.field_info.type->IsProto()) { - ZETASQL_RET_CHECK(get_proto_field_reader_map_.emplace(id.value(), ptr).second); + ZETASQL_RET_CHECK( + get_proto_field_reader_map_.emplace(id.value(), reader.get()).second); } - return ptr; + return std::move(reader); } std::string Algebrizer::SharedProtoFieldPath::DebugString() const { @@ -6848,6 +6869,18 @@ Algebrizer::AlgebrizeNonRedundantConjuncts( return algebrized_conjuncts; } +absl::StatusOr> Algebrizer::AlgebrizeAssertScan( + const ResolvedAssertScan* resolved_assert) { + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input, + AlgebrizeScan(resolved_assert->input_scan())); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr condition, + AlgebrizeExpression(resolved_assert->condition())); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr message, + AlgebrizeExpression(resolved_assert->message())); + return AssertOp::Create(std::move(input), std::move(condition), + std::move(message)); +} + absl::StatusOr> Algebrizer::AlgebrizeBarrierScan( const ResolvedBarrierScan* resolved_barrier_scan) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input, diff --git a/zetasql/reference_impl/algebrizer.h b/zetasql/reference_impl/algebrizer.h index ddc3c11ac..3ed1da162 100644 --- a/zetasql/reference_impl/algebrizer.h +++ b/zetasql/reference_impl/algebrizer.h @@ -41,6 +41,7 @@ #include "zetasql/reference_impl/operator.h" #include "zetasql/reference_impl/parameters.h" #include "zetasql/reference_impl/tuple.h" +#include "zetasql/reference_impl/uda_evaluation.h" #include "zetasql/reference_impl/variable_generator.h" #include "zetasql/reference_impl/variable_id.h" #include "zetasql/resolved_ast/resolved_ast.h" @@ -273,12 +274,17 @@ class Algebrizer { std::unique_ptr array_value, const ResolvedCollation& collation); + // Adds a variable for a UDA argument. Requires that `argument_name` does not + // already have a variable. + absl::StatusOr AddUdaArgumentVariable( + absl::string_view argument_name); + absl::StatusOr> AlgebrizeUdaCall( const AnonymizationOptions* anonymization_options, const ResolvedExpr& function_expr, const std::vector& aggregate_exprs, const ResolvedColumnList& aggregate_expr_columns, - std::vector argument_names, + absl::Span argument_infos, const LanguageOptions& language_options, const AlgebrizerOptions& algebrizer_options, TypeFactory* type_factory); @@ -290,16 +296,19 @@ class Algebrizer { absl::StatusOr> CreateTemplatedUserDefinedAggregateFn( const ResolvedNonScalarFunctionCallBase* aggregate_function, + std::vector>& arguments, const AnonymizationOptions* anonymization_options); absl::StatusOr> CreateNonTemplatedUserDefinedAggregateFn( const ResolvedNonScalarFunctionCallBase* aggregate_function, + std::vector>& arguments, const AnonymizationOptions* anonymization_options); absl::StatusOr> CreateUserDefinedAggregateFn( const ResolvedNonScalarFunctionCallBase* aggregate_function, + std::vector>& arguments, const AnonymizationOptions* anonymization_options); // TODO: Remove the special collation logics in this function. @@ -334,14 +343,6 @@ class Algebrizer { absl::StatusOr> AlgebrizeExpression( const ResolvedExpr* expr); - // Wraps 'value_expr' in a RootExpr to manage ownership of some objects - // required by the algebrized tree. - absl::StatusOr> WrapWithRootExpr( - std::unique_ptr value_expr); - - // Moves state from this algebrizer into a RootData and returns it. - std::unique_ptr GetRootData(); - // Wraps a conjunct from a filter with some associated information. struct FilterConjunctInfo { // Describes 'conjunct'. @@ -482,6 +483,11 @@ class Algebrizer { absl::StatusOr> AlgebrizeProjectScan( const ResolvedProjectScan* resolved_project, std::vector* active_conjuncts); + absl::StatusOr> AlgebrizeProjectScanInternal( + const ResolvedColumnList& column_list, + absl::Span> expr_list, + const ResolvedScan* input_scan, bool is_ordered, + std::vector* active_conjuncts); // 'limit' and 'offset' may both be NULL or both non-NULL. absl::StatusOr> AlgebrizeOrderByScan( const ResolvedOrderByScan* scan, std::unique_ptr limit, @@ -825,6 +831,24 @@ class Algebrizer { std::unique_ptr input, std::vector> algebrized_conjuncts); + // Returns a `RelationalOp` corresponding to `resolved_assert`. + // + // Unlike `AlgebrizeProjectScan`, this function does not accept + // FilterConjunctInfo to prevent query optimizations like filter pushdown, + // which can violate the semantics of the assert statement. + // + // For example, consider the following query: + // + // FROM UNNEST([1, 2, 3]) AS k + // |> ASSERT k > 1 + // |> WHERE k > 1 + // + // The assertion should fail. However, if the filter `WHERE k > 1` is pushed + // down to the array scan for [1, 2, 3], the assertion will succeed and thus + // the result is wrong. + absl::StatusOr> AlgebrizeAssertScan( + const ResolvedAssertScan* resolved_assert); + // Returns a `RelationalOp` corresponding to `resolved_barrier_scan`. // // This function does not accept FilterConjunctInfo to prevent query @@ -968,17 +992,14 @@ class Algebrizer { std::string DebugString() const; }; - // Adds a FieldRegistry to 'get_proto_field_caches_' and returns the - // corresponding pointer. If 'id' is set, also updates - // 'proto_field_registry_map_'. - absl::StatusOr AddProtoFieldRegistry( + // If 'id' is set, also updates 'proto_field_registry_map_'. + absl::StatusOr> MakeProtoFieldRegistry( const std::optional& id); - // Adds a ProtoFieldReader corresponding to 'access_info' and 'registry' to - // 'get_proto_field_readers_' and returns the corresponding pointer. If 'id' - // is set and 'access_info' represents a proto-valued field, also updates - // 'get_proto_field_reader_map_'. - absl::StatusOr AddProtoFieldReader( + // A ProtoFieldReader corresponding to 'access_info' and 'registry' is + // returned. If 'id' is set and 'access_info' represents a proto-valued field, + // also updates 'get_proto_field_reader_map_'. + absl::StatusOr> MakeProtoFieldReader( const std::optional& id, const ProtoFieldAccessInfo& access_info, ProtoFieldRegistry* registry); @@ -1045,6 +1066,9 @@ class Algebrizer { Parameters* parameters_; ParameterMap* column_map_; // Maps columns to variables. Not owned. + // Maps aggregate arguments by names to the variables. + absl::flat_hash_map aggregate_args_map_; + // Maps system variables to variable ids. Not owned. SystemVariablesAlgebrizerMap* system_variables_map_; @@ -1066,12 +1090,6 @@ class Algebrizer { // Entries are removed from the map as AlgebrizeWithRefScan() consumes them. absl::flat_hash_map inlined_with_entries_; - // Owns all the ProtoFieldRegistries created by the algebrizer. - std::vector> proto_field_registries_; - - // Owns all the ProtoFieldReaders created by the algebrizer. - std::vector> get_proto_field_readers_; - // If 'algebrizer_options_.consolidate_proto_field_accesses' is true, contains // every GetProtoFieldExpr::FieldRegistry whose proto-valued expression can be // represented with a ProtoColumnAndFieldPath. The pointers are owned by diff --git a/zetasql/reference_impl/algebrizer_test.cc b/zetasql/reference_impl/algebrizer_test.cc index f29b9e3e0..e966b6e90 100644 --- a/zetasql/reference_impl/algebrizer_test.cc +++ b/zetasql/reference_impl/algebrizer_test.cc @@ -444,11 +444,11 @@ TEST_F(ExpressionAlgebrizerTest, AlgebrizeResolvedCivilTimeExpressions) { test_cases; test_cases.emplace_back(MakeResolvedLiteral(Value::Time( TimeValue::FromHMSAndMicros(1, 2, 3, 123456))), - "RootExpr(ConstExpr(Time(01:02:03.123456)))"); + "ConstExpr(Time(01:02:03.123456))"); test_cases.emplace_back( MakeResolvedLiteral(Value::Datetime( DatetimeValue::FromYMDHMSAndMicros(2006, 1, 2, 3, 4, 5, 123456))), - "RootExpr(ConstExpr(Datetime(2006-01-02 03:04:05.123456)))"); + "ConstExpr(Datetime(2006-01-02 03:04:05.123456))"); TypeFactory type_factory; @@ -756,7 +756,7 @@ TEST_F(ExpressionAlgebrizerTest, PositionalParametersInExpressions) { ASSERT_EQ(2, parameters.positional_parameters().size()); EXPECT_FALSE(parameters.positional_parameters()[0].is_valid()); EXPECT_TRUE(parameters.positional_parameters()[1].is_valid()); - EXPECT_EQ(absl::Substitute("RootExpr(DerefExpr(positional_param_$0))", + EXPECT_EQ(absl::Substitute("DerefExpr(positional_param_$0)", second_param->position()), output->DebugString(/*verbose=*/true)); } diff --git a/zetasql/reference_impl/evaluation.h b/zetasql/reference_impl/evaluation.h index fcb25fd72..2533e1220 100644 --- a/zetasql/reference_impl/evaluation.h +++ b/zetasql/reference_impl/evaluation.h @@ -376,6 +376,12 @@ class EvaluationContext { active_group_rows_ = group_rows; } + // Returns true if FEATURE_TIMESTAMP_NANOS is enabled. + bool UseNanosTimeResolution() const { + return language_options_.LanguageFeatureEnabled( + zetasql::FEATURE_TIMESTAMP_NANOS); + } + // UDF argument references std::map udf_argument_references_; diff --git a/zetasql/reference_impl/expected_errors.cc b/zetasql/reference_impl/expected_errors.cc index 102896226..6f8034b61 100644 --- a/zetasql/reference_impl/expected_errors.cc +++ b/zetasql/reference_impl/expected_errors.cc @@ -160,6 +160,11 @@ std::unique_ptr> ReferenceExpectedErrorMatcher( "Invalid input to JSON_OBJECT: The number of keys and values must " "match")); + // We intentionally allow RQG to generate a small number of queries with + // failed assertions to test the ASSERT logic. + error_matchers.emplace_back(std::make_unique( + absl::StatusCode::kOutOfRange, "Assert failed:")); + return std::make_unique>( matcher_name, std::move(error_matchers)); } diff --git a/zetasql/reference_impl/function.cc b/zetasql/reference_impl/function.cc index 6ef70f279..9c1417ae9 100644 --- a/zetasql/reference_impl/function.cc +++ b/zetasql/reference_impl/function.cc @@ -317,9 +317,19 @@ template bool InvokeString(FunctionType function, Value* result, absl::Status* status, Args... args) { OutType out; - if (!function(args..., &out, status)) { + if constexpr (std::is_invocable_v) { + if (!function(args..., &out, status)) { + return false; + } + } else { + *status = function(args..., &out); + } + + if (!status->ok()) { return false; } + *result = Value::String(out); return true; } @@ -847,6 +857,10 @@ FunctionMap::FunctionMap() { RegisterFunction(FunctionKind::kSplit, "split", "Split"); RegisterFunction(FunctionKind::kSplitWithCollation, "$split_with_collation", "SplitWithCollation"); + RegisterFunction(FunctionKind::kSplitSubstr, "split_substr", "SplitSubstr"); + RegisterFunction(FunctionKind::kSplitSubstrWithCollation, + "$split_substr_with_collation", + "SplitSubstrWithCollation"); RegisterFunction(FunctionKind::kStartsWith, "starts_with", "StartsWith"); RegisterFunction(FunctionKind::kStartsWithWithCollation, "$starts_with_with_collation", "StartsWithWithCollation"); @@ -1113,6 +1127,10 @@ FunctionMap::FunctionMap() { "MapValuesUnsorted"); RegisterFunction(FunctionKind::kMapValuesSortedByKey, "map_values_sorted_by_key", "MapValuesSortedByKey"); + RegisterFunction(FunctionKind::kMapEmpty, "map_empty", "MapEmpty"); + RegisterFunction(FunctionKind::kMapInsert, "map_insert", "MapInsert"); + RegisterFunction(FunctionKind::kMapInsertOrReplace, "map_insert_or_replace", + "MapInsertOrReplace"); }(); } // NOLINT(readability/fn_size) @@ -2322,6 +2340,7 @@ BuiltinScalarFunction::CreateValidatedRaw( case FunctionKind::kChr: case FunctionKind::kTranslate: case FunctionKind::kInitCap: + case FunctionKind::kSplitSubstr: return new StringFunction(kind, output_type); case FunctionKind::kCollate: return new CollateFunction(kind, output_type); @@ -2411,6 +2430,7 @@ BuiltinScalarFunction::CreateValidatedRaw( case FunctionKind::kStrposWithCollation: case FunctionKind::kInstrWithCollation: case FunctionKind::kSplitWithCollation: + case FunctionKind::kSplitSubstrWithCollation: case FunctionKind::kCollationKey: return BuiltinFunctionRegistry::GetScalarFunction(kind, output_type); case FunctionKind::kArrayConcat: @@ -4345,19 +4365,21 @@ absl::StatusOr ArrayMinMaxFunction::Eval( return Value::Datetime(min_value); } case FCT(FunctionKind::kArrayMin, TYPE_TIMESTAMP): { - int64_t min_value = std::numeric_limits::max(); + absl::Time min_value = absl::InfiniteFuture(); for (const Value& element : args[0].elements()) { if (element.is_null()) { continue; } has_non_null = true; - // TODO: Support Value::ToUnixNanos in ARRAY_MIN and MIN. - min_value = std::min(min_value, element.ToUnixMicros()); + min_value = std::min(min_value, element.ToTime()); } if (!has_non_null) { return output_null; } - return Value::TimestampFromUnixMicros(min_value); + return context->UseNanosTimeResolution() + ? Value::Timestamp(min_value) + : Value::TimestampFromUnixMicros( + absl::ToUnixMicros(min_value)); } case FCT(FunctionKind::kArrayMin, TYPE_TIME): { int64_t min_value = std::numeric_limits::max(); @@ -4582,18 +4604,21 @@ absl::StatusOr ArrayMinMaxFunction::Eval( return Value::Datetime(max_value); } case FCT(FunctionKind::kArrayMax, TYPE_TIMESTAMP): { - int64_t max_value = std::numeric_limits::lowest(); + absl::Time max_value = absl::InfinitePast(); for (const Value& element : args[0].elements()) { if (element.is_null()) { continue; } has_non_null = true; - max_value = std::max(max_value, element.ToUnixMicros()); + max_value = std::max(max_value, element.ToTime()); } if (!has_non_null) { return output_null; } - return Value::TimestampFromUnixMicros(max_value); + return context->UseNanosTimeResolution() + ? Value::Timestamp(max_value) + : Value::TimestampFromUnixMicros( + absl::ToUnixMicros(max_value)); } case FCT(FunctionKind::kArrayMax, TYPE_TIME): { int64_t max_value = std::numeric_limits::lowest(); @@ -5431,6 +5456,7 @@ class BuiltinAggregateAccumulator : public AggregateAccumulator { int64_t out_int64_ = 0; // Max, Min uint64_t out_uint64_ = 0; // Max, Min DatetimeValue out_datetime_; // Max, Min + absl::Time out_timestamp_; // Max, Min IntervalValue out_interval_; // Max, Min __int128 out_int128_ = 0; // Sum unsigned __int128 out_uint128_ = 0; // Sum @@ -5600,8 +5626,8 @@ class UserDefinedAggregateFunction : public AggregateFunctionBody { std::string debug_name() const override { return function_name_; } absl::StatusOr> CreateAccumulator( - absl::Span args, CollatorList collator_list, - EvaluationContext* context) const override { + absl::Span args, absl::Span params, + CollatorList collator_list, EvaluationContext* context) const override { auto status_or_evaluator = evaluator_factory_(function_signature_); ZETASQL_RETURN_IF_ERROR(status_or_evaluator.status()); std::unique_ptr evaluator = @@ -5609,7 +5635,11 @@ class UserDefinedAggregateFunction : public AggregateFunctionBody { // This should never happen because we already check for null evaluator // in the algebrizer. ZETASQL_RET_CHECK(evaluator != nullptr); - evaluator->SetEvaluationContext(context); + SqlDefinedAggregateFunctionEvaluator* sql_evaluator = + dynamic_cast(evaluator.get()); + if (sql_evaluator != nullptr) { + sql_evaluator->SetEvaluationContext(context, params); + } return UserDefinedAggregateAccumulator::Create( std::move(evaluator), output_type_, num_input_fields()); } @@ -5691,6 +5721,7 @@ InitializeAnonBuilder<::differential_privacy::Quantiles::Builder>( // quantiles. double number_of_quantile_boundaries = args[0].int64_value() + 1; std::vector quantiles; + quantiles.reserve(number_of_quantile_boundaries); for (double i = 0; i < number_of_quantile_boundaries; ++i) { quantiles.push_back(i / number_of_quantile_boundaries); } @@ -5868,6 +5899,7 @@ BuildDPAlgorithm<::differential_privacy::Quantiles, double>( // quantiles. double number_of_quantile_boundaries = args[0].int64_value() + 1; std::vector quantiles; + quantiles.reserve(number_of_quantile_boundaries); for (double i = 0; i < number_of_quantile_boundaries; ++i) { quantiles.push_back(i / number_of_quantile_boundaries); } @@ -5959,10 +5991,12 @@ absl::Status BuiltinAggregateAccumulator::Reset() { case FCT(FunctionKind::kMax, TYPE_DATE): case FCT(FunctionKind::kMax, TYPE_BOOL): case FCT(FunctionKind::kMax, TYPE_ENUM): - case FCT(FunctionKind::kMax, TYPE_TIMESTAMP): case FCT(FunctionKind::kMax, TYPE_TIME): out_int64_ = std::numeric_limits::lowest(); break; + case FCT(FunctionKind::kMax, TYPE_TIMESTAMP): + out_timestamp_ = absl::InfinitePast(); + break; case FCT(FunctionKind::kMax, TYPE_NUMERIC): out_numeric_ = NumericValue::MinValue(); break; @@ -6000,10 +6034,12 @@ absl::Status BuiltinAggregateAccumulator::Reset() { case FCT(FunctionKind::kMin, TYPE_DATE): case FCT(FunctionKind::kMin, TYPE_BOOL): case FCT(FunctionKind::kMin, TYPE_ENUM): - case FCT(FunctionKind::kMin, TYPE_TIMESTAMP): case FCT(FunctionKind::kMin, TYPE_TIME): out_int64_ = std::numeric_limits::max(); break; + case FCT(FunctionKind::kMin, TYPE_TIMESTAMP): + out_timestamp_ = absl::InfiniteFuture(); + break; case FCT(FunctionKind::kMin, TYPE_NUMERIC): out_numeric_ = NumericValue::MaxValue(); break; @@ -6451,7 +6487,7 @@ bool BuiltinAggregateAccumulator::Accumulate(const Value& value, break; } case FCT(FunctionKind::kMax, TYPE_TIMESTAMP): { - out_int64_ = std::max(out_int64_, value.ToUnixMicros()); + out_timestamp_ = std::max(out_timestamp_, value.ToTime()); break; } case FCT(FunctionKind::kMax, TYPE_DATETIME): { @@ -6556,7 +6592,7 @@ bool BuiltinAggregateAccumulator::Accumulate(const Value& value, } case FCT(FunctionKind::kMin, TYPE_TIMESTAMP): { // TODO: Support Value::ToUnixNanos in ARRAY_MIN and MIN. - out_int64_ = std::min(out_int64_, value.ToUnixMicros()); + out_timestamp_ = std::min(out_timestamp_, value.ToTime()); break; } case FCT(FunctionKind::kMin, TYPE_DATETIME): { @@ -8006,8 +8042,14 @@ absl::StatusOr BuiltinAggregateAccumulator::GetFinalResultInternal( return count_ > 0 ? Value::Date(out_int64_) : Value::NullDate(); case FCT(FunctionKind::kMax, TYPE_TIMESTAMP): case FCT(FunctionKind::kMin, TYPE_TIMESTAMP): - return count_ > 0 ? Value::TimestampFromUnixMicros(out_int64_) - : Value::NullTimestamp(); + if (count_ == 0) { + return Value::NullTimestamp(); + } + return context_->UseNanosTimeResolution() + ? Value::Timestamp(out_timestamp_) + : Value::TimestampFromUnixMicros( + absl::ToUnixMicros(out_timestamp_)); + case FCT(FunctionKind::kMax, TYPE_DATETIME): case FCT(FunctionKind::kMin, TYPE_DATETIME): return count_ > 0 ? Value::Datetime(out_datetime_) @@ -8134,9 +8176,9 @@ absl::StatusOr BuiltinAggregateAccumulator::GetFinalResultInternal( } // namespace absl::StatusOr> -BuiltinAggregateFunction::CreateAccumulator(absl::Span args, - CollatorList collator_list, - EvaluationContext* context) const { +BuiltinAggregateFunction::CreateAccumulator( + absl::Span args, absl::Span params, + CollatorList collator_list, EvaluationContext* context) const { return BuiltinAggregateAccumulator::Create(this, input_type(), args, std::move(collator_list), context); } @@ -8414,6 +8456,7 @@ absl::StatusOr BinaryStatAccumulator::GetFinalResult( absl::StatusOr> BinaryStatFunction::CreateAccumulator(absl::Span args, + absl::Span params, CollatorList collator_list, EvaluationContext* context) const { // should be empty for bivariate stats functions. @@ -9367,6 +9410,15 @@ bool StringFunction::Eval(absl::Span params, return InvokeString(&functions::InitialCapitalize, result, status, args[0].string_value(), args[1].string_value()); + case FCT_TYPE_ARITY(FunctionKind::kSplitSubstr, TYPE_STRING, 3): + return InvokeString( + &functions::SplitSubstr, result, status, args[0].string_value(), + args[1].string_value(), args[2].int64_value()); + case FCT_TYPE_ARITY(FunctionKind::kSplitSubstr, TYPE_STRING, 4): + return InvokeString( + &functions::SplitSubstrWithCount, result, status, + args[0].string_value(), args[1].string_value(), args[2].int64_value(), + args[3].int64_value()); } *status = ::zetasql_base::UnimplementedErrorBuilder() << "Unsupported string function: " << debug_name(); @@ -9962,8 +10014,7 @@ absl::StatusOr FormatDateDatetimeTimestampFunction::Eval( if (args.size() == 2) { ZETASQL_RETURN_IF_ERROR(functions::FormatTimestampToString( args[0].string_value(), - context->GetLanguageOptions().LanguageFeatureEnabled( - FEATURE_TIMESTAMP_NANOS) + context->UseNanosTimeResolution() ? args[1].ToTime() : absl::FromUnixMicros(args[1].ToUnixMicros()), context->GetDefaultTimeZone(), {.expand_Q = true, .expand_J = true}, @@ -9971,8 +10022,7 @@ absl::StatusOr FormatDateDatetimeTimestampFunction::Eval( } else { ZETASQL_RETURN_IF_ERROR(functions::FormatTimestampToString( args[0].string_value(), - context->GetLanguageOptions().LanguageFeatureEnabled( - FEATURE_TIMESTAMP_NANOS) + context->UseNanosTimeResolution() ? args[1].ToTime() : absl::FromUnixMicros(args[1].ToUnixMicros()), args[2].string_value(), {.expand_Q = true, .expand_J = true}, @@ -10273,8 +10323,7 @@ absl::StatusOr StringConversionFunction::Eval( ZETASQL_RETURN_IF_ERROR( functions::MakeTimeZone(args[1].string_value(), &timezone)); } - if (context->GetLanguageOptions().LanguageFeatureEnabled( - FEATURE_TIMESTAMP_NANOS)) { + if (context->UseNanosTimeResolution()) { ZETASQL_RETURN_IF_ERROR(functions::ConvertTimestampToString( args[0].ToTime(), functions::kNanoseconds, timezone, &result_string)); @@ -10367,8 +10416,7 @@ absl::StatusOr ParseTimestampFunction::Eval( EvaluationContext* context) const { ZETASQL_RET_CHECK(args.size() == 2 || args.size() == 3); if (HasNulls(args)) return Value::Null(output_type()); - if (context->GetLanguageOptions().LanguageFeatureEnabled( - FEATURE_TIMESTAMP_NANOS)) { + if (context->UseNanosTimeResolution()) { absl::Time timestamp; if (args.size() == 2) { ZETASQL_RETURN_IF_ERROR(functions::ParseStringToTimestamp( diff --git a/zetasql/reference_impl/function.h b/zetasql/reference_impl/function.h index e86351382..ed9f2a994 100644 --- a/zetasql/reference_impl/function.h +++ b/zetasql/reference_impl/function.h @@ -20,6 +20,7 @@ #define ZETASQL_REFERENCE_IMPL_FUNCTION_H_ #include +#include #include #include #include @@ -356,6 +357,8 @@ enum class FunctionKind { kSafeConvertBytesToString, kSplit, kSplitWithCollation, + kSplitSubstr, + kSplitSubstrWithCollation, kStartsWith, kStartsWithWithCollation, kStrpos, @@ -524,6 +527,9 @@ enum class FunctionKind { kMapValuesSorted, kMapValuesUnsorted, kMapValuesSortedByKey, + kMapEmpty, + kMapInsert, + kMapInsertOrReplace, }; // Provides two utility methods to look up a built-in function name or function @@ -672,8 +678,8 @@ class BuiltinAggregateFunction : public AggregateFunctionBody { std::string debug_name() const override; absl::StatusOr> CreateAccumulator( - absl::Span args, CollatorList collator_list, - EvaluationContext* context) const override; + absl::Span args, absl::Span params, + CollatorList collator_list, EvaluationContext* context) const override; private: const FunctionKind kind_; @@ -690,8 +696,20 @@ class BinaryStatFunction : public BuiltinAggregateFunction { BinaryStatFunction& operator=(const BinaryStatFunction&) = delete; absl::StatusOr> CreateAccumulator( - absl::Span args, CollatorList collator_list, - EvaluationContext* context) const override; + absl::Span args, absl::Span params, + CollatorList collator_list, EvaluationContext* context) const override; +}; + +// Adds additional members to `AggregateFunctionEvaluator` that the reference +// implementation needs in order to enable SQL evaluation during aggregation. +class SqlDefinedAggregateFunctionEvaluator : public AggregateFunctionEvaluator { + public: + ~SqlDefinedAggregateFunctionEvaluator() override = default; + + // Sets an evaluation context. + virtual void SetEvaluationContext( + EvaluationContext* context, + absl::Span params) = 0; }; using ContextAwareFunctionEvaluator = std::function( diff --git a/zetasql/reference_impl/functions/BUILD b/zetasql/reference_impl/functions/BUILD index 221495c16..99de5c796 100644 --- a/zetasql/reference_impl/functions/BUILD +++ b/zetasql/reference_impl/functions/BUILD @@ -68,6 +68,7 @@ cc_library( "//zetasql/public/functions:json_format", "//zetasql/public/functions:json_internal", "//zetasql/public/functions:to_json", + "//zetasql/public/functions:unsupported_fields_cc_proto", "//zetasql/public/types", "//zetasql/reference_impl:evaluation", "@com_google_absl//absl/status:statusor", @@ -80,7 +81,6 @@ cc_library( srcs = ["string_with_collation.cc"], hdrs = ["string_with_collation.h"], deps = [ - ":like", "//zetasql/base:ret_check", "//zetasql/base:status", "//zetasql/public:collator", @@ -88,8 +88,10 @@ cc_library( "//zetasql/public/functions:string_with_collation", "//zetasql/public/types", "//zetasql/reference_impl:evaluation", + "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings:cord", + "@com_google_absl//absl/strings:string_view", "@com_google_absl//absl/types:span", ], ) @@ -166,6 +168,7 @@ cc_library( "//zetasql/public/types", "//zetasql/reference_impl:evaluation", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/types:span", diff --git a/zetasql/reference_impl/functions/json.cc b/zetasql/reference_impl/functions/json.cc index 678da4eec..c4b86a97d 100644 --- a/zetasql/reference_impl/functions/json.cc +++ b/zetasql/reference_impl/functions/json.cc @@ -29,6 +29,7 @@ #include "zetasql/public/functions/json_format.h" #include "zetasql/public/functions/json_internal.h" #include "zetasql/public/functions/to_json.h" +#include "zetasql/public/functions/unsupported_fields.pb.h" #include "zetasql/public/json_value.h" #include "zetasql/public/language_options.h" #include "zetasql/public/type.pb.h" @@ -617,16 +618,22 @@ absl::StatusOr JsonExtractArrayFunction::Eval( absl::StatusOr ToJsonFunction::Eval( absl::Span params, absl::Span args, EvaluationContext* context) const { - ZETASQL_RET_CHECK_EQ(args.size(), 2); + ZETASQL_RET_CHECK_GE(args.size(), 2); + ZETASQL_RET_CHECK_LE(args.size(), 3); if (args[1].is_null()) { return Value::Null(output_type()); } const bool stringify_wide_numbers = args[1].bool_value(); ZETASQL_RETURN_IF_ERROR(ValidateMicrosPrecision(args[0], context)); - ZETASQL_ASSIGN_OR_RETURN(JSONValue outputJson, - functions::ToJson(args[0], stringify_wide_numbers, - context->GetLanguageOptions(), - /*canonicalize_zero=*/true)); + zetasql::functions::UnsupportedFields unsupported_fields = + args.size() == 3 ? static_cast( + args[2].enum_value()) + : zetasql::functions::UnsupportedFields::FAIL; + ZETASQL_ASSIGN_OR_RETURN( + JSONValue outputJson, + functions::ToJson(args[0], stringify_wide_numbers, + context->GetLanguageOptions(), + /*canonicalize_zero=*/true, unsupported_fields)); MaybeSetNonDeterministicContext(args[0], context); return Value::Json(std::move(outputJson)); } diff --git a/zetasql/reference_impl/functions/map.cc b/zetasql/reference_impl/functions/map.cc index 51c127615..db8ff4860 100644 --- a/zetasql/reference_impl/functions/map.cc +++ b/zetasql/reference_impl/functions/map.cc @@ -33,6 +33,7 @@ #include "zetasql/reference_impl/function.h" #include "zetasql/reference_impl/tuple.h" #include "absl/algorithm/container.h" +#include "absl/container/flat_hash_set.h" #include "zetasql/base/check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" @@ -382,6 +383,96 @@ class MapValuesSortedByKeyFunction : public SimpleBuiltinScalarFunction { } }; +class MapEmptyFunction : public SimpleBuiltinScalarFunction { + public: + MapEmptyFunction(FunctionKind kind, const Type* output_type) + : SimpleBuiltinScalarFunction(kind, output_type) {} + absl::StatusOr Eval(absl::Span params, + absl::Span args, + EvaluationContext* context) const override { + ZETASQL_RET_CHECK_EQ(args.size(), 1); + const Value& map = args[0]; + + if (map.is_null()) { + return Value::Null(output_type()); + } + + ZETASQL_RET_CHECK(map.type()->IsMap()) << map.type()->DebugString(); + return Value::Bool(map.num_elements() == 0); + } +}; + +static inline absl::StatusOr MapInsertImpl( + const bool replace_existing_values, const Type* output_type, + absl::Span params, absl::Span args, + EvaluationContext* context) { + ZETASQL_RET_CHECK(args.size() >= 3) << args.size(); + ZETASQL_RET_CHECK(args.size() % 2 == 1) + << args.size() + << ": after the map argument, arguments should be provided in key/value " + "pairs."; + ZETASQL_RET_CHECK(args[0].type()->IsMap()) << args[0].type()->DebugString(); + const Value& map = args[0]; + + if (map.is_null()) { + return Value::Null(output_type); + } + + // (size - 1) because the first argument is the map, which we don't include. + const int num_entries = (args.size() - 1) / 2; + + absl::flat_hash_set keys_to_insert; + keys_to_insert.reserve(num_entries); + std::vector> entries; + entries.reserve(num_entries); + + // The ZETASQL_RET_CHECK above should catch any issue with unexpected size, but just + // to be safe, check (size - 1) here since the loop increments by 2. + for (int i = 1; i < args.size() - 1; i += 2) { + const auto& [unused, success] = keys_to_insert.emplace(args[i]); + if (!success) { + return MakeEvalError() << "Key provided more than once as argument: " + << args[i].Format(/*print_top_level_type=*/false); + } + entries.push_back({args[i], args[i + 1]}); + } + + for (const auto& [key, value] : map.map_entries()) { + if (!keys_to_insert.contains(key)) { + entries.push_back({key, value}); + } else if (!replace_existing_values) { + return MakeEvalError() << "Key already exists in map: " + << key.Format(/*print_top_level_type=*/false); + } + } + + return Value::MakeMap(args[0].type(), std::move(entries)); +} + +class MapInsertFunction : public SimpleBuiltinScalarFunction { + public: + MapInsertFunction(FunctionKind kind, const Type* output_type) + : SimpleBuiltinScalarFunction(kind, output_type) {} + absl::StatusOr Eval(absl::Span params, + absl::Span args, + EvaluationContext* context) const override { + return MapInsertImpl(/*replace_existing_values=*/false, output_type(), + params, args, context); + } +}; + +class MapInsertOrReplaceFunction : public SimpleBuiltinScalarFunction { + public: + MapInsertOrReplaceFunction(FunctionKind kind, const Type* output_type) + : SimpleBuiltinScalarFunction(kind, output_type) {} + absl::StatusOr Eval(absl::Span params, + absl::Span args, + EvaluationContext* context) const override { + return MapInsertImpl(/*replace_existing_values=*/true, output_type(), + params, args, context); + } +}; + } // namespace void RegisterBuiltinMapFunctions() { @@ -439,5 +530,20 @@ void RegisterBuiltinMapFunctions() { [](FunctionKind kind, const Type* output_type) { return new MapValuesSortedByKeyFunction(kind, output_type); }); + BuiltinFunctionRegistry::RegisterScalarFunction( + {FunctionKind::kMapEmpty}, + [](FunctionKind kind, const Type* output_type) { + return new MapEmptyFunction(kind, output_type); + }); + BuiltinFunctionRegistry::RegisterScalarFunction( + {FunctionKind::kMapInsert}, + [](FunctionKind kind, const Type* output_type) { + return new MapInsertFunction(kind, output_type); + }); + BuiltinFunctionRegistry::RegisterScalarFunction( + {FunctionKind::kMapInsertOrReplace}, + [](FunctionKind kind, const Type* output_type) { + return new MapInsertOrReplaceFunction(kind, output_type); + }); } } // namespace zetasql diff --git a/zetasql/reference_impl/functions/range.cc b/zetasql/reference_impl/functions/range.cc index e657858c9..89c95b6e3 100644 --- a/zetasql/reference_impl/functions/range.cc +++ b/zetasql/reference_impl/functions/range.cc @@ -270,8 +270,8 @@ absl::StatusOr RangeIntersectFunction::Eval( ZETASQL_RETURN_IF_ERROR(ValidateMicrosPrecision(args[1], context)); if (!DoTwoRangesOverlap(args[0], args[1])) { return MakeEvalError() - << "Provided RANGE inputs: " << args[0] << " and " << args[1] - << " do not overlap. " + << "Provided RANGE inputs: " << args[0].Format() << " and " + << args[1].Format() << " do not overlap. " << "Please check RANGE_OVERLAPS before calling RANGE_INTERSECT"; } diff --git a/zetasql/reference_impl/functions/string_with_collation.cc b/zetasql/reference_impl/functions/string_with_collation.cc index 8477f8b92..a13fc5f6e 100644 --- a/zetasql/reference_impl/functions/string_with_collation.cc +++ b/zetasql/reference_impl/functions/string_with_collation.cc @@ -17,6 +17,7 @@ #include "zetasql/reference_impl/functions/string_with_collation.h" #include +#include #include #include #include @@ -24,13 +25,15 @@ #include "zetasql/public/collator.h" #include "zetasql/public/functions/string_with_collation.h" #include "zetasql/public/types/type.h" +#include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" #include "zetasql/reference_impl/evaluation.h" #include "zetasql/reference_impl/function.h" -#include "zetasql/reference_impl/functions/like.h" #include "zetasql/reference_impl/tuple.h" +#include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/cord.h" +#include "absl/strings/string_view.h" #include "absl/types/span.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_builder.h" @@ -71,13 +74,20 @@ class CollationKeyFunction : public SimpleBuiltinScalarFunction { EvaluationContext* context) const override; }; -class LikeWithCollationFunction : public SimpleBuiltinScalarFunction { - public: - using SimpleBuiltinScalarFunction::SimpleBuiltinScalarFunction; - absl::StatusOr Eval(absl::Span params, - absl::Span args, - EvaluationContext* context) const override; -}; +template +bool EvaluateFunctionWithCollation(FunctionType function, + const ZetaSqlCollator& collator, + Args&&... args, OutType* out, + absl::Status* status) { + if constexpr (std::is_invocable_v) { + return function(collator, args..., out, status); + } else { + *status = function(collator, args..., out); + return status->ok(); + } +} template bool InvokeWithCollation(FunctionType function, Value* result, @@ -90,9 +100,12 @@ bool InvokeWithCollation(FunctionType function, Value* result, return false; } OutType out; - if (!function(*(collator_or_status.value()), args..., &out, status)) { + if (!EvaluateFunctionWithCollation( + function, *(collator_or_status.value()), std::forward(args)..., + &out, status)) { return false; } + *result = Value::Make(out); return true; } @@ -108,7 +121,9 @@ bool InvokeStringWithCollation(FunctionType function, Value* result, return false; } std::string out; - if (!function(*(collator_or_status.value()), args..., &out, status)) { + if (!EvaluateFunctionWithCollation( + function, *(collator_or_status.value()), std::forward(args)..., + &out, status)) { return false; } *result = Value::String(out); @@ -162,6 +177,19 @@ bool StringWithCollationFunction::Eval( &functions::ReplaceUtf8WithCollation, result, status, args[0].string_value(), args[1].string_value(), args[2].string_value(), args[3].string_value()); + case FCT_TYPE_ARITY(FunctionKind::kSplitSubstrWithCollation, TYPE_STRING, + 4): + return InvokeStringWithCollation( + &functions::SplitSubstrWithCollation, result, status, + args[0].string_value(), args[1].string_value(), + args[2].string_value(), args[3].int64_value(), + /*count=*/std::numeric_limits::max()); + case FCT_TYPE_ARITY(FunctionKind::kSplitSubstrWithCollation, TYPE_STRING, + 5): + return InvokeStringWithCollation( + &functions::SplitSubstrWithCollation, result, status, + args[0].string_value(), args[1].string_value(), + args[2].string_value(), args[3].int64_value(), args[4].int64_value()); } *status = ::zetasql_base::UnimplementedErrorBuilder() << "Unsupported string function: " << debug_name(); @@ -215,19 +243,6 @@ absl::StatusOr CollationKeyFunction::Eval( return Value::Bytes(cord.Flatten()); } -absl::StatusOr LikeWithCollationFunction::Eval( - absl::Span params, absl::Span args, - EvaluationContext* context) const { - ZETASQL_RET_CHECK_GE(args.size(), 3) << "LIKE with collation has 3 or more arguments"; - QuantifiedLikeEvaluationParams quantified_like_eval_params( - /*search_value=*/args[1], - /*pattern_elements=*/args.subspan(2), - /*operation_type=*/QuantifiedLikeEvaluationParams::kLike, - /*is_not=*/false, - /*collation_str=*/args[0].string_value()); - return EvaluateQuantifiedLike(quantified_like_eval_params); -} - } // namespace void RegisterBuiltinStringWithCollationFunctions() { @@ -235,7 +250,8 @@ void RegisterBuiltinStringWithCollationFunctions() { {FunctionKind::kEndsWithWithCollation, FunctionKind::kReplaceWithCollation, FunctionKind::kSplitWithCollation, FunctionKind::kStartsWithWithCollation, - FunctionKind::kStrposWithCollation, FunctionKind::kInstrWithCollation}, + FunctionKind::kStrposWithCollation, FunctionKind::kInstrWithCollation, + FunctionKind::kSplitSubstrWithCollation}, [](FunctionKind kind, const Type* output_type) { return new StringWithCollationFunction(kind, output_type); }); diff --git a/zetasql/reference_impl/operator.h b/zetasql/reference_impl/operator.h index 50effd7c4..fc5e87b19 100644 --- a/zetasql/reference_impl/operator.h +++ b/zetasql/reference_impl/operator.h @@ -1677,7 +1677,7 @@ class RowsForUdaOp : public RelationalOp { absl::string_view input_iter_debug_string); static std::unique_ptr Create( - std::vector argument_names); + std::vector arguments); absl::Status SetSchemasForEvaluation( absl::Span params_schemas) override; @@ -1696,11 +1696,9 @@ class RowsForUdaOp : public RelationalOp { bool verbose) const override; private: - explicit RowsForUdaOp(std::vector argument_names); + explicit RowsForUdaOp(std::vector argument); - std::vector argument_names() const; - - std::vector argument_names_; + std::vector arguments_; }; // Partitions the input by , and evaluates a number of analytic @@ -2587,12 +2585,10 @@ class FieldValueExpr final : public ValueExpr { // ProtoFieldAccessInfo from a ProtoFieldRegistry. class ProtoFieldReader { public: - // 'id' is a unique identifier of this instance that is used for debug - // logging. Does not take ownership of 'registry'. - ProtoFieldReader(int id, const ProtoFieldAccessInfo& access_info, + // Does not take ownership of 'registry'. + ProtoFieldReader(const ProtoFieldAccessInfo& access_info, ProtoFieldRegistry* registry) - : id_(id), - access_info_(access_info), + : access_info_(access_info), registry_(registry), access_info_registry_id_(registry->RegisterField(&access_info_)) {} @@ -2612,12 +2608,7 @@ class ProtoFieldReader { const ProtoFieldAccessInfo& access_info() const { return access_info_; } - int id() const { return id_; } - - int registry_id() const { return registry_->id(); } - private: - const int id_; const ProtoFieldAccessInfo access_info_; const ProtoFieldRegistry* registry_; const int access_info_registry_id_; @@ -2698,6 +2689,12 @@ class GetProtoFieldExpr final : public ValueExpr { const ProtoFieldReader* field_reader() const { return field_reader_; } + void set_owned_reader(std::unique_ptr owned_reader, + std::unique_ptr owned_registry) { + owned_reader_ = std::move(owned_reader); + owned_registry_ = std::move(owned_registry); + } + private: enum ArgKind { kProtoExpr }; @@ -2710,6 +2707,12 @@ class GetProtoFieldExpr final : public ValueExpr { ValueExpr* mutable_proto_expr(); const ProtoFieldReader* field_reader_; + + // Ownership of these objects is transferred to the first GetPRotoFieldExpr + // that uses them. Subsequent GetProtoFieldExprs get a raw pointer, but no + // ownership. + std::unique_ptr owned_reader_; + std::unique_ptr owned_registry_; }; // Handles evaluating a flatten which merges the results over nested arrays. @@ -3090,11 +3093,15 @@ class AggregateFunctionBody : public FunctionBody { const int num_input_fields() const { return num_input_fields_; } const Type* input_type() const { return input_type_; } - // contains the constant arguments for the aggregation - // function (e.g., the delimeter for STRING_AGG). indicates - // the collations used for aggregate function. + // `args` contains the constant arguments for the aggregation function + // (e.g., the delimiter for STRING_AGG). + // `params` is the parameters needed for argument evaluation. (e.g. for SQL + // UDAs which will evaluate NOT AGGREGATE arguments later.) + // `collator_list` indicates the collations used for aggregate function. virtual absl::StatusOr> - CreateAccumulator(absl::Span args, CollatorList collator_list, + CreateAccumulator(absl::Span args, + absl::Span params, + CollatorList collator_list, EvaluationContext* context) const = 0; private: @@ -3677,7 +3684,7 @@ class DMLValueExpr : public ValueExpr { // OUT_OF_RANGE and error message 'duplicate_primary_key_error_prefix' + ' (' // + + ')'. absl::Status PopulatePrimaryKeyRowMap( - const std::vector>& original_rows, + absl::Span> original_rows, absl::string_view duplicate_primary_key_error_prefix, EvaluationContext* context, PrimaryKeyRowMap* row_map, bool* has_primary_key) const; @@ -3725,7 +3732,7 @@ class DMLValueExpr : public ValueExpr { // need to have values of gen1 and data evaluated before gen2 value can be // evaluated. absl::Status EvalGeneratedColumnsByTopologicalOrder( - const std::vector& topologically_sorted_generated_column_id_list, + absl::Span topologically_sorted_generated_column_id_list, const absl::flat_hash_map& generated_columns_position_map, EvaluationContext* context, std::vector& row) const; @@ -4152,7 +4159,7 @@ class DMLUpdateValueExpr final : public DMLValueExpr { const ResolvedUpdateStmt* nested_update, absl::Span tuples_for_row, const ResolvedColumn& element_column, - const std::vector& original_elements, EvaluationContext* context, + absl::Span original_elements, EvaluationContext* context, std::vector* new_elements) const; // Applies 'nested_insert' to a vector whose values were originally @@ -4162,7 +4169,7 @@ class DMLUpdateValueExpr final : public DMLValueExpr { absl::Status ProcessNestedInsert( const ResolvedInsertStmt* nested_insert, absl::Span tuples_for_row, - const std::vector& original_elements, EvaluationContext* context, + absl::Span original_elements, EvaluationContext* context, std::vector* new_elements) const; }; @@ -4280,87 +4287,6 @@ class DMLInsertValueExpr final : public DMLValueExpr { const std::vector>& dml_returning_rows, EvaluationContext* context) const; }; - -// ------------------------------------------------------- -// Dummy root objects -// ------------------------------------------------------- - -// Shared objects in the tree whose ownership is managed by the dummy root node. -struct RootData { - std::vector> registries; - std::vector> field_readers; -}; - -// A dummy root object that wraps up a RelationalOp along with ownership of -// shared objects in the tree. -class RootOp final : public RelationalOp { - public: - RootOp(const RootOp&) = delete; - RootOp& operator=(const RootOp&) = delete; - - static absl::StatusOr> Create( - std::unique_ptr input, std::unique_ptr root_data); - - absl::Status SetSchemasForEvaluation( - absl::Span params_schemas) override; - - absl::StatusOr> CreateIterator( - absl::Span params, int num_extra_slots, - EvaluationContext* context) const override; - - std::unique_ptr CreateOutputSchema() const override; - - std::string IteratorDebugString() const override; - - std::string DebugInternal(const std::string& indent, - bool verbose) const override; - - private: - enum ArgKind { kInput }; - - RootOp(std::unique_ptr input, - std::unique_ptr root_data); - - // Returns the RelationalOp passed to the constructor. - const RelationalOp* input() const; - RelationalOp* mutable_input(); - - std::unique_ptr root_data_; -}; - -// A dummy root object that wraps up a ValueExpr along with ownership of -// shared objects in the tree. -class RootExpr final : public ValueExpr { - public: - RootExpr(const RootExpr&) = delete; - RootExpr& operator=(const RootExpr&) = delete; - - static absl::StatusOr> Create( - std::unique_ptr value_expr, - std::unique_ptr root_data); - - absl::Status SetSchemasForEvaluation( - absl::Span params_schemas) override; - - bool Eval(absl::Span params, - EvaluationContext* context, VirtualTupleSlot* result, - absl::Status* status) const override; - - std::string DebugInternal(const std::string& indent, - bool verbose) const override; - - private: - enum ArgKind { kValueExpr }; - - RootExpr(std::unique_ptr value_expr, - std::unique_ptr root_data); - - // Returns the ValueExpr passed to the constructor. - const ValueExpr* value_expr() const; - ValueExpr* mutable_value_expr(); - - std::unique_ptr root_data_; -}; } // namespace zetasql #endif // ZETASQL_REFERENCE_IMPL_OPERATOR_H_ diff --git a/zetasql/reference_impl/reference_driver.cc b/zetasql/reference_impl/reference_driver.cc index 9485564e4..e7b058a30 100644 --- a/zetasql/reference_impl/reference_driver.cc +++ b/zetasql/reference_impl/reference_driver.cc @@ -133,9 +133,9 @@ ReferenceDriver::ReferenceDriver( statement_evaluation_timeout_(absl::Seconds( absl::GetFlag(FLAGS_reference_driver_query_eval_timeout_sec))) { if (absl::GetFlag(FLAGS_force_reference_product_mode_external) && - options.product_mode() != ProductMode::PRODUCT_EXTERNAL) { + language_options_.product_mode() != ProductMode::PRODUCT_EXTERNAL) { ABSL_LOG(WARNING) << "Overriding requested Reference ProductMode " - << ProductMode_Name(options.product_mode()) + << ProductMode_Name(language_options_.product_mode()) << " with PRODUCT_EXTERNAL."; language_options_.set_product_mode(ProductMode::PRODUCT_EXTERNAL); } @@ -247,6 +247,7 @@ absl::Status ReferenceDriver::CreateDatabase(const TestDatabase& test_db) { const TestTable& test_table = t.second; AddTableInternal(table_name, test_table); } + return absl::OkStatus(); } @@ -288,14 +289,12 @@ absl::Status ReferenceDriver::AddSqlUdfs( // Don't pre-rewrite function bodies. // TODO: In RQG mode, apply a random subset of rewriters. analyzer_options.set_enabled_rewrites({}); - // TODO: b/277368430 - Remove this rewriter once the reference implementation - // for UDA is fixed. - analyzer_options.enable_rewrite(REWRITE_INLINE_SQL_UDAS); for (const std::string& create_function : create_function_stmts) { - sql_udf_artifacts_.emplace_back(); + artifacts_.emplace_back(); ZETASQL_RETURN_IF_ERROR(AddFunctionFromCreateFunction( create_function, analyzer_options, /*allow_persistent_function=*/false, - function_options, sql_udf_artifacts_.back(), *catalog_.catalog())); + function_options, artifacts_.back(), *catalog_.catalog(), + *catalog_.catalog())); } return absl::OkStatus(); } @@ -313,7 +312,7 @@ absl::Status ReferenceDriver::AddViews( for (const std::string& create_view : create_view_stmts) { ZETASQL_RETURN_IF_ERROR(AddViewFromCreateView(create_view, analyzer_options, /*allow_non_temp=*/false, - sql_udf_artifacts_.emplace_back(), + artifacts_.emplace_back(), *catalog_.catalog())); } return absl::OkStatus(); @@ -480,9 +479,9 @@ absl::StatusOr ReferenceDriver::ExecuteScript( const std::string& sql, const std::map& parameters, TypeFactory* type_factory) { ExecuteStatementOptions options{.primary_key_mode = PrimaryKeyMode::DEFAULT}; - bool uses_unsupported_type = false; // unused + ExecuteScriptAuxOutput aux_output_ignored; return ExecuteScriptForReferenceDriver(sql, parameters, options, type_factory, - &uses_unsupported_type); + aux_output_ignored); } absl::StatusOr> @@ -515,7 +514,6 @@ ReferenceDriver::ExecuteStatementForReferenceDriverInternal( // If provide, uses this instead of calling analyzer. const AnalyzerOutput* analyzed_input, ExecuteStatementAuxOutput& aux_output) { - ABSL_CHECK(catalog_.catalog() != nullptr) << "Call CreateDatabase() first"; std::unique_ptr internal_catalog; @@ -871,6 +869,10 @@ class ReferenceDriverStatementEvaluator : public StatementEvaluatorImpl { // expression uses an unsupported type. bool uses_unsupported_type() const { return uses_unsupported_type_; } + // Returns false if any call to ExecuteStatement() returned a + // non-deterministic result. + bool is_deterministic_output() const { return is_deterministic_output_; } + private: StatementEvaluatorCallback evaluator_callback_; ScriptResult* result_; @@ -879,6 +881,7 @@ class ReferenceDriverStatementEvaluator : public StatementEvaluatorImpl { TypeFactory* type_factory_; ReferenceDriver* driver_; bool uses_unsupported_type_ = false; + bool is_deterministic_output_ = true; }; absl::Status ReferenceDriverStatementEvaluator::ExecuteStatement( @@ -923,6 +926,11 @@ absl::Status ReferenceDriverStatementEvaluator::ExecuteStatement( uses_unsupported_type_ |= *aux_output.uses_unsupported_type; } + if (aux_output.is_deterministic_output.has_value()) { + is_deterministic_output_ = + is_deterministic_output_ && *aux_output.is_deterministic_output; + } + result_->statement_results.push_back(std::move(result)); absl::Status status = result_->statement_results.back().result.status(); if (!status.ok() && status.code() != absl::StatusCode::kInternal) { @@ -944,25 +952,24 @@ absl::Status ExecuteScriptInternal(ScriptExecutor* executor) { absl::StatusOr ReferenceDriver::ExecuteScriptForReferenceDriver( absl::string_view sql, const std::map& parameters, const ExecuteStatementOptions& options, TypeFactory* type_factory, - bool* uses_unsupported_type) { + ExecuteScriptAuxOutput& aux_output) { ScriptResult result; ZETASQL_RETURN_IF_ERROR(ExecuteScriptForReferenceDriverInternal( - sql, parameters, options, type_factory, uses_unsupported_type, &result)); + sql, parameters, options, type_factory, aux_output, &result)); return result; } absl::Status ReferenceDriver::ExecuteScriptForReferenceDriverInternal( absl::string_view sql, const std::map& parameters, const ExecuteStatementOptions& options, TypeFactory* type_factory, - bool* uses_unsupported_type, ScriptResult* result) { + ExecuteScriptAuxOutput& aux_output, ScriptResult* result) { std::optional tmp_uses_unsupported_type; procedures_.clear(); // Clear procedures leftover from previous script. absl::StatusOr analyzer_options = GetAnalyzerOptions(parameters, tmp_uses_unsupported_type); - if (uses_unsupported_type != nullptr && - tmp_uses_unsupported_type.has_value()) { - *uses_unsupported_type = *tmp_uses_unsupported_type; + if (tmp_uses_unsupported_type.has_value()) { + aux_output.uses_unsupported_type = *tmp_uses_unsupported_type; } if (!analyzer_options.ok()) { return analyzer_options.status(); @@ -992,7 +999,8 @@ absl::Status ReferenceDriver::ExecuteScriptForReferenceDriverInternal( std::unique_ptr executor, ScriptExecutor::Create(sql, script_executor_options, &evaluator)); absl::Status status = ExecuteScriptInternal(executor.get()); - *uses_unsupported_type = evaluator.uses_unsupported_type(); + aux_output.uses_unsupported_type = evaluator.uses_unsupported_type(); + aux_output.is_deterministic_output = evaluator.is_deterministic_output(); ZETASQL_RETURN_IF_ERROR(status); return absl::OkStatus(); } diff --git a/zetasql/reference_impl/reference_driver.h b/zetasql/reference_impl/reference_driver.h index b8607cfb3..2907c2e7a 100644 --- a/zetasql/reference_impl/reference_driver.h +++ b/zetasql/reference_impl/reference_driver.h @@ -205,12 +205,23 @@ class ReferenceDriver : public TestDriver { const ExecuteStatementOptions& options, TypeFactory* type_factory, ExecuteStatementAuxOutput& aux_output, TestDatabase* database = nullptr); + struct ExecuteScriptAuxOutput { + // If this has a value, it indicates whether the reference evaluation + // engine detected non-determinism in any part of the script. + std::optional is_deterministic_output; + // If this has a value, it indicates the script included unsupported + // types. This will generally cause a failure of the query. + std::optional uses_unsupported_type; + }; // The same as ExecuteStatementForReferenceDriver(), except executes a script // instead of a statement. + // + // 'aux_output' contains additional information. These values may provided + // even in the cause of failures in some cases. absl::StatusOr ExecuteScriptForReferenceDriver( absl::string_view sql, const std::map& parameters, const ExecuteStatementOptions& options, TypeFactory* type_factory, - bool* uses_unsupported_type); + ExecuteScriptAuxOutput& aux_output); bool IsReferenceImplementation() const override { return true; } @@ -244,7 +255,7 @@ class ReferenceDriver : public TestDriver { // The LanguageOptions used by the zero-arg constructor. static LanguageOptions DefaultLanguageOptions(); - private: + protected: struct TableInfo { std::string table_name; std::set required_features; @@ -256,7 +267,7 @@ class ReferenceDriver : public TestDriver { absl::Status ExecuteScriptForReferenceDriverInternal( absl::string_view sql, const std::map& parameters, const ExecuteStatementOptions& options, TypeFactory* type_factory, - bool* uses_unsupported_type, ScriptResult* result); + ExecuteScriptAuxOutput& aux_output, ScriptResult* result); absl::StatusOr ExecuteStatementForReferenceDriverInternal( absl::string_view sql, const AnalyzerOptions& analyzer_options, @@ -291,7 +302,7 @@ class ReferenceDriver : public TestDriver { TestDatabaseCatalog catalog_; // Maintains lifetime of objects referenced by SQL UDFs added to catalog_. - std::vector> sql_udf_artifacts_; + std::vector> artifacts_; // Defaults to America/Los_Angeles. absl::TimeZone default_time_zone_; diff --git a/zetasql/reference_impl/reference_impl_all_rewrites_known_errors.textproto b/zetasql/reference_impl/reference_impl_all_rewrites_known_errors.textproto index f5c979bc1..3a5a42e20 100644 --- a/zetasql/reference_impl/reference_impl_all_rewrites_known_errors.textproto +++ b/zetasql/reference_impl/reference_impl_all_rewrites_known_errors.textproto @@ -36,7 +36,10 @@ known_errors { label: "unnest_multiway_test:multiway_unnest_one_null_array_default_mode" label: "unnest_multiway_test:multiway_unnest_one_null_array_pad_mode" label: "unnest_multiway_test:multiway_unnest_ten_unequal_arrays_column_ref_default_mode" + label: "unnest_multiway_test:multiway_unnest_ten_unequal_arrays_column_ref_null_mode" label: "unnest_multiway_test:multiway_unnest_ten_unequal_arrays_column_ref_pad_mode" + label: "unnest_multiway_test:multiway_unnest_ten_unequal_arrays_column_ref_strict_mode" + label: "unnest_multiway_test:multiway_unnest_ten_unequal_arrays_column_ref_truncate_mode" label: "unnest_multiway_test:multiway_unnest_three_unequal_arrays_column_ref_default_mode" label: "unnest_multiway_test:multiway_unnest_three_unequal_arrays_column_ref_pad_mode" label: "unnest_multiway_test:multiway_unnest_three_unequal_arrays_column_ref_truncate_mode" @@ -304,7 +307,7 @@ known_errors { } known_errors { mode: ALLOW_ERROR_OR_WRONG_ANSWER - reason: "TODO: Reference implementation reports non-determinism and rewritten there is none" + reason: "TODO: Reference implementation reports non-determinism and in the rewritten resolved AST there is none" label: "array_zip_test:array_zip_igore_order_array_all_elements_are_the_same" label: "array_zip_test:array_zip_igore_order_array_all_elements_are_the_same_lambda" } @@ -313,3 +316,34 @@ known_errors { reason: "TODO: After rewriter array is ordered instead of unordered" label: "array_path_test:flatten_deeply_nested_unordered_array" } + +known_errors { + mode: ALLOW_ERROR_OR_WRONG_ANSWER + reason: "TODO: Reference implementation reports non-determinism and in the rewritten resolved AST there is none" + label: "aggregation_order_by_queries_test:aggregate_order_by_correlated_column_1" + label: "aggregation_order_by_queries_test:aggregation_order_by_computed_order_by_exprs" + label: "aggregation_order_by_queries_test:aggregation_order_by_distinct" + label: "aggregation_order_by_queries_test:aggregation_order_by_distinct_limit" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_computed_order_by_exprs" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_desc" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_order_by_different_columns" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_param" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_same_values" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_test_table" + label: "aggregation_order_by_queries_test:aggregation_order_by_limit_without_group_by" + label: "aggregation_order_by_queries_test:aggregation_order_by_range_date" + label: "aggregation_order_by_queries_test:aggregation_order_by_range_datetime" + label: "aggregation_order_by_queries_test:aggregation_order_by_range_timestamp" + label: "aggregation_order_by_queries_test:aggregation_order_by_test_table" + label: "aggregation_order_by_queries_test:aggregation_order_by_with_nulls_first_last" +} +known_errors { + mode: ALLOW_ERROR_OR_WRONG_ANSWER + reason: "TODO: Reference implementation reports non-determinism and in the rewritten resolved AST there is none" + label: "with_recursive_test:recursive_query_prime_factorization" +} +known_errors { + mode: ALLOW_ERROR_OR_WRONG_ANSWER + reason: "TODO: Reference implementation reports non-determinism and in the rewritten resolved AST there is none" + label: "pivot_test:pivot_array_agg_ignore_nulls" +} diff --git a/zetasql/reference_impl/relational_op.cc b/zetasql/reference_impl/relational_op.cc index 1619590a6..5769c4efe 100644 --- a/zetasql/reference_impl/relational_op.cc +++ b/zetasql/reference_impl/relational_op.cc @@ -33,6 +33,7 @@ #include "zetasql/common/internal_value.h" #include "zetasql/common/thread_stack.h" +#include "zetasql/public/cast.h" #include "zetasql/public/catalog.h" #include "zetasql/public/evaluator_table_iterator.h" #include "zetasql/public/function_signature.h" @@ -5231,54 +5232,197 @@ absl::StatusOr> LoopOp::CreateIterator( lower_bound_val, upper_bound_val); } -// ------------------------------------------------------- -// RootOp -// ------------------------------------------------------- +namespace { + +// Iterates over the input from `input_iterator` and evaluates `condition` on +// it. If `condition` is true, returns the current tuple. Otherwise, returns +// nullptr and updates `status_` with an error. +// +// If `message` is not nullptr, it will be evaluated when the `condition` is +// violated and its output will be included in the error message. +class AssertTupleIterator : public TupleIterator { + public: + AssertTupleIterator(absl::Span params, + const ValueExpr* condition, const ValueExpr* message, + std::unique_ptr input_iterator, + EvaluationContext* context) + : params_(params.begin(), params.end()), + condition_(condition), + message_(message), + input_iterator_(std::move(input_iterator)), + context_(context) {} + + AssertTupleIterator(const AssertTupleIterator&) = delete; + AssertTupleIterator& operator=(const AssertTupleIterator&) = delete; + + const TupleSchema& Schema() const override { + return input_iterator_->Schema(); + } + + TupleData* Next() override { + TupleData* current = input_iterator_->Next(); + if (current == nullptr) { + status_ = input_iterator_->Status(); + return nullptr; + } + + TupleSlot condition_slot; + if (!condition_->EvalSimple( + ConcatSpans(absl::Span(params_), {current}), + context_, &condition_slot, &status_)) { + return nullptr; + } + + if (!condition_slot.value().type()->IsBool()) { + status_ = absl::InternalError("`condition` should be of the type bool"); + return nullptr; + } + + if (!condition_slot.value().is_null() && + condition_slot.value().bool_value()) { + // The assertion succeeds if and only if the condition is TRUE. + return current; + } + + // The assertion failed. Report an error by evaluating the `message` + // expression. + // + // When the assertion fails, the output tends to be non-deterministic + // because it evaluates the `message` expression, which depends on input + // rows. Always marking the output as non-deterministic when an assertion + // fails, however, would be too broad and cause many test cases to be + // skipped. + // + // Here we choose to not mark the output non-deterministic, and require the + // test cases to be carefully designed so that the output is deterministic. + TupleSlot message_slot; + if (!message_->EvalSimple( + ConcatSpans(absl::Span(params_), {current}), + context_, &message_slot, &status_)) { + return nullptr; + } + if (!message_slot.value().type()->IsString()) { + status_ = absl::InternalError("`message` should be of the type string"); + return nullptr; + } + // The message should never be NULL because each payload is wrapped with an + // IFNULL function call except for non-NULL literals. + if (message_slot.value().is_null()) { + status_ = absl::InternalError("`message` should not be NULL"); + return nullptr; + } + status_ = absl::OutOfRangeError( + absl::StrCat("Assert failed: ", message_slot.value().string_value())); + return nullptr; + } + + // AssertScan does not preserve order. + bool PreservesOrder() const override { return false; } + + absl::Status Status() const override { return status_; } + + std::string DebugString() const override { + return AssertOp::GetIteratorDebugString(input_iterator_->DebugString()); + } + + private: + const std::vector params_; + const ValueExpr* condition_; + const ValueExpr* message_; + + std::unique_ptr input_iterator_; + absl::Status status_; + EvaluationContext* context_; +}; + +} // namespace -absl::StatusOr> RootOp::Create( - std::unique_ptr input, std::unique_ptr root_data) { - return absl::WrapUnique(new RootOp(std::move(input), std::move(root_data))); +std::string AssertOp::GetIteratorDebugString( + absl::string_view input_iterator_debug_string) { + return absl::StrCat("AssertTupleIterator(", input_iterator_debug_string, ")"); +} + +absl::StatusOr> AssertOp::Create( + std::unique_ptr input, std::unique_ptr condition, + std::unique_ptr message) { + return absl::WrapUnique( + new AssertOp(std::move(input), std::move(condition), std::move(message))); } -absl::Status RootOp::SetSchemasForEvaluation( +absl::Status AssertOp::SetSchemasForEvaluation( absl::Span params_schemas) { - return mutable_input()->SetSchemasForEvaluation(params_schemas); + ZETASQL_RETURN_IF_ERROR(mutable_input()->SetSchemasForEvaluation(params_schemas)); + + // `message` and `condition` can access the columns of the input table. + std::vector all_schemas(params_schemas.begin(), + params_schemas.end()); + std::unique_ptr input_schema = input()->CreateOutputSchema(); + all_schemas.push_back(input_schema.get()); + + ZETASQL_RETURN_IF_ERROR(mutable_condition()->SetSchemasForEvaluation(all_schemas)); + ZETASQL_RETURN_IF_ERROR(mutable_message()->SetSchemasForEvaluation(all_schemas)); + return absl::OkStatus(); } -absl::StatusOr> RootOp::CreateIterator( +absl::StatusOr> AssertOp::CreateIterator( absl::Span params, int num_extra_slots, EvaluationContext* context) const { - return input()->CreateIterator(params, num_extra_slots, context); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr iter, + input()->CreateIterator(params, num_extra_slots, context)); + auto assert_iter = std::make_unique( + params, condition(), message(), std::move(iter), context); + return MaybeReorder(std::move(assert_iter), context); } -std::unique_ptr RootOp::CreateOutputSchema() const { +std::unique_ptr AssertOp::CreateOutputSchema() const { + // The output schema of an AssertOp is the same as the input schema. return input()->CreateOutputSchema(); } -std::string RootOp::IteratorDebugString() const { - return input()->IteratorDebugString(); +std::string AssertOp::DebugInternal(const std::string& indent, + bool verbose) const { + return absl::StrCat("AssertOp(", + ArgDebugString({"condition", "message", "input"}, + {k1, k1, k1}, indent, verbose), + ")"); } -std::string RootOp::DebugInternal(const std::string& indent, - bool verbose) const { - return absl::StrCat("RootOp(", - ArgDebugString({"input"}, {k1}, indent, verbose), ")"); +AssertOp::AssertOp(std::unique_ptr input, + std::unique_ptr condition, + std::unique_ptr message) { + SetArg(kInput, std::make_unique(std::move(input))); + SetArg(kCondition, std::make_unique(std::move(condition))); + SetArg(kMessage, std::make_unique(std::move(message))); } -RootOp::RootOp(std::unique_ptr input, - std::unique_ptr root_data) - : root_data_(std::move(root_data)) { - SetArg(kInput, std::make_unique(std::move(input))); +std::string AssertOp::IteratorDebugString() const { + return AssertOp::GetIteratorDebugString(input()->IteratorDebugString()); } -const RelationalOp* RootOp::input() const { +const RelationalOp* AssertOp::input() const { return GetArg(kInput)->node()->AsRelationalOp(); } -RelationalOp* RootOp::mutable_input() { +RelationalOp* AssertOp::mutable_input() { return GetMutableArg(kInput)->mutable_node()->AsMutableRelationalOp(); } +const ValueExpr* AssertOp::condition() const { + return GetArg(kCondition)->node()->AsValueExpr(); +} + +ValueExpr* AssertOp::mutable_condition() { + return GetMutableArg(kCondition)->mutable_node()->AsMutableValueExpr(); +} + +const ValueExpr* AssertOp::message() const { + return GetArg(kMessage)->node()->AsValueExpr(); +} + +ValueExpr* AssertOp::mutable_message() { + return GetMutableArg(kMessage)->mutable_node()->AsMutableValueExpr(); +} + namespace { // Iterates over the input from `input_iterator`. diff --git a/zetasql/reference_impl/relational_op_test.cc b/zetasql/reference_impl/relational_op_test.cc index 5362a3e3a..e5d628eb3 100644 --- a/zetasql/reference_impl/relational_op_test.cc +++ b/zetasql/reference_impl/relational_op_test.cc @@ -4598,40 +4598,6 @@ TEST_F(CreateIteratorTest, EnumerateOp) { EXPECT_FALSE(iter->PreservesOrder()); } -TEST_F(CreateIteratorTest, RootOp) { - VariableId a("a"); - std::vector test_values = - CreateTestTupleDatas({{Int64(1)}, {Int64(2)}}); - auto input = absl::WrapUnique( - new TestRelationalOp({a}, test_values, /*preserves_order=*/true)); - - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto root_op, - RootOp::Create(std::move(input), std::make_unique())); - EXPECT_EQ(root_op->IteratorDebugString(), "TestTupleIterator"); - EXPECT_EQ( - "RootOp(\n" - "+-input: TestRelationalOp)", - root_op->DebugString()); - std::unique_ptr output_schema = root_op->CreateOutputSchema(); - EXPECT_THAT(output_schema->variables(), ElementsAre(a)); - - ZETASQL_ASSERT_OK(root_op->SetSchemasForEvaluation(/*params_schemas=*/{})); - EvaluationContext context((EvaluationOptions())); - ZETASQL_ASSERT_OK_AND_ASSIGN( - std::unique_ptr iter, - root_op->CreateIterator(EmptyParams(), /*num_extra_slots=*/1, &context)); - EXPECT_EQ(iter->DebugString(), "TestTupleIterator"); - EXPECT_TRUE(iter->PreservesOrder()); - ZETASQL_ASSERT_OK_AND_ASSIGN(std::vector data, - ReadFromTupleIterator(iter.get())); - ASSERT_EQ(data.size(), 2); - EXPECT_THAT(data[0].slots(), - ElementsAre(IsTupleSlotWith(Int64(1), IsNull()), _)); - EXPECT_THAT(data[1].slots(), - ElementsAre(IsTupleSlotWith(Int64(2), IsNull()), _)); -} - // Builds a join between two relations with 'tuple_count' tuples each with // matching values and a relation1_tuple < relation2_tuple join condition. absl::StatusOr> BuildTestJoin(int tuple_count = 1) { diff --git a/zetasql/reference_impl/rewrite_flags.cc b/zetasql/reference_impl/rewrite_flags.cc index b2f0aabc9..14a975d84 100644 --- a/zetasql/reference_impl/rewrite_flags.cc +++ b/zetasql/reference_impl/rewrite_flags.cc @@ -48,15 +48,13 @@ absl::btree_set MinimalRewritesForReference() { // and anonymization rewriter are updated to follow the correct pattern. REWRITE_AGGREGATION_THRESHOLD, REWRITE_INLINE_SQL_TVFS, - // TODO: Remove this after resolving memory leak in direct UDA eval. - REWRITE_INLINE_SQL_UDAS, REWRITE_INLINE_SQL_VIEWS, // (broken link) end // clang-format on }; } -static const RewriteSet& MinimalRewrites() { +const RewriteSet& MinimalRewrites() { static const auto* minimal_rewrites = new RewriteSet({MinimalRewritesForReference()}); return *minimal_rewrites; diff --git a/zetasql/reference_impl/rewrite_flags.h b/zetasql/reference_impl/rewrite_flags.h index da202eb28..a75826f20 100644 --- a/zetasql/reference_impl/rewrite_flags.h +++ b/zetasql/reference_impl/rewrite_flags.h @@ -41,6 +41,8 @@ class RewriteSet : public absl::btree_set { std::string AbslUnparseFlag(RewriteSet set); bool AbslParseFlag(absl::string_view text, RewriteSet* set, std::string* error); +// RewriteSet that contains MinimalRewritesForReference() +const RewriteSet& MinimalRewrites(); } // namespace zetasql // Command-line flag to indicate which rewriters should be enabled. diff --git a/zetasql/reference_impl/tuple.h b/zetasql/reference_impl/tuple.h index 0393c2b8d..fd807c403 100644 --- a/zetasql/reference_impl/tuple.h +++ b/zetasql/reference_impl/tuple.h @@ -119,7 +119,7 @@ class ProtoFieldRegistry { public: // 'id' is a unique identifier of this instance that is used for debug // logging. - explicit ProtoFieldRegistry(int id) : id_(id) {} + ProtoFieldRegistry() = default; ProtoFieldRegistry(const ProtoFieldRegistry&) = delete; ProtoFieldRegistry& operator=(const ProtoFieldRegistry&) = delete; @@ -139,11 +139,7 @@ class ProtoFieldRegistry { return registered_access_infos_; } - int id() const { return id_; } - private: - const int id_; - // This is the set of fields that GetProtoFieldExprs care about. Not owned. std::vector registered_access_infos_; }; diff --git a/zetasql/reference_impl/tuple_comparator.cc b/zetasql/reference_impl/tuple_comparator.cc index b498ccddb..9a976ad79 100644 --- a/zetasql/reference_impl/tuple_comparator.cc +++ b/zetasql/reference_impl/tuple_comparator.cc @@ -145,6 +145,7 @@ bool TupleComparator::operator()(const TupleData& t1, } } + bool use_inequality_comparison = true; if (collator != nullptr) { ABSL_DCHECK(v1.type()->IsString()); ABSL_DCHECK(v2.type()->IsString()); @@ -160,7 +161,7 @@ bool TupleComparator::operator()(const TupleData& t1, } } } else { - if (!v1.Equals(v2)) { + if (!v1.Equals(v2) && use_inequality_comparison) { if (key->is_descending()) { return v2.LessThan(v1); } else { @@ -176,6 +177,7 @@ bool TupleComparator::operator()(const TupleData& t1, const Value& v1 = t1.slot(slot_idx).value(); const Value& v2 = t2.slot(slot_idx).value(); + bool use_inequality_comparison = true; if (v1.is_null() || v2.is_null()) { if (v1.is_null() && v2.is_null()) { // NULLs are considered equal continue; @@ -184,7 +186,7 @@ bool TupleComparator::operator()(const TupleData& t1, return !v2.is_null(); } // ASC by default. - if (!v1.Equals(v2)) { + if (!v1.Equals(v2) && use_inequality_comparison) { return v1.LessThan(v2); } } diff --git a/zetasql/reference_impl/uda_evaluation.cc b/zetasql/reference_impl/uda_evaluation.cc index 113c01b43..9f4ef52b8 100644 --- a/zetasql/reference_impl/uda_evaluation.cc +++ b/zetasql/reference_impl/uda_evaluation.cc @@ -17,7 +17,6 @@ #include "zetasql/reference_impl/uda_evaluation.h" #include -#include #include #include @@ -27,6 +26,7 @@ #include "zetasql/public/type.h" #include "zetasql/public/value.h" #include "zetasql/reference_impl/evaluation.h" +#include "zetasql/reference_impl/function.h" #include "zetasql/reference_impl/operator.h" #include "zetasql/reference_impl/tuple.h" #include "zetasql/reference_impl/type_helpers.h" @@ -42,19 +42,20 @@ namespace zetasql { class UserDefinedAggregateFunctionEvaluator - : public AggregateFunctionEvaluator { + : public SqlDefinedAggregateFunctionEvaluator { public: UserDefinedAggregateFunctionEvaluator( std::unique_ptr algebrized_tree, - std::vector argument_names, - std::vector argument_is_aggregate) + std::vector argument_infos) : algebrized_tree_(std::move(algebrized_tree)), - argument_names_(std::move(argument_names)), - argument_is_aggregate_(std::move(argument_is_aggregate)) {} + argument_infos_(std::move(argument_infos)) {} ~UserDefinedAggregateFunctionEvaluator() override = default; - void SetEvaluationContext(EvaluationContext* context) override { + void SetEvaluationContext( + EvaluationContext* context, + absl::Span params) override { eval_context_ = context; + params_ = std::vector(params.begin(), params.end()); } absl::Status Reset() override { @@ -90,18 +91,20 @@ class UserDefinedAggregateFunctionEvaluator eval_context_->MakeChildContext(); local_context->set_active_group_rows(inputs_.get()); - if (!inputs_->IsEmpty()) { - auto first_row = inputs_->GetTuplePtrs()[0]; - for (int i = 0; i < argument_names_.size(); ++i) { - // NOT_AGGREGATE arguments should be mapped by value since they are - // represented by a FunctionArgumentRefExpr. These arguments have a - // constant value for each grouped rows, so we can just add them once. - if (!local_context->HasFunctionArgumentRef(argument_names_[i]) && - !argument_is_aggregate_[i]) { - ZETASQL_RETURN_IF_ERROR(local_context->AddFunctionArgumentRef( - argument_names_[i], first_row->slot(i).value())); - } + std::shared_ptr shared_state = + std::make_shared(); + for (const auto& info : argument_infos_) { + if (info.is_aggregate) { + continue; } + Value arg; + VirtualTupleSlot virtual_slot(&arg, &shared_state); + absl::Status status; + ZETASQL_RET_CHECK(info.expr != nullptr); + info.expr->Eval(params_, eval_context_, &virtual_slot, &status); + ZETASQL_RETURN_IF_ERROR(status); + ZETASQL_RETURN_IF_ERROR( + local_context->AddFunctionArgumentRef(info.argument_name, arg)); } ZETASQL_RETURN_IF_ERROR( @@ -127,8 +130,8 @@ class UserDefinedAggregateFunctionEvaluator private: std::unique_ptr algebrized_tree_; - std::vector argument_names_; - std::vector argument_is_aggregate_; + std::vector argument_infos_; + std::vector params_; std::unique_ptr memory_accountant_; std::unique_ptr inputs_; EvaluationContext* eval_context_; @@ -137,11 +140,9 @@ class UserDefinedAggregateFunctionEvaluator std::unique_ptr MakeUserDefinedAggregateFunctionEvaluator( std::unique_ptr algebrized_tree, - std::vector argument_names, - std::vector argument_is_aggregate) { + std::vector argument_infos) { return std::make_unique( - std::move(algebrized_tree), std::move(argument_names), - std::move(argument_is_aggregate)); + std::move(algebrized_tree), std::move(argument_infos)); } } // namespace zetasql diff --git a/zetasql/reference_impl/uda_evaluation.h b/zetasql/reference_impl/uda_evaluation.h index 52a8373e5..3763bce3b 100644 --- a/zetasql/reference_impl/uda_evaluation.h +++ b/zetasql/reference_impl/uda_evaluation.h @@ -32,11 +32,22 @@ namespace zetasql { +// Information about a UDA argument. +struct UdaArgumentInfo { + std::string argument_name; + + // False if the argument is declared "NOT AGGREGATE". + bool is_aggregate; + + // The expression evaluator for the argument is set when `!is_aggregate`, + // otherwise it is nullptr. + const ValueExpr* expr; +}; + std::unique_ptr MakeUserDefinedAggregateFunctionEvaluator( std::unique_ptr algebrized_tree, - std::vector argument_names, - std::vector argument_is_aggregate); + std::vector argument_infos); } // namespace zetasql diff --git a/zetasql/reference_impl/value_expr.cc b/zetasql/reference_impl/value_expr.cc index 900f8f81f..1e1c28a14 100644 --- a/zetasql/reference_impl/value_expr.cc +++ b/zetasql/reference_impl/value_expr.cc @@ -753,9 +753,7 @@ std::string GetProtoFieldExpr::DebugInternal(const std::string& indent, const ProtoFieldInfo& field_info = field_reader_->access_info().field_info; return absl::StrCat( "GetProtoFieldExpr(", (field_info.get_has_bit ? "has_" : ""), - field_info.descriptor->name(), ", ", proto_expr()->DebugString(), - " [fid=", field_reader_->id(), " rid=", field_reader_->registry_id(), - "])"); + field_info.descriptor->name(), ", ", proto_expr()->DebugString(), ")"); } // ------------------------------------------------------- @@ -2096,7 +2094,7 @@ absl::StatusOr DMLValueExpr::GetColumnValue(const ResolvedColumn& column, } absl::Status DMLValueExpr::PopulatePrimaryKeyRowMap( - const std::vector>& original_rows, + absl::Span> original_rows, absl::string_view duplicate_primary_key_error_prefix, EvaluationContext* context, PrimaryKeyRowMap* row_map, bool* has_primary_key) const { @@ -2278,7 +2276,7 @@ absl::Status DMLValueExpr::SetSchemasForColumnExprEvaluation() const { } absl::Status DMLValueExpr::EvalGeneratedColumnsByTopologicalOrder( - const std::vector& topologically_sorted_generated_column_id_list, + absl::Span topologically_sorted_generated_column_id_list, const absl::flat_hash_map& generated_columns_position_map, EvaluationContext* context, std::vector& row) const { for (int column_id : topologically_sorted_generated_column_id_list) { @@ -3368,7 +3366,7 @@ absl::Status DMLUpdateValueExpr::ProcessNestedUpdate( const ResolvedUpdateStmt* nested_update, absl::Span tuples_for_row, const ResolvedColumn& element_column, - const std::vector& original_elements, EvaluationContext* context, + absl::Span original_elements, EvaluationContext* context, std::vector* new_elements) const { ZETASQL_ASSIGN_OR_RETURN( const VariableId element_column_variable_id, @@ -3450,7 +3448,7 @@ absl::Status DMLUpdateValueExpr::ProcessNestedUpdate( absl::Status DMLUpdateValueExpr::ProcessNestedInsert( const ResolvedInsertStmt* nested_insert, absl::Span tuples_for_row, - const std::vector& original_elements, EvaluationContext* context, + absl::Span original_elements, EvaluationContext* context, std::vector* new_elements) const { const int64_t original_size_of_new_elements = new_elements->size(); @@ -4045,46 +4043,4 @@ absl::StatusOr DMLInsertValueExpr::GetDMLOutputValue( dml_returning_rows, context); } -// ------------------------------------------------------- -// RootExpr -// ------------------------------------------------------- - -absl::StatusOr> RootExpr::Create( - std::unique_ptr value_expr, - std::unique_ptr root_data) { - return absl::WrapUnique( - new RootExpr(std::move(value_expr), std::move(root_data))); -} - -absl::Status RootExpr::SetSchemasForEvaluation( - absl::Span params_schemas) { - return mutable_value_expr()->SetSchemasForEvaluation(params_schemas); -} - -bool RootExpr::Eval(absl::Span params, - EvaluationContext* context, VirtualTupleSlot* result, - absl::Status* status) const { - return value_expr()->Eval(params, context, result, status); -} - -std::string RootExpr::DebugInternal(const std::string& indent, - bool verbose) const { - return absl::StrCat("RootExpr(", value_expr()->DebugInternal(indent, verbose), - ")"); -} - -RootExpr::RootExpr(std::unique_ptr value_expr, - std::unique_ptr root_data) - : ValueExpr(value_expr->output_type()), root_data_(std::move(root_data)) { - SetArg(kValueExpr, std::make_unique(std::move(value_expr))); -} - -const ValueExpr* RootExpr::value_expr() const { - return GetArg(kValueExpr)->node()->AsValueExpr(); -} - -ValueExpr* RootExpr::mutable_value_expr() { - return GetMutableArg(kValueExpr)->mutable_node()->AsMutableValueExpr(); -} - } // namespace zetasql diff --git a/zetasql/reference_impl/value_expr_test.cc b/zetasql/reference_impl/value_expr_test.cc index 471acfcf5..7e43c9917 100644 --- a/zetasql/reference_impl/value_expr_test.cc +++ b/zetasql/reference_impl/value_expr_test.cc @@ -878,32 +878,6 @@ TEST_F(EvalTest, DerefExprNameNotFound) { StatusIs(absl::StatusCode::kInternal, HasSubstr("Missing name: v"))); } -TEST_F(EvalTest, RootExpr) { - VariableId p("p"); - ZETASQL_ASSERT_OK_AND_ASSIGN(auto deref_expr, DerefExpr::Create(p, proto_type_)); - ZETASQL_ASSERT_OK_AND_ASSIGN( - auto root_expr, - RootExpr::Create(std::move(deref_expr), std::make_unique())); - EXPECT_EQ(root_expr->DebugString(), "RootExpr($p)"); - - const TupleSchema params_schema({p}); - ZETASQL_ASSERT_OK(root_expr->SetSchemasForEvaluation({¶ms_schema})); - - std::vector params_shared_states; - const TupleData params_data = - CreateTestTupleData({GetProtoValue(1)}, ¶ms_shared_states); - EvaluationContext context((EvaluationOptions())); - TupleSlot result; - absl::Status status; - ASSERT_TRUE(root_expr->EvalSimple({¶ms_data}, &context, &result, &status)) - << status; - EXPECT_EQ(result.value(), GetProtoValue(1)); - const std::shared_ptr result_shared_state = - *result.mutable_shared_proto_state(); - EXPECT_THAT(result_shared_state, Pointee(Eq(nullopt))); - EXPECT_THAT(result_shared_state, HasRawPointer(params_shared_states[0])); -} - class DMLValueExprEvalTest : public EvalTest { public: DMLValueExprEvalTest() { @@ -1916,7 +1890,7 @@ class ProtoEvalTest : public ::testing::Test { // Checks presence of 'field_name' in 'msg' using a GetProtoFieldExpr. Crashes // on error. Value HasProtoFieldOrDie(const google::protobuf::Message* msg, - const std::string& field_name) { + absl::string_view field_name) { const ProtoType* proto_type = MakeProtoType(msg); absl::Cord bytes; ABSL_CHECK(msg->SerializePartialToCord(&bytes)); @@ -1976,7 +1950,7 @@ class ProtoEvalTest : public ::testing::Test { // actual proto value is represented by a parameter. absl::Status EvalGetProtoFieldExprs( std::vector> infos, - const std::vector& proto_slots, EvaluationContext* context, + absl::Span proto_slots, EvaluationContext* context, std::vector>* output_slots) { ZETASQL_RET_CHECK(!proto_slots.empty()); @@ -2043,16 +2017,14 @@ class ProtoEvalTest : public ::testing::Test { EvaluationContext* context, std::unique_ptr* registry, std::vector>* field_readers, std::vector>* exprs) { - *registry = std::make_unique( - /*id=*/0); + *registry = std::make_unique(); for (const auto& entry : infos) { const ProtoFieldAccessInfo* info = entry.first; const int count = entry.second; - const int id = field_readers->size(); field_readers->push_back( - std::make_unique(id, *info, registry->get())); + std::make_unique(*info, registry->get())); ProtoFieldReader* field_reader = field_readers->back().get(); for (int i = 0; i < count; ++i) { @@ -2070,8 +2042,7 @@ class ProtoEvalTest : public ::testing::Test { field_name = absl::StrCat("has_", field_name); } EXPECT_EQ(get_proto_field_expr->DebugString(), - absl::StrCat("GetProtoFieldExpr(", field_name, - ", $p [fid=", id, " rid=0])")); + absl::StrCat("GetProtoFieldExpr(", field_name, ", $p)")); exprs->push_back(std::move(get_proto_field_expr)); } diff --git a/zetasql/reference_impl/variable_generator.cc b/zetasql/reference_impl/variable_generator.cc index 4a57d165b..37e7c2e95 100644 --- a/zetasql/reference_impl/variable_generator.cc +++ b/zetasql/reference_impl/variable_generator.cc @@ -111,7 +111,7 @@ VariableId ColumnToVariableMapping::GetVariableNameFromColumn( const ResolvedColumn& column) { const absl::StatusOr status_or_id = LookupVariableNameForColumn(column); - if (status_or_id.ok()) return status_or_id.value(); + if (status_or_id.ok()) return std::move(status_or_id).value(); return AssignNewVariableToColumn(column); } diff --git a/zetasql/resolved_ast/BUILD b/zetasql/resolved_ast/BUILD index 3e05fc182..4259359bb 100644 --- a/zetasql/resolved_ast/BUILD +++ b/zetasql/resolved_ast/BUILD @@ -15,6 +15,7 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@com_google_protobuf//:protobuf.bzl", "py_proto_library") + # Placeholder: load py_proto_library # Placeholder: load py_binary load("//zetasql/resolved_ast:build_rules.bzl", "gen_resolved_ast_files") @@ -29,12 +30,12 @@ py_binary( python_version = "PY3", deps = [ ":resolved_ast_enums_py_pb2", + "//zetasql/parser:generator_utils", "@io_abseil_py//absl:app", "@io_abseil_py//absl/flags", "@io_abseil_py//absl/logging", "@jinja//:jinja2", - "@markupsafe//:markupsafe", - "//zetasql/parser:generator_utils", + "@markupsafe", ], ) @@ -325,13 +326,16 @@ cc_library( ":target_syntax", "//zetasql/analyzer:expr_matching_helpers", "//zetasql/analyzer:resolver", + "//zetasql/analyzer:set_operation_resolver_base", "//zetasql/base", "//zetasql/base:case", "//zetasql/base:check", "//zetasql/base:map_util", "//zetasql/base:ret_check", "//zetasql/base:status", + "//zetasql/base:stl_util", "//zetasql/common:thread_stack", + "//zetasql/parser:parse_tree", "//zetasql/public:analyzer", "//zetasql/public:builtin_function_cc_proto", "//zetasql/public:catalog", @@ -347,7 +351,10 @@ cc_library( "//zetasql/public/functions:datetime_cc_proto", "//zetasql/public/functions:differential_privacy_cc_proto", "//zetasql/public/functions:normalize_mode_cc_proto", + "//zetasql/public/types", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/cleanup", + "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/status", diff --git a/zetasql/resolved_ast/gen_resolved_ast.py b/zetasql/resolved_ast/gen_resolved_ast.py index dbadf452b..f09aca1e5 100644 --- a/zetasql/resolved_ast/gen_resolved_ast.py +++ b/zetasql/resolved_ast/gen_resolved_ast.py @@ -561,7 +561,7 @@ def __init__(self): def AddNode( self, name, - tag_id, # Next tag_id: 263 + tag_id, # Next tag_id: 265 parent, fields, is_abstract=False, @@ -4269,9 +4269,11 @@ def main(unused_argv): This statement: CREATE [OR REPLACE] [UNIQUE] [SEARCH | VECTOR] INDEX [IF NOT EXISTS] ON - [STORING (Expression, ...)] [UNNEST(path_expression) [[AS] alias] [WITH OFFSET [[AS] alias]], ...] - (path_expression [ASC|DESC], ...) [OPTIONS (name=value, ...)]; + (path_expression [ASC|DESC], ...) + [STORING (Expression, ...)] + [PARTITION BY partition_expression, ...] + [OPTIONS (name=value, ...)]; is the name of table being indexed. is a TableScan on the table being indexed. @@ -4287,6 +4289,7 @@ def main(unused_argv): to 'computed_columns_list' entries or the columns of 'table_scan'. has the expressions in the storing clause. + has the expressions in the partition by clause. has engine-specific directives for how and where to materialize this index. has computed columns derived from the columns of @@ -4335,6 +4338,12 @@ def main(unused_argv): tag_id=9, vector=True, ignorable=IGNORABLE_DEFAULT), + Field( + 'partition_by_list', + 'ResolvedExpr', + tag_id=13, + vector=True, + ignorable=IGNORABLE_DEFAULT), Field( 'option_list', 'ResolvedOption', @@ -9367,6 +9376,50 @@ def main(unused_argv): ], ) + gen.AddNode( + name='ResolvedStaticDescribeScan', + tag_id=251, + parent='ResolvedScan', + use_custom_debug_string=True, + comment=""" + This represents the pipe STATIC_DESCRIBE operator, which is controlled by + FEATURE_PIPE_STATIC_DESCRIBE. + + This scan is a no-op, that just stores the describe_text produced to show + the intermediate schema where this operator occurred. + + This describe_text is displayed in resolved AST DebugStrings (which is + used internally in analyzer tests), and is also meant to be displayed + through some engine-specific side-channel at query prepare time. + """, + fields=[ + Field('input_scan', 'ResolvedScan', tag_id=2, propagate_order=True), + Field('describe_text', SCALAR_STRING, ignorable=IGNORABLE, tag_id=3), + ], + ) + + gen.AddNode( + name='ResolvedAssertScan', + tag_id=252, + parent='ResolvedScan', + comment=""" + This represents the pipe ASSERT operator, which is controlled by + FEATURE_PIPE_ASSERT. + + `condition` is a boolean expression. + `message` is a string expression. + + `condition` is computed for each row. If it does not return true, + the assertion fails. Then `message` is evaluated and used as part + of the error message, following something like "Assert failed: ". + """, + fields=[ + Field('input_scan', 'ResolvedScan', tag_id=2), + Field('condition', 'ResolvedExpr', tag_id=3), + Field('message', 'ResolvedExpr', tag_id=4), + ], + ) + gen.AddNode( name='ResolvedBarrierScan', tag_id=261, @@ -9391,6 +9444,43 @@ def main(unused_argv): ], ) + gen.AddNode( + name='ResolvedCreateConnectionStmt', + tag_id=263, + parent='ResolvedCreateStatement', + comment=""" + This statement: + CREATE [OR REPLACE] [TEMP] CONNECTION + [IF NOT EXISTS] [OPTIONS (name=value, ...)] + + builds a new connection based on the inputs provided via the + the OPTIONS field. + + is the name of the fully qualified connection. + is the list of options for the connection. + """, + fields=[ + Field( + 'option_list', + 'ResolvedOption', + tag_id=2, + vector=True, + ignorable=IGNORABLE_DEFAULT, + ), + ], + ) + + gen.AddNode( + name='ResolvedAlterConnectionStmt', + tag_id=264, + parent='ResolvedAlterObjectStmt', + comment=""" + This statement: + ALTER CONNECTION [IF EXISTS] SET OPTIONS(...) + """, + fields=[], + ) + gen.Generate( input_file_paths=input_templates, output_file_paths=output_files, diff --git a/zetasql/resolved_ast/query_expression.cc b/zetasql/resolved_ast/query_expression.cc index e0615fb08..c4eaa401d 100644 --- a/zetasql/resolved_ast/query_expression.cc +++ b/zetasql/resolved_ast/query_expression.cc @@ -34,30 +34,10 @@ #include "zetasql/base/case.h" #include "zetasql/base/map_util.h" #include "zetasql/base/ret_check.h" +#include "zetasql/base/status_macros.h" namespace zetasql { -// Joins entries present in (with pairs as elements) separated by -// . While appending each pair we add the second element (if present) -// as an alias to the first element. -static std::string JoinListWithAliases( - absl::Span> list, - absl::string_view delimiter) { - std::string list_str; - bool first = true; - for (const auto& entry : list) { - if (!first) absl::StrAppend(&list_str, delimiter); - - if (entry.second.empty()) { - absl::StrAppend(&list_str, entry.first); - } else { - absl::StrAppend(&list_str, entry.first, " AS ", entry.second); - } - first = false; - } - return list_str; -} - absl::StatusOr QueryExpression::GetQueryType() const { if (set_op_scan_list_.empty()) { @@ -79,35 +59,25 @@ absl::StatusOr QueryExpression::GetQueryType() return QueryExpression::kCorrespondenceSetOpScan; } -void QueryExpression::set_corresponding_set_op_output_column_list( - std::vector> select_list) { - corresponding_set_op_output_column_list_ = std::move(select_list); -} +// Joins entries present in (with pairs as elements) separated by +// . While appending each pair we add the second element (if present) +// as an alias to the first element. +static std::string JoinListWithAliases( + absl::Span> list, + absl::string_view delimiter) { + std::string list_str; + bool first = true; + for (const auto& entry : list) { + if (!first) absl::StrAppend(&list_str, delimiter); -void QueryExpression::ClearAllClauses() { - with_list_.clear(); - select_list_.clear(); - select_as_modifier_.clear(); - query_hints_.clear(); - from_.clear(); - where_.clear(); - set_op_type_.clear(); - set_op_modifier_.clear(); - set_op_column_match_mode_.clear(); - set_op_column_propagation_mode_.clear(); - set_op_scan_list_.clear(); - corresponding_set_op_output_column_list_.clear(); - group_by_all_ = false; - group_by_list_.clear(); - group_by_hints_.clear(); - order_by_list_.clear(); - order_by_hints_.clear(); - limit_.clear(); - offset_.clear(); - anonymization_options_.clear(); - with_recursive_ = false; - pivot_.clear(); - unpivot_.clear(); + if (entry.second.empty()) { + absl::StrAppend(&list_str, entry.first); + } else { + absl::StrAppend(&list_str, entry.first, " AS ", entry.second); + } + first = false; + } + return list_str; } std::string QueryExpression::GetSQLQuery() const { @@ -207,14 +177,14 @@ std::string QueryExpression::GetSQLQuery() const { "BY "); // Legacy ROLLUP if (!rollup_column_id_list_.empty()) { - absl::StrAppend( - &sql, "ROLLUP(", - absl::StrJoin(rollup_column_id_list_, ", ", - [this](std::string* out, int column_id) { - absl::StrAppend( - out, zetasql_base::FindOrDie(group_by_list_, column_id)); - }), - ")"); + absl::StrAppend(&sql, "ROLLUP(", + absl::StrJoin(rollup_column_id_list_, ", ", + [this](std::string* out, int column_id) { + absl::StrAppend( + out, + GetGroupByColumnOrDie(column_id)); + }), + ")"); } else if (!grouping_set_id_list_.empty()) { // There are rollup, cube, or grouping sets in the group by clause. // a lambda expression to output a column list @@ -228,12 +198,11 @@ std::string QueryExpression::GetSQLQuery() const { absl::StrAppend(output, "("); } absl::StrAppend( - output, - absl::StrJoin(column_id_list, ", ", - [this](std::string* out, int column_id) { - absl::StrAppend( - out, zetasql_base::FindOrDie(group_by_list_, column_id)); - })); + output, absl::StrJoin(column_id_list, ", ", + [this](std::string* out, int column_id) { + absl::StrAppend( + out, GetGroupByColumnOrDie(column_id)); + })); if (column_id_list.size() > 1) { absl::StrAppend(output, ")"); } @@ -314,10 +283,6 @@ std::string QueryExpression::GetSQLQuery() const { return sql; } -bool QueryExpression::CanFormSQLQuery() const { - return !CanSetSelectClause(); -} - void QueryExpression::Wrap(absl::string_view alias) { ABSL_DCHECK(CanFormSQLQuery()); ABSL_DCHECK(!alias.empty()); @@ -349,10 +314,6 @@ bool QueryExpression::TrySetSelectClause( return true; } -void QueryExpression::ResetSelectClause() { - select_list_.clear(); -} - bool QueryExpression::TrySetFromClause(absl::string_view from) { if (!CanSetFromClause()) { return false; @@ -407,17 +368,6 @@ bool QueryExpression::TrySetGroupByClause( return true; } -absl::Status QueryExpression::SetGroupByAllClause( - const std::map& group_by_list, - absl::string_view group_by_hints) { - ZETASQL_RET_CHECK(CanSetGroupByClause()); - group_by_all_ = true; - group_by_list_ = group_by_list; - ABSL_DCHECK(group_by_hints_.empty()); - group_by_hints_ = group_by_hints; - return absl::OkStatus(); -} - bool QueryExpression::TrySetOrderByClause( const std::vector& order_by_list, absl::string_view order_by_hints) { @@ -471,9 +421,9 @@ bool QueryExpression::TrySetUnpivotClause(absl::string_view unpivot) { return true; } -bool QueryExpression::CanSetWithClause() const { - return !HasWithClause(); -} +bool QueryExpression::CanFormSQLQuery() const { return !CanSetSelectClause(); } + +bool QueryExpression::CanSetWithClause() const { return !HasWithClause(); } bool QueryExpression::CanSetSelectClause() const { return !HasSelectClause() && !HasSetOpScanList(); } @@ -492,15 +442,16 @@ bool QueryExpression::CanSetGroupByClause() const { } bool QueryExpression::CanSetOrderByClause() const { return !HasOrderByClause() && !HasLimitClause() && !HasOffsetClause() && - HasFromClause(); + HasFromClause(); } bool QueryExpression::CanSetLimitClause() const { return !HasLimitClause() && !HasOffsetClause(); } bool QueryExpression::CanSetOffsetClause() const { return !HasOffsetClause(); } - +bool QueryExpression::CanSetWithAnonymizationClause() const { + return !HasWithAnonymizationClause(); +} bool QueryExpression::CanSetPivotClause() const { return !HasPivotClause(); } - bool QueryExpression::CanSetUnpivotClause() const { return !HasUnpivotClause(); } @@ -517,9 +468,6 @@ QueryExpression::SelectList() const { return select_list_; } -bool QueryExpression::CanSetWithAnonymizationClause() const { - return !HasWithAnonymizationClause(); -} static bool HasDuplicateAliases( const absl::flat_hash_map& aliases) { @@ -590,4 +538,41 @@ void QueryExpression::SetSelectAsModifier(absl::string_view modifier) { select_as_modifier_ = modifier; } +absl::Status QueryExpression::SetGroupByAllClause( + const std::map& group_by_list, + absl::string_view group_by_hints) { + ZETASQL_RET_CHECK(CanSetGroupByClause()); + group_by_all_ = true; + group_by_list_ = group_by_list; + ABSL_DCHECK(group_by_hints_.empty()); + group_by_hints_ = group_by_hints; + return absl::OkStatus(); +} + +void QueryExpression::ClearAllClauses() { + with_list_.clear(); + select_list_.clear(); + select_as_modifier_.clear(); + query_hints_.clear(); + from_.clear(); + where_.clear(); + set_op_type_.clear(); + set_op_modifier_.clear(); + set_op_column_match_mode_.clear(); + set_op_column_propagation_mode_.clear(); + set_op_scan_list_.clear(); + corresponding_set_op_output_column_list_.clear(); + group_by_all_ = false; + group_by_list_.clear(); + group_by_hints_.clear(); + order_by_list_.clear(); + order_by_hints_.clear(); + limit_.clear(); + offset_.clear(); + anonymization_options_.clear(); + with_recursive_ = false; + pivot_.clear(); + unpivot_.clear(); +} + } // namespace zetasql diff --git a/zetasql/resolved_ast/query_expression.h b/zetasql/resolved_ast/query_expression.h index b68a5c6be..d343db5ab 100644 --- a/zetasql/resolved_ast/query_expression.h +++ b/zetasql/resolved_ast/query_expression.h @@ -28,6 +28,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "zetasql/base/map_util.h" namespace zetasql { @@ -50,10 +51,7 @@ class QueryExpression { // internal state of the query expression is inconsistent. absl::StatusOr GetQueryType() const; - // Returns true if the clauses necessary to form a SQL query, i.e. select_list - // or set_op_scan_list, are filled in QueryExpression. Otherwise false. - bool CanFormSQLQuery() const; - + // Returns the SQL query represented by this QueryExpression. std::string GetSQLQuery() const; // Mutates the QueryExpression, wrapping its previous form as a subquery in @@ -82,9 +80,6 @@ class QueryExpression { absl::string_view group_by_hints, const std::vector& grouping_set_id_list, const std::vector& rollup_column_id_list); - absl::Status SetGroupByAllClause( - const std::map& group_by_list, - absl::string_view group_by_hints); bool TrySetOrderByClause(const std::vector& order_by_list, absl::string_view order_by_hints); bool TrySetLimitClause(absl::string_view limit); @@ -93,6 +88,10 @@ class QueryExpression { bool TrySetPivotClause(absl::string_view pivot); bool TrySetUnpivotClause(absl::string_view unpivot); + // Returns true if the clauses necessary to form a SQL query, i.e. select_list + // or set_op_scan_list, are filled in QueryExpression. Otherwise false. + bool CanFormSQLQuery() const; + // The below CanSet... methods return true if filling in the concerned clause // in the QueryExpression will succeed (without mutating it or wrapping it as // a subquery). Otherwise false. @@ -128,14 +127,24 @@ class QueryExpression { return !anonymization_options_.empty(); } - void ResetSelectClause(); + bool HasGroupByColumn(int column_id) const { + return zetasql_base::ContainsKey(group_by_list_, column_id); + } - const std::string FromClause() const { return from_; } + absl::string_view FromClause() const { return from_; } // Returns an immutable reference to select_list_. For QueryExpression built // from a SetOp scan, it returns the select_list_ of its first subquery. const std::vector>& SelectList() const; + const std::map& GroupByList() const { + return group_by_list_; + } + + std::string GetGroupByColumnOrDie(int column_id) const { + return zetasql_base::FindOrDie(group_by_list_, column_id); + } + // Updates the aliases of the output columns if their indexes appear in // `aliases`. If this query_expression corresponds to a set operation with // CORRESPONDING, each of its query_expression(s) corresponding to its set @@ -149,26 +158,28 @@ class QueryExpression { // Set the AS modifier for the SELECT. e.g. "AS VALUE". void SetSelectAsModifier(absl::string_view modifier); - // Returns a mutable pointer to the group_by_list_ of QueryExpression. Used - // mostly to update the sql text of the group_by columns to reflect the - // ordinal position of select clause. - std::map* MutableGroupByList() { return &group_by_list_; } + void SetGroupByColumn(int column_id, std::string column_name) { + group_by_list_.insert_or_assign(column_id, column_name); + } - // Returns a mutable pointer to the from_ clause of QueryExpression. Used - // while building sql for a sample scan so as to rewrite the from_ clause to - // include the TABLESAMPLE clause. - std::string* MutableFromClause() { return &from_; } + absl::Status SetGroupByAllClause( + const std::map& group_by_list, + absl::string_view group_by_hints); + + void SetFromClause(std::string from) { from_ = from; } - // Returns a mutable pointer to the select_list_ of QueryExpression. Used - // while building sql for a sample scan that has a WITH WEIGHT clause. - std::vector>* MutableSelectList() { - return &select_list_; + void AppendSelectColumn(std::string column, std::string alias) { + select_list_.emplace_back(column, alias); } // Set the `corresponding_set_op_output_column_list` field for set operations // with column_match_mode = CORRESPONDING. - void set_corresponding_set_op_output_column_list( - std::vector> select_list); + void SetCorrespondingSetOpOutputColumnList( + std::vector> select_list) { + corresponding_set_op_output_column_list_ = std::move(select_list); + } + + void ResetSelectClause() { select_list_.clear(); } private: void ClearAllClauses(); diff --git a/zetasql/resolved_ast/resolved_ast.cc.template b/zetasql/resolved_ast/resolved_ast.cc.template index 15e38324e..b63dc599e 100644 --- a/zetasql/resolved_ast/resolved_ast.cc.template +++ b/zetasql/resolved_ast/resolved_ast.cc.template @@ -1138,6 +1138,12 @@ const ResolvedNodeKind {{node.name}}::TYPE; {{node.extra_enum_defs}} {{node.name}}::~{{node.name}}() { + {% set ns = namespace(has_vector_or_ptr= + (node.fields | selectattr('is_node_vector') | list | length > 0) or + (node.fields | selectattr('is_node_ptr') | list | length > 0) + ) %} + {% if ns.has_vector_or_ptr %} + {% endif %} } absl::Status {{node.name}}::SaveTo( diff --git a/zetasql/resolved_ast/resolved_column.h b/zetasql/resolved_ast/resolved_column.h index b9ac6ecba..912b3ca4c 100644 --- a/zetasql/resolved_ast/resolved_column.h +++ b/zetasql/resolved_ast/resolved_column.h @@ -51,7 +51,17 @@ class ResolvedColumn { ResolvedColumn(const ResolvedColumn&) = default; ResolvedColumn& operator=(const ResolvedColumn&) = default; - // Construct a ResolvedColumn with the given and . + // Construct a ResolvedColumn with the given and + // { or <}. + // + // WARNING: The constructor that takes will not propagate collation or + // other type annotations. When creating a new ResolvedColumn from an + // existing ResolvedColumn or ResolvedExpr, it is necessary to use + // the constructor to avoid bugs where annotations are lost. + // + // TODO: Maybe the constructor should be deprecated, or + // we should find another way to make sure the safe one is used where needed. + // // and are for display only, have no defined meaning and // are required to be non-empty. ResolvedColumn(int column_id, IdString table_name, IdString name, diff --git a/zetasql/resolved_ast/resolved_node.cc b/zetasql/resolved_ast/resolved_node.cc index e199cdacf..670e7f7c7 100644 --- a/zetasql/resolved_ast/resolved_node.cc +++ b/zetasql/resolved_ast/resolved_node.cc @@ -805,4 +805,25 @@ FunctionEnums::Volatility ResolvedCreateFunctionStmt::volatility() const { } } +void ResolvedStaticDescribeScan::CollectDebugStringFields( + std::vector* fields) const { + SUPER::CollectDebugStringFields(fields); + + if (!describe_text_.empty()) { + // Show describe_text contents directly, rather than the normal way + // strings get shown as quoted values on a single line, with + // escaped newlines. + fields->emplace_back(/*name=*/"describe_text", describe_text_, + describe_text_accessed()); + } + if (input_scan_ != nullptr) { + fields->emplace_back(/*name=*/"input_scan", input_scan_.get(), + input_scan_accessed()); + } +} + +std::string ResolvedStaticDescribeScan::GetNameForDebugString() const { + return SUPER::GetNameForDebugString(); +} + } // namespace zetasql diff --git a/zetasql/resolved_ast/rewrite_utils.cc b/zetasql/resolved_ast/rewrite_utils.cc index e688bf6ed..e91ceec15 100644 --- a/zetasql/resolved_ast/rewrite_utils.cc +++ b/zetasql/resolved_ast/rewrite_utils.cc @@ -17,6 +17,7 @@ #include "zetasql/resolved_ast/rewrite_utils.h" #include +#include #include #include #include @@ -44,9 +45,11 @@ #include "zetasql/resolved_ast/resolved_node.h" #include "absl/algorithm/container.h" #include "absl/container/flat_hash_set.h" +#include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" #include "absl/types/span.h" @@ -426,6 +429,13 @@ FunctionCallBuilder::IsNull(std::unique_ptr arg) { ResolvedFunctionCall::DEFAULT_ERROR_MODE); } +absl::StatusOr> +FunctionCallBuilder::IsNotNull(std::unique_ptr arg) { + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr is_null, + IsNull(std::move(arg))); + return Not(std::move(is_null)); +} + absl::StatusOr> FunctionCallBuilder::AnyIsNull( std::vector> args) { @@ -503,7 +513,8 @@ FunctionCallBuilder::Error(std::unique_ptr error_expr, absl::StatusOr> FunctionCallBuilder::MakeArray( const Type* element_type, - std::vector> elements) { + std::vector> elements, + bool cast_elements_if_needed) { ZETASQL_RET_CHECK(element_type != nullptr); const Function* make_array_fn = nullptr; ZETASQL_RETURN_IF_ERROR(GetBuiltinFunctionFromCatalog("$make_array", &make_array_fn)); @@ -527,9 +538,19 @@ FunctionCallBuilder::MakeArray( catalog_signature->context_id(), catalog_signature->options()); + std::vector> cast_elements; + for (auto& element : elements) { + const bool cast_needed = + cast_elements_if_needed && !element->type()->Equals(element_type); + cast_elements.push_back( + cast_needed ? MakeResolvedCast(element_type, std::move(element), + /*return_null_on_error=*/false) + : std::move(element)); + } + std::unique_ptr resolved_function = MakeResolvedFunctionCall(array_type, make_array_fn, make_array_signature, - std::move(elements), + std::move(cast_elements), ResolvedFunctionCall::DEFAULT_ERROR_MODE); ZETASQL_RETURN_IF_ERROR(annotation_propagator_.CheckAndPropagateAnnotations( /*error_node=*/nullptr, resolved_function.get())); @@ -1058,6 +1079,35 @@ FunctionCallBuilder::Concat( return resolved_function; } +absl::StatusOr> +FunctionCallBuilder::MakeNullIfEmptyArray( + ColumnFactory& column_factory, + std::unique_ptr array_expr) { + ZETASQL_RET_CHECK(array_expr != nullptr); + const Type* array_type = array_expr->type(); + ZETASQL_RET_CHECK(array_type->IsArray()); + // TODO: We should support DeferredComputedColumns here. + ResolvedColumn out_column = + column_factory.MakeCol("null_if_empty_array", "$out", array_type); + ZETASQL_ASSIGN_OR_RETURN( + std::unique_ptr array_length, + ArrayLength(MakeResolvedColumnRef(out_column.type(), out_column, + /*is_correlated=*/false))); + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr array_non_empty, + GreaterOrEqual(std::move(array_length), + MakeResolvedLiteral(Value::Int64(1)))); + return ResolvedWithExprBuilder() + .add_assignment_list(ResolvedComputedColumnBuilder() + .set_column(out_column) + .set_expr(std::move(array_expr))) + .set_expr(If(std::move(array_non_empty), + MakeResolvedColumnRef(out_column.type(), out_column, + /*is_correlated=*/false), + MakeResolvedLiteral(Value::Null(array_type)))) + .set_type(array_type) + .Build(); +} + namespace { absl::StatusOr GetBinaryFunctionSignatureFromArgumentTypes( const Function* function, const Type* left_expr_type, @@ -1080,7 +1130,12 @@ absl::StatusOr GetBinaryFunctionSignatureFromArgumentTypes( } if (left_expr_type->Equals(function_argument_type_list[0].type()) && right_expr_type->Equals(function_argument_type_list[1].type())) { - return signature; + // If left and right have types, we make them concrete by setting + // `num_occurrences` to 1. + return FunctionSignature({signature.result_type().type(), 1}, + {{function_argument_type_list[0].type(), 1}, + {function_argument_type_list[1].type(), 1}}, + signature.context_id(), signature.options()); } } ZETASQL_RET_CHECK_FAIL() << "No builtin function with name " << function->Name() @@ -1261,10 +1316,46 @@ FunctionCallBuilder::NaryLogic( } absl::StatusOr> -FunctionCallBuilder::Int64Add( +FunctionCallBuilder::NestedBinaryOp( + absl::string_view op_catalog_name, FunctionSignatureId op_function_id, + std::vector> expressions, + const Type* expr_type) { + ZETASQL_RET_CHECK_GE(expressions.size(), 2); + ZETASQL_RET_CHECK(absl::c_all_of(expressions, [expr_type](const auto& expr) { + return expr->type()->Equals(expr_type); + })); + + const Function* fn = nullptr; + ZETASQL_RETURN_IF_ERROR(GetBuiltinFunctionFromCatalog(op_catalog_name, &fn)); + + FunctionSignature signature( + {expr_type, 1}, + {{expr_type, FunctionArgumentType::REPEATED, /*num_occurrences=*/2}}, + op_function_id); + std::unique_ptr result = std::move(expressions[0]); + auto function_call_info = std::make_shared(); + for (size_t i = 1; i < expressions.size(); ++i) { + std::vector> args(2); + args[0] = std::move(result); + args[1] = std::move(expressions[i]); + ZETASQL_ASSIGN_OR_RETURN( + result, ResolvedFunctionCallBuilder() + .set_type(expr_type) + .set_function(fn) + .set_signature(signature) + .set_argument_list(std::move(args)) + .set_error_mode(ResolvedFunctionCall::DEFAULT_ERROR_MODE) + .set_function_call_info(function_call_info) + .Build()); + } + return absl::WrapUnique(result.release()->GetAs()); +} + +absl::StatusOr> +FunctionCallBuilder::NestedBinaryInt64Add( std::vector> expressions) { - return NaryLogic("$add", FN_ADD_INT64, std::move(expressions), - types::Int64Type()); + return NestedBinaryOp("$add", FN_ADD_INT64, std::move(expressions), + types::Int64Type()); } absl::StatusOr> @@ -1392,6 +1483,68 @@ FunctionCallBuilder::ArrayAtOffset( return resolved_function; } +absl::StatusOr> +FunctionCallBuilder::ArrayToString( + std::unique_ptr array_expr, + std::unique_ptr delimiter_expr) { + ZETASQL_RET_CHECK(array_expr != nullptr); + ZETASQL_RET_CHECK(delimiter_expr != nullptr); + ZETASQL_RET_CHECK(array_expr->type()->IsArray()); + ZETASQL_RET_CHECK(delimiter_expr->type()->IsString() || + delimiter_expr->type()->IsBytes()); + ZETASQL_RET_CHECK_EQ(array_expr->type()->AsArray()->element_type(), + delimiter_expr->type()); + + const Function* array_to_string_fn = nullptr; + ZETASQL_RETURN_IF_ERROR( + GetBuiltinFunctionFromCatalog("array_to_string", &array_to_string_fn)); + + ZETASQL_RET_CHECK_EQ(array_to_string_fn->signatures().size(), 2); + const FunctionSignature* catalog_signature = nullptr; + for (const FunctionSignature& signature : array_to_string_fn->signatures()) { + if (signature.result_type().type() == delimiter_expr->type()) { + catalog_signature = &signature; + break; + } + } + ZETASQL_RET_CHECK(catalog_signature != nullptr); + ZETASQL_RET_CHECK_EQ(catalog_signature->arguments().size(), 3); + + FunctionArgumentType result_type( + array_expr->type()->AsArray()->element_type(), + catalog_signature->result_type().options(), + /*num_occurrences=*/1); + FunctionArgumentType array_arg(array_expr->type(), + catalog_signature->argument(0).options(), + /*num_occurrences=*/1); + FunctionArgumentType delimiter_arg(delimiter_expr->type(), + catalog_signature->argument(1).options(), + /*num_occurrences=*/1); + + FunctionSignature concrete_signature(result_type, {array_arg, delimiter_arg}, + catalog_signature->context_id(), + catalog_signature->options()); + std::vector> args; + args.push_back(std::move(array_expr)); + args.push_back(std::move(delimiter_expr)); + + ZETASQL_ASSIGN_OR_RETURN( + std::unique_ptr resolved_function, + ResolvedFunctionCallBuilder() + .set_type(result_type.type()) + .set_function(array_to_string_fn) + .set_signature(concrete_signature) + .set_argument_list(std::move(args)) + .set_error_mode(ResolvedFunctionCall::DEFAULT_ERROR_MODE) + .set_function_call_info(std::make_shared()) + .Build()); + + ZETASQL_RETURN_IF_ERROR(annotation_propagator_.CheckAndPropagateAnnotations( + /*error_node=*/nullptr, + const_cast(resolved_function.get()))); + return resolved_function; +} + // Returns the FunctionSignatureId of MOD corresponding to `input_type`. static absl::StatusOr GetModSignatureIdForInputType( const Function* mod_fn, const Type* input_type) { @@ -1565,6 +1718,42 @@ FunctionCallBuilder::AnyValue( .Build(); } +absl::StatusOr> +FunctionCallBuilder::ArrayAgg( + std::unique_ptr input_expr, + std::unique_ptr having_expr, + ResolvedAggregateHavingModifier::HavingModifierKind having_kind) { + ZETASQL_RET_CHECK(input_expr != nullptr); + const Type* input_type = input_expr->type(); + const ArrayType* array_type; + ZETASQL_RETURN_IF_ERROR(type_factory_.MakeArrayType(input_type, &array_type)); + const Function* array_agg_fn = nullptr; + ZETASQL_RETURN_IF_ERROR(GetBuiltinFunctionFromCatalog("array_agg", &array_agg_fn)); + ZETASQL_RET_CHECK_EQ(array_agg_fn->signatures().size(), 1); + const FunctionSignature* signature = array_agg_fn->GetSignature(0); + FunctionArgumentTypeList args_types; + args_types.emplace_back(input_type, signature->argument(0).options(), + /*num_occurrences=*/1); + FunctionSignature array_agg_fn_sig( + /*result_type=*/FunctionArgumentType(array_type, + signature->result_type().options(), + /*num_occurrences=*/1), + args_types, FN_ARRAY_AGG); + + auto builder = ResolvedAggregateFunctionCallBuilder() + .set_type(array_type) + .set_function(array_agg_fn) + .set_signature(array_agg_fn_sig) + .add_argument_list(std::move(input_expr)) + .set_error_mode(ResolvedFunctionCall::DEFAULT_ERROR_MODE); + if (having_expr != nullptr) { + builder.set_having_modifier(ResolvedAggregateHavingModifierBuilder() + .set_having_expr(std::move(having_expr)) + .set_kind(having_kind)); + } + return std::move(builder).Build(); +} + absl::StatusOr CatalogSupportsBuiltinFunction( absl::string_view function_name, const AnalyzerOptions& analyzer_options, Catalog& catalog) { diff --git a/zetasql/resolved_ast/rewrite_utils.h b/zetasql/resolved_ast/rewrite_utils.h index 5f075910e..fea4c7008 100644 --- a/zetasql/resolved_ast/rewrite_utils.h +++ b/zetasql/resolved_ast/rewrite_utils.h @@ -256,6 +256,13 @@ class FunctionCallBuilder { absl::StatusOr> IsNull( std::unique_ptr arg); + // Construct ResolvedFunctionCall for IS NOT NULL + // + // The signature for the built-in functions "$is_null" and "$not"must be + // available in or an error status is returned. + absl::StatusOr> IsNotNull( + std::unique_ptr arg); + // Construct a ResolvedFunctionCall for arg[0] IS NULL OR arg[1] IS NULL OR .. // // Like `IsNull`, a built-in function "$is_null" must be available. @@ -294,15 +301,18 @@ class FunctionCallBuilder { const Type* target_type = nullptr); // Constructs a ResolvedFunctionCall for the $make_array function to create an - // array for a list of elements + // array for a list of elements. If `cast_elements_if_needed` is true, the + // elements will be coerced to the element type of the array. // - // Requires: Each element in elements must have the same type as element_type + // Requires: If `cast_elements_if_needed` is false, each element in `elements` + // must have the same type as `element_type`. // // The signature for the built-in function "$make_array" must be available in // or an error status is returned absl::StatusOr> MakeArray( const Type* element_type, - std::vector> elements); + std::vector> elements, + bool cast_elements_if_needed = false); // Constructs a ResolvedFunctionCall for ARRAY_CONCAT(arrays...) // @@ -491,15 +501,17 @@ class FunctionCallBuilder { absl::StatusOr> Or( std::vector> expressions); - // Construct a ResolvedFunctionCall for - // expressions[0] ADD expressions[1] ADD ... ADD expressions[N-1] + // Construct a ResolvedFunctionCall which is a nested series of binary + // addition: + // ((expressions[0] ADD expressions[1]) ADD ... ADD expressions[N-1]) // where N is the number of expressions. // // Requires: N >= 2 and all expressions return INT64. // // The signature for the built-in function "$add" must be available in // or an error status is returned. - absl::StatusOr> Int64Add( + absl::StatusOr> + NestedBinaryInt64Add( std::vector> expressions); // Construct a ResolvedFunctionCall for ARRAY_LENGTH(array_expr). @@ -526,6 +538,20 @@ class FunctionCallBuilder { std::unique_ptr array_expr, std::unique_ptr offset_expr); + // Constructs a ResolvedFunctionCall for ARRAY_TO_STRING(array_expr, + // delimiter_expr). + // + // Requires: + // - `array_expr` is ARRAY of STRING or BYTES. + // - `delimiter_expr` is STRING or BYTES. + // - `array_expr`'s element type and `delimiter_expr` have the same SQL type. + // + // The signature for the built-in function "array_to_string" must be + // available in or an error status is returned. + absl::StatusOr> ArrayToString( + std::unique_ptr array_expr, + std::unique_ptr delimiter_expr); + // Constructs a ResolvedFunctionCall for the MOD(dividend, divisor). // // Requires: `dividend_expr` and `divisor_expr` must be of the same type and @@ -566,6 +592,22 @@ class FunctionCallBuilder { std::unique_ptr input_expr, std::unique_ptr having_min_expr); + // Constructs a ResolvedAggregateFunctionCall for + // `ARRAY_AGG(input_expr [HAVING having_kind having_expr])`. + // If `having_expr` is nullptr, no HAVING clause is added. + // + // Requires: + // - `input_expr` has non-ARRAY type. + // - `having_expr` has type that supports ordering. + // + // The signature for the built-in function "array_agg" must be + // available in or an error status is returned. + absl::StatusOr> ArrayAgg( + std::unique_ptr input_expr, + std::unique_ptr having_expr, + ResolvedAggregateHavingModifier::HavingModifierKind having_kind = + ResolvedAggregateHavingModifier::INVALID); + // Constructs a ResolvedFunctionCall for `CONCAT(input_expr[, input_expr, // ...])`. // @@ -577,6 +619,22 @@ class FunctionCallBuilder { absl::StatusOr> Concat( std::vector> elements); + // Constructs an expression that generates null if an array is empty, + // otherwise returns the array. + // + // Generates: + // WITH($result AS , + // IF(ARRAY_LENGTH($result) >= 1, $result, NULL)) + // + // Requires: `array_expr` is of ARRAY type. + // + // The signature for the built-in function "array_length", "not", and + // "$greater_or_equal" must be available in `catalog_` or an error status is + // returned. + absl::StatusOr> MakeNullIfEmptyArray( + ColumnFactory& column_factory, + std::unique_ptr array_expr); + private: static AnnotationPropagator BuildAnnotationPropagator( const AnalyzerOptions& analyzer_options, TypeFactory& type_factory) { @@ -597,6 +655,21 @@ class FunctionCallBuilder { std::vector> expressions, const Type* expr_type); + // Construct a ResolvedFunctionCall which is a nested series of binary + // operations: + // ((expressions[0] OP expressions[1]) OP ... OP expressions[N-1]) + // where N is the number of expressions. + // + // Requires: N >= 2 AND all expressions return `expr_type` AND + // the nary logic function returns `expr_type`. + // + // The signature for the built-in function `op_catalog_name` must be available + // in `catalog` or an error status is returned. + absl::StatusOr> NestedBinaryOp( + absl::string_view op_catalog_name, FunctionSignatureId op_function_id, + std::vector> expressions, + const Type* expr_type); + // Construct a ResolvedFunctionCall of // builtin_function_name(REPEATED ) // whose arguments have the same type and supports ordering. diff --git a/zetasql/resolved_ast/rewrite_utils_test.cc b/zetasql/resolved_ast/rewrite_utils_test.cc index f852f85ad..f5bc8a7e0 100644 --- a/zetasql/resolved_ast/rewrite_utils_test.cc +++ b/zetasql/resolved_ast/rewrite_utils_test.cc @@ -1510,6 +1510,94 @@ TEST_F(FunctionCallBuilderTest, ConcatEmptyArgList) { StatusIs(absl::StatusCode::kInternal, HasSubstr("!elements.empty() "))); } +TEST_F(FunctionCallBuilderTest, IsNotNull) { + auto column = + MakeResolvedColumnRef(types::Int64Type(), ResolvedColumn(), false); + + ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr function, + fn_builder_.IsNotNull(std::move(column))); + EXPECT_EQ(function->DebugString(), absl::StripLeadingAsciiWhitespace(R"( +FunctionCall(ZetaSQL:$not(BOOL) -> BOOL) ++-FunctionCall(ZetaSQL:$is_null(INT64) -> BOOL) + +-ColumnRef(type=INT64, column=.#-1) +)")); +} + +TEST_F(FunctionCallBuilderTest, ArrayToString) { + const ArrayType* array_of_strings; + ZETASQL_ASSERT_OK( + type_factory_.MakeArrayType(types::StringType(), &array_of_strings)); + auto array = MakeResolvedColumnRef(array_of_strings, ResolvedColumn(), false); + auto delimiter = MakeResolvedLiteral(Value::String("delimiter")); + + ZETASQL_ASSERT_OK_AND_ASSIGN( + std::unique_ptr function, + fn_builder_.ArrayToString(std::move(array), std::move(delimiter))); + EXPECT_EQ(function->DebugString(), absl::StripLeadingAsciiWhitespace(R"( +FunctionCall(ZetaSQL:array_to_string(ARRAY, STRING) -> STRING) ++-ColumnRef(type=ARRAY, column=.#-1) ++-Literal(type=STRING, value="delimiter") +)")); +} + +TEST_F(FunctionCallBuilderTest, ArrayAgg) { + auto element = + MakeResolvedColumnRef(types::StringType(), ResolvedColumn(), false); + + ZETASQL_ASSERT_OK_AND_ASSIGN(std::unique_ptr function, + fn_builder_.ArrayAgg(std::move(element), nullptr)); + EXPECT_EQ(function->DebugString(), absl::StripLeadingAsciiWhitespace(R"( +AggregateFunctionCall(ZetaSQL:array_agg(STRING) -> ARRAY) ++-ColumnRef(type=STRING, column=.#-1) +)")); +} + +TEST_F(FunctionCallBuilderTest, ArrayAggWithHaving) { + ResolvedColumn col; + auto element = MakeResolvedColumnRef(types::StringType(), col, false); + auto having = MakeResolvedColumnRef(types::StringType(), col, false); + + ZETASQL_ASSERT_OK_AND_ASSIGN( + std::unique_ptr function, + fn_builder_.ArrayAgg(std::move(element), std::move(having), + ResolvedAggregateHavingModifier::MAX)); + EXPECT_EQ(function->DebugString(), absl::StripLeadingAsciiWhitespace(R"( +AggregateFunctionCall(ZetaSQL:array_agg(STRING) -> ARRAY) ++-ColumnRef(type=STRING, column=.#-1) ++-having_modifier= + +-AggregateHavingModifier + +-kind=MAX + +-having_expr= + +-ColumnRef(type=STRING, column=.#-1) +)")); +} + +TEST_F(FunctionCallBuilderTest, MakeNullIfEmptyArray) { + zetasql_base::SequenceNumber sequence; + ColumnFactory column_factory(10, &sequence); + const ArrayType* array_of_strings; + ZETASQL_ASSERT_OK( + type_factory_.MakeArrayType(types::StringType(), &array_of_strings)); + auto array = MakeResolvedColumnRef(array_of_strings, ResolvedColumn(), false); + ZETASQL_ASSERT_OK_AND_ASSIGN( + std::unique_ptr function, + fn_builder_.MakeNullIfEmptyArray(column_factory, std::move(array))); + EXPECT_EQ(function->DebugString(), absl::StripLeadingAsciiWhitespace(R"( +WithExpr ++-type=ARRAY ++-assignment_list= +| +-$out#11 := ColumnRef(type=ARRAY, column=.#-1) ++-expr= + +-FunctionCall(ZetaSQL:if(BOOL, ARRAY, ARRAY) -> ARRAY) + +-FunctionCall(ZetaSQL:$greater_or_equal(INT64, INT64) -> BOOL) + | +-FunctionCall(ZetaSQL:array_length(ARRAY) -> INT64) + | | +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + | +-Literal(type=INT64, value=1) + +-ColumnRef(type=ARRAY, column=null_if_empty_array.$out#11) + +-Literal(type=ARRAY, value=NULL) +)")); +} + class LikeAnyAllSubqueryScanBuilderTest : public ::testing::TestWithParam { public: diff --git a/zetasql/resolved_ast/sql_builder.cc b/zetasql/resolved_ast/sql_builder.cc index 5721150f5..f31deee6c 100644 --- a/zetasql/resolved_ast/sql_builder.cc +++ b/zetasql/resolved_ast/sql_builder.cc @@ -38,9 +38,11 @@ #include "zetasql/resolved_ast/sql_builder.h" #include +#include #include #include #include +#include #include #include #include @@ -55,12 +57,15 @@ #include "zetasql/base/logging.h" #include "zetasql/analyzer/expr_matching_helpers.h" #include "zetasql/analyzer/query_resolver_helper.h" +#include "zetasql/analyzer/set_operation_resolver_base.h" #include "zetasql/common/thread_stack.h" +#include "zetasql/parser/parse_tree.h" #include "zetasql/public/annotation/collation.h" #include "zetasql/public/builtin_function.pb.h" #include "zetasql/public/catalog.h" #include "zetasql/public/constant.h" #include "zetasql/public/function.h" +#include "zetasql/public/function_signature.h" #include "zetasql/public/functions/date_time_util.h" #include "zetasql/public/functions/datetime.pb.h" #include "zetasql/public/functions/differential_privacy.pb.h" @@ -69,6 +74,16 @@ #include "zetasql/public/strings.h" #include "zetasql/public/table_valued_function.h" #include "zetasql/public/type.pb.h" +#include "zetasql/public/types/annotation.h" +#include "zetasql/public/types/array_type.h" +#include "zetasql/public/types/collation.h" +#include "zetasql/public/types/proto_type.h" +#include "zetasql/public/types/struct_type.h" +#include "zetasql/public/types/type.h" +#include "zetasql/public/types/type_factory.h" +#include "zetasql/public/types/type_modifiers.h" +#include "zetasql/public/types/type_parameters.h" +#include "zetasql/public/value.h" #include "zetasql/resolved_ast/node_sources.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "zetasql/resolved_ast/resolved_ast_enums.pb.h" @@ -78,7 +93,9 @@ #include "zetasql/resolved_ast/resolved_node_kind.pb.h" #include "zetasql/resolved_ast/rewrite_utils.h" #include "zetasql/resolved_ast/target_syntax.h" +#include "absl/base/optimization.h" #include "absl/cleanup/cleanup.h" +#include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/status/status.h" @@ -89,9 +106,11 @@ #include "absl/strings/str_join.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/strings/strip.h" #include "absl/strings/substitute.h" #include "absl/types/span.h" #include "zetasql/base/map_util.h" +#include "zetasql/base/stl_util.h" #include "re2/re2.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_macros.h" @@ -109,31 +128,27 @@ static const char kFrom[] = " FROM "; static const char kEmptyAlias[] = "``"; std::string SQLBuilder::GetColumnPath(const ResolvedColumn& column) { - if (zetasql_base::ContainsKey(column_paths_, column.column_id())) { - return zetasql_base::FindOrDie(column_paths_, column.column_id()); + if (zetasql_base::ContainsKey(column_paths(), column.column_id())) { + return zetasql_base::FindOrDie(column_paths(), column.column_id()); } return ToIdentifierLiteral(GetColumnAlias(column)); } std::string SQLBuilder::GetColumnAlias(const ResolvedColumn& column) { - if (zetasql_base::ContainsKey(computed_column_alias_, column.column_id())) { - return zetasql_base::FindOrDie(computed_column_alias_, column.column_id()); + if (zetasql_base::ContainsKey(computed_column_alias(), column.column_id())) { + return zetasql_base::FindOrDie(computed_column_alias(), column.column_id()); } const std::string alias = GenerateUniqueAliasName(); - zetasql_base::InsertOrDie(&computed_column_alias_, column.column_id(), alias); + zetasql_base::InsertOrDie(&mutable_computed_column_alias(), column.column_id(), alias); return alias; } -bool SQLBuilder::HasColumnAlias(const ResolvedColumn& column) { - return zetasql_base::ContainsKey(computed_column_alias_, column.column_id()); -} - std::string SQLBuilder::UpdateColumnAlias(const ResolvedColumn& column) { - auto it = computed_column_alias_.find(column.column_id()); - ABSL_CHECK(it != computed_column_alias_.end()) + auto it = computed_column_alias().find(column.column_id()); + ABSL_CHECK(it != computed_column_alias().end()) // Crash OK << "Alias does not exist for " << column.DebugString(); - computed_column_alias_.erase(it); + mutable_computed_column_alias().erase(it); return GetColumnAlias(column); } @@ -398,7 +413,7 @@ ResolvedColumnList GetRecursiveScanColumnsExcludingDepth( absl::Status SQLBuilder::Process(const ResolvedNode& ast) { ast.ClearFieldsAccessed(); - ColumnNameCollector name_collector(&col_ref_names_); + ColumnNameCollector name_collector(&mutable_col_ref_names()); ZETASQL_RETURN_IF_ERROR(ast.Accept(&name_collector)); SideEffectsScopeCollector side_effects_scope_collector; @@ -459,7 +474,7 @@ SQLBuilder::ProcessNode(const ResolvedNode* node) { RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); ZETASQL_RET_CHECK(node != nullptr); ZETASQL_RETURN_IF_ERROR(node->Accept(this)); - ZETASQL_RET_CHECK_EQ(query_fragments_.size(), 1); + ZETASQL_RET_CHECK_EQ(query_fragments_.size(), 1) << CurrentStackTrace(); return PopQueryFragment(); } @@ -510,7 +525,9 @@ absl::StatusOr SQLBuilder::GetSQL( } if (value.is_null()) { - if (is_constant_value || is_opaque_enum_not_in_catalog) { + if (is_constant_value || + is_opaque_enum_not_in_catalog + ) { // To handle NULL literals in ResolvedOption, where we would not want to // print them as a casted literal. return std::string("NULL"); @@ -774,9 +791,14 @@ absl::Status SQLBuilder::VisitResolvedFunctionCall( if (node->function()->GetSignature(0)->context_id() == FN_MAKE_ARRAY) { // For MakeArray function we explicitly prepend the array type to the // function sql, and is passed as a part of the inputs. - inputs.push_back( - node->type()->TypeName(options_.language_options.product_mode(), - options_.use_external_float32)); + bool should_print_explicit_type = true; + if (should_print_explicit_type) { + inputs.push_back( + node->type()->TypeName(options_.language_options.product_mode(), + options_.use_external_float32)); + } else { + inputs.push_back("ARRAY"); + } } else if (node->function()->GetSignature(0)->context_id() == FN_WITH_SIDE_EFFECTS) { // The $with_side_effects() function is just symbolic to represent side @@ -879,10 +901,10 @@ absl::Status SQLBuilder::VisitResolvedAggregateFunctionCall( // compute the column. So clearing the pending_aggregate_columns here would // not have any side effects. std::map previous_pending_aggregate_columns; - previous_pending_aggregate_columns.swap(pending_columns_); + previous_pending_aggregate_columns.swap(mutable_pending_columns()); auto cleanup = absl::MakeCleanup([this, &previous_pending_aggregate_columns]() { - previous_pending_aggregate_columns.swap(pending_columns_); + previous_pending_aggregate_columns.swap(mutable_pending_columns()); }); ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, @@ -1021,6 +1043,13 @@ std::string SQLBuilder::AnalyticFunctionInfo::GetSQL() const { ")"); } +SQLBuilder::SQLBuilder(int* max_seen_alias_id_root_ptr, + const SQLBuilderOptions& options, + const CopyableState& state) + : max_seen_alias_id_root_ptr_(max_seen_alias_id_root_ptr), + options_(options), + state_(state) {} + absl::Status SQLBuilder::VisitResolvedAnalyticFunctionCall( const ResolvedAnalyticFunctionCall* node) { std::vector inputs; @@ -1080,7 +1109,8 @@ absl::Status SQLBuilder::VisitResolvedAnalyticFunctionGroup( pending_analytic_function_->set_partition_by(partition_by); pending_analytic_function_->set_order_by(order_by); - zetasql_base::InsertOrDie(&pending_columns_, analytic_function->column().column_id(), + zetasql_base::InsertOrDie(&mutable_pending_columns(), + analytic_function->column().column_id(), pending_analytic_function_->GetSQL()); pending_analytic_function_.reset(); @@ -1445,13 +1475,13 @@ absl::Status SQLBuilder::VisitResolvedReplaceField( absl::Status SQLBuilder::VisitResolvedColumnRef(const ResolvedColumnRef* node) { const ResolvedColumn& column = node->column(); - if (zetasql_base::ContainsKey(pending_columns_, column.column_id())) { + if (zetasql_base::ContainsKey(pending_columns(), column.column_id())) { PushQueryFragment(node, - zetasql_base::FindOrDie(pending_columns_, column.column_id())); - } else if (zetasql_base::ContainsKey(correlated_pending_columns_, + zetasql_base::FindOrDie(pending_columns(), column.column_id())); + } else if (zetasql_base::ContainsKey(correlated_pending_columns(), column.column_id())) { PushQueryFragment( - node, zetasql_base::FindOrDie(correlated_pending_columns_, column.column_id())); + node, zetasql_base::FindOrDie(correlated_pending_columns(), column.column_id())); } else { PushQueryFragment(node, GetColumnPath(node->column())); } @@ -1547,27 +1577,27 @@ absl::Status SQLBuilder::VisitResolvedSubqueryExpr( // NOTE: Swapping pending_columns_ must happen after resolution of in_expr // which may reference current pending columns as uncorrelated. std::map previous_pending_columns; - previous_pending_columns.swap(pending_columns_); + previous_pending_columns.swap(mutable_pending_columns()); std::map previous_correlated_columns; - previous_correlated_columns.swap(correlated_pending_columns_); + previous_correlated_columns.swap(mutable_correlated_pending_columns()); auto cleanup = absl::MakeCleanup( [this, &previous_pending_columns, &previous_correlated_columns]() { - previous_pending_columns.swap(pending_columns_); - previous_correlated_columns.swap(correlated_pending_columns_); + previous_pending_columns.swap(mutable_pending_columns()); + previous_correlated_columns.swap(mutable_correlated_pending_columns()); }); for (const auto& parameter : node->parameter_list()) { auto pending_column = previous_pending_columns.find(parameter->column().column_id()); if (pending_column != previous_pending_columns.end()) { - correlated_pending_columns_.insert(*pending_column); + mutable_correlated_pending_columns().insert(*pending_column); continue; } auto correlated_column = previous_correlated_columns.find(parameter->column().column_id()); if (correlated_column != previous_correlated_columns.end()) { - correlated_pending_columns_.insert(*correlated_column); + mutable_correlated_pending_columns().insert(*correlated_column); continue; } } @@ -1627,7 +1657,7 @@ absl::Status SQLBuilder::VisitResolvedWithExpr(const ResolvedWithExpr* node) { ProcessNode(node->assignment_list(i))); const ResolvedColumn& col = node->assignment_list(i)->column(); std::string column_alias = zetasql_base::FindWithDefault( - computed_column_alias_, col.column_id(), col.name()); + computed_column_alias(), col.column_id(), col.name()); if (i == 0) { sql = absl::Substitute("(SELECT $0 AS $1)", assignment->GetSQL(), column_alias); @@ -1679,10 +1709,10 @@ absl::Status SQLBuilder::AppendColumnSchema( annotations != nullptr && i < annotations->child_list_size() ? annotations->child_list(i) : nullptr; - ZETASQL_RETURN_IF_ERROR(AppendColumnSchema( - field.type, /*is_hidden=*/false, child_annotations, - /*generated_column_info=*/nullptr, /*default_value=*/nullptr, - text)); + ZETASQL_RETURN_IF_ERROR(AppendColumnSchema(field.type, /*is_hidden=*/false, + child_annotations, + /*generated_column_info=*/nullptr, + /*default_value=*/nullptr, text)); } absl::StrAppend(text, ">"); } else if (type->IsArray()) { @@ -1712,8 +1742,7 @@ absl::Status SQLBuilder::AppendColumnSchema( type->TypeName(options_.language_options.product_mode(), options_.use_external_float32)); } - if (annotations != nullptr && - annotations->collation_name() != nullptr) { + if (annotations != nullptr && annotations->collation_name() != nullptr) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr collation, ProcessNode(annotations->collation_name())); absl::StrAppend(text, " COLLATE ", collation->GetSQL()); @@ -2028,7 +2057,7 @@ absl::Status SQLBuilder::VisitResolvedGetStructField( if (result_sql != kEmptyAlias) { if (node->expr()->node_kind() == RESOLVED_CONSTANT || (node->expr()->node_kind() == RESOLVED_SYSTEM_VARIABLE && - !in_set_lhs_)) { + !state_.in_set_lhs)) { // Enclose in parentheses to ensure that "(foo).bar" // (accessing field bar of constant foo) does not get unparsed as // "foo.bar" (accessing named constant bar in namespace foo). @@ -2101,8 +2130,6 @@ absl::Status SQLBuilder::GetSelectList( std::vector>* select_list) { ABSL_DCHECK(select_list != nullptr); - std::map* group_by_list = - query_expression->MutableGroupByList(); std::set has_alias; std::set seen_aliases; for (int i = 0; i < column_list.size(); ++i) { @@ -2133,32 +2160,32 @@ absl::Status SQLBuilder::GetSelectList( ProcessNode(expr)); select_list->push_back(std::make_pair(result->GetSQL(), alias)); } else { - if (zetasql_base::ContainsKey(*group_by_list, col.column_id())) { + if (query_expression->HasGroupByColumn(col.column_id())) { select_list->push_back(std::make_pair( - zetasql_base::FindOrDie(*group_by_list, col.column_id()), alias)); - } else if (zetasql_base::ContainsKey(pending_columns_, col.column_id())) { + query_expression->GetGroupByColumnOrDie(col.column_id()), alias)); + } else if (zetasql_base::ContainsKey(pending_columns(), col.column_id())) { select_list->push_back(std::make_pair( - zetasql_base::FindOrDie(pending_columns_, col.column_id()), alias)); - } else if (zetasql_base::ContainsKey(correlated_pending_columns_, + zetasql_base::FindOrDie(pending_columns(), col.column_id()), alias)); + } else if (zetasql_base::ContainsKey(correlated_pending_columns(), col.column_id())) { select_list->push_back(std::make_pair( - zetasql_base::FindOrDie(correlated_pending_columns_, col.column_id()), + zetasql_base::FindOrDie(correlated_pending_columns(), col.column_id()), alias)); } else { select_list->push_back(std::make_pair(GetColumnPath(col), alias)); } } } - pending_columns_.clear(); + mutable_pending_columns().clear(); // If any group_by column is computed in the select list, then update the sql // text for those columns in group_by_list inside the QueryExpression, // reflecting the ordinal position of select clause. for (int pos = 0; pos < column_list.size(); ++pos) { const ResolvedColumn& col = column_list[pos]; - if (zetasql_base::ContainsKey(*group_by_list, col.column_id())) { - zetasql_base::InsertOrUpdate( - group_by_list, col.column_id(), + if (query_expression->HasGroupByColumn(col.column_id())) { + query_expression->SetGroupByColumn( + col.column_id(), absl::StrCat(pos + 1) /* select list ordinal position */); } } @@ -2194,9 +2221,9 @@ absl::Status SQLBuilder::AddSelectListIfNeeded( bool SQLBuilder::CanTableBeUsedWithImplicitAlias(const Table* table) { std::string table_identifier = TableToIdentifierLiteral(table); - auto it = tables_with_implicit_alias_.find(table->Name()); - if (it == tables_with_implicit_alias_.end()) { - tables_with_implicit_alias_[table->Name()] = table_identifier; + auto it = tables_with_implicit_alias().find(table->Name()); + if (it == tables_with_implicit_alias().end()) { + mutable_tables_with_implicit_alias()[table->Name()] = table_identifier; return true; } else { return it->second == table_identifier; @@ -2228,9 +2255,11 @@ absl::Status SQLBuilder::VisitResolvedTableScan(const ResolvedTableScan* node) { // TODO: Remove the case use_column_index_list=false const bool use_column_index_list = node->column_index_list_size() == node->column_list_size(); + const bool use_value_table = + node->table()->IsValueTable() && use_column_index_list; for (int i = 0; i < node->column_list_size(); ++i) { const ResolvedColumn& column = node->column_list(i); - if (node->table()->IsValueTable() && node->column_index_list(i) == 0) { + if (use_value_table && node->column_index_list(i) == 0) { ZETASQL_RETURN_IF_ERROR(AddValueTableAliasForVisitResolvedTableScan( table_alias, column, &select_list)); } else { @@ -2319,7 +2348,7 @@ absl::Status SQLBuilder::VisitResolvedPivotScan(const ResolvedPivotScan* node) { pivot_expr_aliases[col->pivot_expr_index()]; const std::string& pivot_value_alias = in_expr_aliases[col->pivot_value_index()]; - computed_column_alias_[col->column().column_id()] = + mutable_computed_column_alias()[col->column().column_id()] = absl::StrCat(pivot_expr_alias, "_", pivot_value_alias); } @@ -2327,7 +2356,7 @@ absl::Status SQLBuilder::VisitResolvedPivotScan(const ResolvedPivotScan* node) { const ResolvedColumn& output_column = groupby_expr->column(); const ResolvedColumn& input_column = groupby_expr->expr()->GetAs()->column(); - computed_column_alias_[output_column.column_id()] = + mutable_computed_column_alias()[output_column.column_id()] = GetColumnAlias(input_column); } @@ -2347,9 +2376,9 @@ absl::Status SQLBuilder::VisitResolvedUnpivotScan( for (const ResolvedColumn& val_col : node->value_column_list()) { std::string alias = GetColumnAlias(val_col); unpivot_value_columns.push_back(alias); - computed_column_alias_[val_col.column_id()] = alias; + mutable_computed_column_alias()[val_col.column_id()] = alias; } - computed_column_alias_[node->label_column().column_id()] = + mutable_computed_column_alias()[node->label_column().column_id()] = GetColumnAlias(node->label_column()); // Remember the aliases of the column refs from the input source that @@ -2359,7 +2388,7 @@ absl::Status SQLBuilder::VisitResolvedUnpivotScan( const ResolvedColumn& output_column = input_column_expr->column(); const ResolvedColumn& input_column = input_column_expr->expr()->GetAs()->column(); - computed_column_alias_[output_column.column_id()] = + mutable_computed_column_alias()[output_column.column_id()] = GetColumnAlias(input_column); } @@ -2445,8 +2474,8 @@ absl::Status SQLBuilder::VisitResolvedProjectScan( std::string SQLBuilder::ComputedColumnAliasDebugString() const { std::vector pairs; - pairs.reserve(computed_column_alias_.size()); - for (const auto& kv : computed_column_alias_) { + pairs.reserve(computed_column_alias().size()); + for (const auto& kv : computed_column_alias()) { pairs.push_back(absl::StrCat("(", kv.first, ", ", kv.second, ")")); } return absl::StrCat("[", absl::StrJoin(pairs, ", "), "]"); @@ -2542,7 +2571,7 @@ absl::Status SQLBuilder::VisitResolvedTVFScan(const ResolvedTVFScan* node) { ZETASQL_RETURN_IF_ERROR(AddSelectListIfNeeded(scan->column_list(), result->query_expression.get())); const std::string column_alias = zetasql_base::FindWithDefault( - computed_column_alias_, scan->column_list(0).column_id()); + computed_column_alias(), scan->column_list(0).column_id()); ZETASQL_RET_CHECK(!column_alias.empty()) << "\nfor column: " << scan->column_list(0).DebugString() << "\nscan: " << scan->DebugString() @@ -2603,12 +2632,13 @@ absl::Status SQLBuilder::VisitResolvedTVFScan(const ResolvedTVFScan* node) { // exact column names to work properly. absl::StrAppend(&arg_col_list_select_items.back(), " AS ", required_col); - zetasql_base::InsertOrUpdate(&computed_column_alias_, col.column_id(), + zetasql_base::InsertOrUpdate(&mutable_computed_column_alias(), col.column_id(), required_col); } else { // If there is no explicit column/alias name, then we use the // original column name/alias. - zetasql_base::InsertOrUpdate(&computed_column_alias_, col.column_id(), *alias); + zetasql_base::InsertOrUpdate(&mutable_computed_column_alias(), col.column_id(), + *alias); } } argument_list.push_back(absl::StrCat( @@ -2635,7 +2665,8 @@ absl::Status SQLBuilder::VisitResolvedTVFScan(const ResolvedTVFScan* node) { for (int i = 0; i < node->column_list_size(); ++i) { const ResolvedColumn& column = node->column_list(i); if (i == 0 && is_value_table) { - zetasql_base::InsertOrDie(&pending_columns_, column.column_id(), tvf_scan_alias); + zetasql_base::InsertOrDie(&mutable_pending_columns(), column.column_id(), + tvf_scan_alias); } else { std::string column_name; // If this TVF has this particular implementation, then we pull the @@ -2661,7 +2692,7 @@ absl::Status SQLBuilder::VisitResolvedTVFScan(const ResolvedTVFScan* node) { signature_result_schema.column(node->column_index_list(i)).name; } // Prefix column name with tvf alias to support strict resolution mode. - zetasql_base::InsertOrDie(&pending_columns_, column.column_id(), + zetasql_base::InsertOrDie(&mutable_pending_columns(), column.column_id(), absl::StrCat(tvf_scan_alias, ".", column_name)); } } @@ -2718,7 +2749,7 @@ absl::Status SQLBuilder::VisitResolvedFilterScan( // below. ZETASQL_RETURN_IF_ERROR(SetPathForColumnsInScan( simple_table_scan, - zetasql_base::FindWithDefault(table_alias_map_, simple_table_scan->table()))); + zetasql_base::FindWithDefault(table_alias_map(), simple_table_scan->table()))); // Remove the underlying TableScan's select list, to let the ProjectScan // just above this FilterScan to install its select list without wrapping // this fragment. @@ -2818,7 +2849,7 @@ absl::Status SQLBuilder::VisitResolvedGroupRowsScan( std::string current_path = GetColumnPath(ref->column()); bool aliased_to_self = absl::EndsWith(current_path, ref->column().name()); if (!aliased_to_self && - zetasql_base::ContainsKey(computed_column_alias_, ref->column().column_id())) { + zetasql_base::ContainsKey(computed_column_alias(), ref->column().column_id())) { alias = GetColumnAlias(ref->column()); } else { alias = ref->column().name(); @@ -2901,7 +2932,7 @@ absl::StatusOr SQLBuilder::GetUsingWrapper( // The alias should have already been computed at this point. const std::string* const new_alias = - zetasql_base::FindOrNull(computed_column_alias_, left_scan_col_id); + zetasql_base::FindOrNull(computed_column_alias(), left_scan_col_id); ZETASQL_RET_CHECK(new_alias != nullptr); if (!result.empty()) { absl::StrAppend(&result, ", "); @@ -2924,7 +2955,7 @@ absl::StatusOr SQLBuilder::GetUsingWrapper( } } for (const auto& elem : updated_alias) { - computed_column_alias_[elem.first.column_id()] = *elem.second; + mutable_computed_column_alias()[elem.first.column_id()] = *elem.second; SetPathForColumn(elem.first, absl::StrCat(scan_alias, ".", *elem.second)); } return result; @@ -2935,46 +2966,67 @@ std::string SQLBuilder::MakeNonconflictingAlias(absl::string_view name) { std::string alias; do { alias = absl::StrCat(alias_prefix_lower, "_", GetUniqueId()); - } while (col_ref_names_.contains(alias)); + } while (col_ref_names().contains(alias)); return ToIdentifierLiteral(alias); } std::string SQLBuilder::GetTableAlias(const Table* table) { - if (!table_alias_map_.contains(table)) { - zetasql_base::InsertIfNotPresent(&table_alias_map_, table, + if (!table_alias_map().contains(table)) { + zetasql_base::InsertIfNotPresent(&mutable_table_alias_map(), table, MakeNonconflictingAlias(table->Name())); } - return zetasql_base::FindOrDie(table_alias_map_, table); + return zetasql_base::FindOrDie(table_alias_map(), table); } std::string SQLBuilder::GetScanAlias(const ResolvedScan* scan) { - if (!scan_alias_map_.contains(scan)) { + if (!scan_alias_map().contains(scan)) { const std::string scan_alias_prefix_lower = absl::AsciiStrToLower( (scan->node_kind() == RESOLVED_TABLE_SCAN) // We prefer to use table names as alias prefix when possible. ? scan->GetAs()->table()->Name() : scan->node_kind_string()); - zetasql_base::InsertIfNotPresent(&scan_alias_map_, scan, + zetasql_base::InsertIfNotPresent(&mutable_scan_alias_map(), scan, MakeNonconflictingAlias(scan_alias_prefix_lower)); } - return zetasql_base::FindOrDie(scan_alias_map_, scan); + return zetasql_base::FindOrDie(scan_alias_map(), scan); } int64_t SQLBuilder::GetUniqueId() { - int64_t id = scan_id_sequence_.GetNext(); - if (id == 0) { // Avoid using scan id 0. - id = scan_id_sequence_.GetNext(); - ABSL_DCHECK_NE(id, 0); + while (true) { + // Allocate from the sequence, but make sure it's higher than the max we + // should start from. + int next_alias_id = static_cast(alias_id_sequence_.GetNext()); + if (next_alias_id > max_seen_alias_id()) { + ABSL_DCHECK_NE(next_alias_id, 0); + set_max_seen_alias_id(next_alias_id); + break; + } + } + ABSL_DCHECK_LE(max_seen_alias_id(), std::numeric_limits::max()); + return max_seen_alias_id(); +} + +void SQLBuilder::set_max_seen_alias_id(int max_seen_alias_id) { + *mutable_max_seen_alias_id() = max_seen_alias_id; +} + +int* SQLBuilder::mutable_max_seen_alias_id() { + return max_seen_alias_id_root_ptr_ == nullptr ? &max_seen_alias_id_ + : max_seen_alias_id_root_ptr_; +} + +int SQLBuilder::max_seen_alias_id() const { + if (max_seen_alias_id_root_ptr_ == nullptr) { + return max_seen_alias_id_; } - ABSL_DCHECK_LE(id, std::numeric_limits::max()); - return id; + return *max_seen_alias_id_root_ptr_; } void SQLBuilder::SetPathForColumn(const ResolvedColumn& column, const std::string& path) { - zetasql_base::InsertOrUpdate(&column_paths_, column.column_id(), path); + zetasql_base::InsertOrUpdate(&mutable_column_paths(), column.column_id(), path); } void SQLBuilder::SetPathForColumnList(const ResolvedColumnList& column_list, @@ -4098,7 +4150,7 @@ absl::Status SQLBuilder::VisitResolvedSetOperationScan( ABSL_DCHECK_EQ(first_select_list.size(), std::max(node->column_list_size(), 1)); for (int i = 0; i < node->column_list_size(); i++) { - zetasql_base::InsertOrDie(&computed_column_alias_, + zetasql_base::InsertOrDie(&mutable_computed_column_alias(), node->column_list(i).column_id(), first_select_list[i].second); } @@ -4113,11 +4165,12 @@ absl::Status SQLBuilder::VisitResolvedSetOperationScan( final_column_list.reserve(node->column_list_size()); for (const ResolvedColumn& column : node->column_list()) { std::string alias = ToIdentifierLiteral(GenerateUniqueAliasName()); - zetasql_base::InsertOrDie(&computed_column_alias_, column.column_id(), alias); + zetasql_base::InsertOrDie(&mutable_computed_column_alias(), column.column_id(), + alias); final_column_list.push_back( std::make_pair(GetColumnPath(column), alias)); } - query_expression->set_corresponding_set_op_output_column_list( + query_expression->SetCorrespondingSetOpOutputColumnList( final_column_list); if (node->column_match_mode() == ResolvedSetOperationScan::CORRESPONDING && @@ -4288,7 +4341,7 @@ absl::Status SQLBuilder::ProcessAggregateScanBase( ZETASQL_RETURN_IF_ERROR(CheckNoSuccessiveAggregateScansWithDeferredColumns(node)); std::map group_by_list; - int64_t pending_columns_before_grouping_columns = pending_columns_.size(); + int64_t pending_columns_before_grouping_columns = pending_columns().size(); for (const auto& computed_col : node->group_by_list()) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, ProcessNode(computed_col->expr())); @@ -4298,14 +4351,15 @@ absl::Status SQLBuilder::ProcessAggregateScanBase( for (const auto& [grouping_col_id, grouping_argument_group_by_col_id] : grouping_column_id_map) { if (grouping_argument_group_by_col_id == group_by_col_id) { - zetasql_base::InsertOrDie(&pending_columns_, grouping_col_id, + zetasql_base::InsertOrDie(&mutable_pending_columns(), grouping_col_id, absl::StrCat("GROUPING(", result->GetSQL(), ")")); } } } // If we haven't added the same number of pending columns as there are // GROUPING function calls, throw error. - ZETASQL_RET_CHECK(pending_columns_.size() - pending_columns_before_grouping_columns == + ZETASQL_RET_CHECK(pending_columns().size() - + pending_columns_before_grouping_columns == grouping_column_id_map.size()); for (const auto& collation : node->collation_list()) { @@ -4327,8 +4381,8 @@ absl::Status SQLBuilder::ProcessAggregateScanBase( } ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, ProcessNode(computed_col->expr())); - zetasql_base::InsertOrDie(&pending_columns_, computed_col->column().column_id(), - result->GetSQL()); + zetasql_base::InsertOrDie(&mutable_pending_columns(), + computed_col->column().column_id(), result->GetSQL()); } std::string group_by_hints; @@ -4491,14 +4545,14 @@ absl::Status SQLBuilder::VisitResolvedWithScan(const ResolvedWithScan* node) { // Save state of the WITH alias map from the outer scope so we can restore it // after processing the local query. const std::map old_with_query_name_to_scan = - with_query_name_to_scan_; + state_.with_query_name_to_scan; std::vector> with_list; bool has_recursive_entries = false; for (const auto& with_entry : node->with_entry_list()) { const std::string name = with_entry->with_query_name(); const ResolvedScan* scan = with_entry->with_subquery(); - zetasql_base::InsertOrDie(&with_query_name_to_scan_, name, scan); + zetasql_base::InsertOrDie(&state_.with_query_name_to_scan, name, scan); bool actually_recursive = false; std::optional recursive_scan = MaybeGetRecursiveScan(with_entry.get()); @@ -4507,7 +4561,7 @@ absl::Status SQLBuilder::VisitResolvedWithScan(const ResolvedWithScan* node) { has_recursive_entries = true; actually_recursive = true; const ResolvedRecursiveScan* scan = recursive_scan.value(); - recursive_query_info_.push({ToIdentifierLiteral(name), scan}); + state_.recursive_query_info.push({ToIdentifierLiteral(name), scan}); if (scan->recursion_depth_modifier() != nullptr) { ZETASQL_ASSIGN_OR_RETURN(auto modifier, @@ -4530,8 +4584,8 @@ absl::Status SQLBuilder::VisitResolvedWithScan(const ResolvedWithScan* node) { SetPathForColumnList(scan->column_list(), ToIdentifierLiteral(name)); if (actually_recursive) { - ZETASQL_RET_CHECK(!recursive_query_info_.empty()); - recursive_query_info_.pop(); + ZETASQL_RET_CHECK(!state_.recursive_query_info.empty()); + state_.recursive_query_info.pop(); } } ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, @@ -4550,7 +4604,7 @@ absl::Status SQLBuilder::VisitResolvedWithScan(const ResolvedWithScan* node) { query_expression->TrySetWithClause(with_list, has_recursive_entries)); PushSQLForQueryExpression(node, query_expression.release()); - with_query_name_to_scan_ = old_with_query_name_to_scan; + state_.with_query_name_to_scan = old_with_query_name_to_scan; return absl::OkStatus(); } @@ -4562,10 +4616,10 @@ absl::Status SQLBuilder::VisitResolvedWithRefScan( absl::StrAppend(&from, ToIdentifierLiteral(node->with_query_name()), " AS ", alias); const ResolvedScan* with_scan = - zetasql_base::FindOrDie(with_query_name_to_scan_, node->with_query_name()); + zetasql_base::FindOrDie(state_.with_query_name_to_scan, node->with_query_name()); ZETASQL_RET_CHECK_EQ(node->column_list_size(), with_scan->column_list_size()); for (int i = 0; i < node->column_list_size(); ++i) { - zetasql_base::InsertIfNotPresent(&computed_column_alias_, + zetasql_base::InsertIfNotPresent(&mutable_computed_column_alias(), node->column_list(i).column_id(), GetColumnAlias(with_scan->column_list(i))); } @@ -4619,7 +4673,7 @@ absl::Status SQLBuilder::VisitResolvedSampleScan( node->input_scan()->GetAs(); ZETASQL_RETURN_IF_ERROR(SetPathForColumnsInScan( node->input_scan(), - zetasql_base::FindWithDefault(table_alias_map_, + zetasql_base::FindWithDefault(table_alias_map(), resolved_table_scan->table()))); break; } @@ -4668,9 +4722,8 @@ absl::Status SQLBuilder::VisitResolvedSampleScan( ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, ProcessNode(node->weight_column())); absl::StrAppend(&sample, " WITH WEIGHT ", result->GetSQL()); - query_expression->MutableSelectList()->emplace_back( - result->GetSQL() /* select column */, - result->GetSQL() /* select alias */); + query_expression->AppendSelectColumn(result->GetSQL() /* select column */, + result->GetSQL() /* select alias */); } if (node->repeatable_argument() != nullptr) { @@ -4680,7 +4733,7 @@ absl::Status SQLBuilder::VisitResolvedSampleScan( ")"); } - *(query_expression->MutableFromClause()) = absl::StrCat(from_clause, sample); + query_expression->SetFromClause(absl::StrCat(from_clause, sample)); PushSQLForQueryExpression(node, query_expression.release()); return absl::OkStatus(); } @@ -4731,10 +4784,23 @@ absl::Status SQLBuilder::MatchOutputColumns( ZETASQL_RET_CHECK_EQ(output_column_list.size(), query_expression->SelectList().size()); absl::flat_hash_map final_aliases; + absl::flat_hash_set output_column_aliases; for (int i = 0; i < output_column_list.size(); ++i) { const absl::string_view output_col_alias = output_column_list[i]->name(); if (!IsInternalAlias(output_col_alias)) { final_aliases[i] = ToIdentifierLiteral(output_col_alias); + output_column_aliases.insert(final_aliases[i]); + } + } + // Assign an new alias for internal alias columns whose generated aliases + // were used in the output list names. + for (int i = 0; i < output_column_list.size(); ++i) { + const absl::string_view output_col_alias = output_column_list[i]->name(); + std::string select_list_alias = query_expression->SelectList()[i].second; + if (IsInternalAlias(output_col_alias) && + output_column_aliases.contains(select_list_alias)) { + final_aliases[i] = GenerateUniqueAliasName(); + output_column_aliases.insert(final_aliases[i]); } } absl::flat_hash_map final_aliases_view; @@ -4878,7 +4944,7 @@ absl::Status SQLBuilder::MaybeSetupRecursiveView( const auto* recursive_scan = node->query()->GetAs(); ZETASQL_RET_CHECK(recursive_scan->recursion_depth_modifier() == nullptr) << "Recursive view should NOT have recursion depth modifier."; - recursive_query_info_.push({query_name, recursive_scan}); + state_.recursive_query_info.push({query_name, recursive_scan}); // Force the actual column names to be used against the recursive table; // we cannot use generated column names with an outer SELECT wrapper, as @@ -4893,12 +4959,12 @@ absl::Status SQLBuilder::MaybeSetupRecursiveView( recursive_scan->non_recursive_term()->output_column_list(i); ZETASQL_RET_CHECK_EQ(recursive_query_column.name(), nonrecursive_term_column.name()); - zetasql_base::InsertOrDie(&computed_column_alias_, + zetasql_base::InsertOrDie(&mutable_computed_column_alias(), recursive_query_column.column_id(), recursive_query_column.name()); if (nonrecursive_term_column.column_id() != recursive_query_column.column_id()) { - zetasql_base::InsertOrDie(&computed_column_alias_, + zetasql_base::InsertOrDie(&mutable_computed_column_alias(), nonrecursive_term_column.column_id(), recursive_query_column.name()); } @@ -5254,11 +5320,11 @@ absl::StatusOr SQLBuilder::ProcessCreateTableStmtBase( // Make column aliases available for PARTITION BY, CLUSTER BY and table // constraints. for (const auto& column_definition : node->column_definition_list()) { - computed_column_alias_[column_definition->column().column_id()] = + mutable_computed_column_alias()[column_definition->column().column_id()] = column_definition->name(); } for (const ResolvedColumn& column : node->pseudo_column_list()) { - computed_column_alias_[column.column_id()] = column.name(); + mutable_computed_column_alias()[column.column_id()] = column.name(); } if (process_column_definitions && like_table_name_empty) { ZETASQL_RETURN_IF_ERROR(ProcessTableElementsBase( @@ -5275,6 +5341,18 @@ absl::StatusOr SQLBuilder::ProcessCreateTableStmtBase( return sql; } +absl::Status SQLBuilder::VisitResolvedCreateConnectionStmt( + const ResolvedCreateConnectionStmt* node) { + std::string sql; + ZETASQL_RETURN_IF_ERROR(GetCreateStatementPrefix(node, "CONNECTION", &sql)); + + ZETASQL_ASSIGN_OR_RETURN(const std::string options_string, + GetHintListString(node->option_list())); + absl::StrAppend(&sql, " OPTIONS(", options_string, ")"); + PushQueryFragment(node, sql); + return absl::OkStatus(); +} + absl::Status SQLBuilder::VisitResolvedCreateSchemaStmt( const ResolvedCreateSchemaStmt* node) { std::string sql; @@ -5503,11 +5581,11 @@ absl::Status SQLBuilder::VisitResolvedCreateModelStmt( query_column_definition->column().column_id()}); } // Rename columns in TRANSFORM with the aliases from SELECT statement. - std::map computed_column_alias; + std::map renamed_computed_column_alias; for (const auto& output_col : node->output_column_list()) { const int output_col_id = output_col->column().column_id(); const std::string alias = output_col->name(); - if (!zetasql_base::ContainsKey(computed_column_alias_, output_col_id)) { + if (!zetasql_base::ContainsKey(computed_column_alias(), output_col_id)) { return ::zetasql_base::InternalErrorBuilder() << absl::Substitute( "Column id $0 with name '$1' is not found in " "computed_column_alias_", @@ -5519,9 +5597,10 @@ absl::Status SQLBuilder::VisitResolvedCreateModelStmt( "query_column_name_id_map", output_col_id, alias); } - computed_column_alias.insert({query_column_name_id_map[alias], alias}); + renamed_computed_column_alias.insert( + {query_column_name_id_map[alias], alias}); } - computed_column_alias_.swap(computed_column_alias); + mutable_computed_column_alias().swap(renamed_computed_column_alias); for (const auto& analytic_function_group : node->transform_analytic_function_group_list()) { ZETASQL_RETURN_IF_ERROR( @@ -5696,6 +5775,11 @@ absl::Status SQLBuilder::VisitResolvedCreateIndexStmt( absl::StrJoin(argument_list.begin(), argument_list.end(), ","), ")"); } + if (!node->partition_by_list().empty()) { + absl::StrAppend(&sql, " PARTITION BY "); + ZETASQL_RETURN_IF_ERROR(GetPartitionByListString(node->partition_by_list(), &sql)); + } + ZETASQL_ASSIGN_OR_RETURN(const std::string options_string, GetHintListString(node->option_list())); if (!options_string.empty()) { @@ -5739,7 +5823,7 @@ absl::Status SQLBuilder::VisitResolvedCreateMaterializedViewStmt( // Make column aliases available for PARTITION BY, CLUSTER BY. for (const auto& column_definition : node->column_definition_list()) { - computed_column_alias_[column_definition->column().column_id()] = + mutable_computed_column_alias()[column_definition->column().column_id()] = column_definition->name(); } absl::StrAppend(&sql, GetSqlSecuritySql(node->sql_security())); @@ -5876,9 +5960,6 @@ absl::Status SQLBuilder::VisitResolvedCreateFunctionStmt( std::string sql; ZETASQL_RETURN_IF_ERROR(GetCreateStatementPrefix( node, node->is_aggregate() ? "AGGREGATE FUNCTION" : "FUNCTION", &sql)); - ZETASQL_ASSIGN_OR_RETURN( - const std::string args, - GetFunctionArgListString(node->argument_name_list(), node->signature())); absl::StrAppend(&sql, node->signature().GetSQLDeclaration( node->argument_name_list(), options_.language_options.product_mode())); @@ -5909,13 +5990,13 @@ absl::Status SQLBuilder::VisitResolvedCreateFunctionStmt( } // If we have aggregates, extract the strings for the aggregate expressions - // and store them in pending_columns_ so they will be substituted into - // the main expression body. + // and store them in `CopyableState::pending_columns` so they will be + // substituted into the main expression body. for (const auto& computed_col : node->aggregate_expression_list()) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, ProcessNode(computed_col->expr())); - zetasql_base::InsertOrDie(&pending_columns_, computed_col->column().column_id(), - result->GetSQL()); + zetasql_base::InsertOrDie(&mutable_pending_columns(), + computed_col->column().column_id(), result->GetSQL()); } node->is_aggregate(); // Mark as accessed. @@ -6292,11 +6373,11 @@ absl::Status SQLBuilder::VisitResolvedAbortBatchStmt( absl::Status SQLBuilder::VisitResolvedAssignmentStmt( const ResolvedAssignmentStmt* node) { - ABSL_CHECK_EQ(in_set_lhs_, false); - in_set_lhs_ = true; + ABSL_CHECK_EQ(state_.in_set_lhs, false); // Crash OK + state_.in_set_lhs = true; ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr target_sql, ProcessNode(node->target())); - in_set_lhs_ = false; + state_.in_set_lhs = false; ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr expr_sql, ProcessNode(node->expr())); @@ -6372,10 +6453,10 @@ absl::Status SQLBuilder::VisitResolvedDeleteStmt( target_sql = absl::StrCat(TableToIdentifierLiteral(table), " AS ", alias); returning_table_alias_ = alias; } else { - ZETASQL_RET_CHECK(!nested_dml_targets_.empty()); - target_sql = nested_dml_targets_.back().first; - if (nested_dml_targets_.back().second != kEmptyAlias) { - absl::StrAppend(&target_sql, " ", nested_dml_targets_.back().second); + ZETASQL_RET_CHECK(!nested_dml_targets().empty()); + target_sql = nested_dml_targets().back().first; + if (nested_dml_targets().back().second != kEmptyAlias) { + absl::StrAppend(&target_sql, " ", nested_dml_targets().back().second); } } ZETASQL_RET_CHECK(!target_sql.empty()); @@ -6612,14 +6693,13 @@ absl::Status SQLBuilder::VisitResolvedTruncateStmt( const ResolvedTruncateStmt* node) { std::string sql = "TRUNCATE TABLE "; ZETASQL_RET_CHECK(node->table_scan() != nullptr) << "Missing target table."; - std::string name_path = - TableToIdentifierLiteral(node->table_scan()->table()); + std::string name_path = TableToIdentifierLiteral(node->table_scan()->table()); ZETASQL_RET_CHECK(!name_path.empty()); absl::StrAppend(&sql, name_path, " "); // Make column aliases available for WHERE expression for (const auto& column_definition : node->table_scan()->column_list()) { - computed_column_alias_[column_definition.column_id()] = + mutable_computed_column_alias()[column_definition.column_id()] = column_definition.name(); } @@ -6649,11 +6729,12 @@ absl::Status SQLBuilder::VisitResolvedUpdateItem( const ResolvedUpdateItem* node) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr target, ProcessNode(node->target())); - ZETASQL_RET_CHECK(!update_item_targets_and_offsets_.empty()); + ZETASQL_RET_CHECK(!update_item_targets_and_offsets().empty()); // Use an empty offset for now. VisitResolvedUpdateArrayItem will fill it in // later if needed. - update_item_targets_and_offsets_.back().emplace_back(target->GetSQL(), - /*offset_sql=*/""); + mutable_update_item_targets_and_offsets().back().emplace_back( + target->GetSQL(), + /*offset_sql=*/""); if (node->array_update_list_size() > 0) { // Use kEmptyAlias as the path so that VisitResolvedGet{Proto,Struct}Field @@ -6670,14 +6751,14 @@ absl::Status SQLBuilder::VisitResolvedUpdateItem( PushQueryFragment(node, absl::StrJoin(sql_fragments, ", ")); } else { std::string target_sql; - for (int i = 0; i < update_item_targets_and_offsets_.back().size(); ++i) { + for (int i = 0; i < update_item_targets_and_offsets().back().size(); ++i) { const auto& target_and_offset = - update_item_targets_and_offsets_.back()[i]; + update_item_targets_and_offsets().back()[i]; const std::string& target = target_and_offset.first; const std::string& offset = target_and_offset.second; const bool last = - (i == update_item_targets_and_offsets_.back().size() - 1); + (i == update_item_targets_and_offsets().back().size() - 1); ZETASQL_RET_CHECK_EQ(last, offset.empty()); // The ResolvedColumn representing an array element has path @@ -6722,7 +6803,8 @@ absl::Status SQLBuilder::VisitResolvedUpdateItem( } SetPathForColumn(column, target_alias); } - nested_dml_targets_.push_back(std::make_pair(target_sql, target_alias)); + mutable_nested_dml_targets().push_back( + std::make_pair(target_sql, target_alias)); std::vector nested_statements_sql; if (node->delete_list_size() > 0) { @@ -6751,11 +6833,11 @@ absl::Status SQLBuilder::VisitResolvedUpdateItem( } absl::StrAppend(&sql, absl::StrJoin(nested_statements_sql, ", ")); - nested_dml_targets_.pop_back(); + mutable_nested_dml_targets().pop_back(); PushQueryFragment(node, sql); } - update_item_targets_and_offsets_.back().pop_back(); + mutable_update_item_targets_and_offsets().back().pop_back(); return absl::OkStatus(); } @@ -6763,18 +6845,18 @@ absl::Status SQLBuilder::VisitResolvedUpdateArrayItem( const ResolvedUpdateArrayItem* node) { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr offset, ProcessNode(node->offset())); - ZETASQL_RET_CHECK(!update_item_targets_and_offsets_.empty()); - ZETASQL_RET_CHECK(!update_item_targets_and_offsets_.back().empty()); - ZETASQL_RET_CHECK_EQ("", update_item_targets_and_offsets_.back().back().second); + ZETASQL_RET_CHECK(!update_item_targets_and_offsets().empty()); + ZETASQL_RET_CHECK(!update_item_targets_and_offsets().back().empty()); + ZETASQL_RET_CHECK_EQ("", update_item_targets_and_offsets().back().back().second); const std::string offset_sql = offset->GetSQL(); ZETASQL_RET_CHECK(!offset_sql.empty()); - update_item_targets_and_offsets_.back().back().second = offset_sql; + mutable_update_item_targets_and_offsets().back().back().second = offset_sql; ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr update, ProcessNode(node->update_item())); // Clear the offset_sql. - update_item_targets_and_offsets_.back().back().second.clear(); + mutable_update_item_targets_and_offsets().back().back().second.clear(); PushQueryFragment(node, update->GetSQL()); return absl::OkStatus(); @@ -6799,10 +6881,10 @@ absl::Status SQLBuilder::VisitResolvedUpdateStmt( " AS ", alias); returning_table_alias_ = alias; } else { - ZETASQL_RET_CHECK(!nested_dml_targets_.empty()); - target_sql = nested_dml_targets_.back().first; - if (nested_dml_targets_.back().second != kEmptyAlias) { - absl::StrAppend(&target_sql, " ", nested_dml_targets_.back().second); + ZETASQL_RET_CHECK(!nested_dml_targets().empty()); + target_sql = nested_dml_targets().back().first; + if (nested_dml_targets().back().second != kEmptyAlias) { + absl::StrAppend(&target_sql, " ", nested_dml_targets().back().second); } } ZETASQL_RET_CHECK(!target_sql.empty()); @@ -6896,14 +6978,13 @@ absl::Status SQLBuilder::VisitResolvedInsertStmt( std::string target_sql; if (node->table_scan() != nullptr) { - target_sql = - TableToIdentifierLiteral(node->table_scan()->table()); + target_sql = TableToIdentifierLiteral(node->table_scan()->table()); // INSERT doesn't support explicit aliasing, and this is the implicit alias // of the table's full name. returning_table_alias_ = node->table_scan()->table()->Name(); } else { - ZETASQL_RET_CHECK(!nested_dml_targets_.empty()); - target_sql = nested_dml_targets_.back().first; + ZETASQL_RET_CHECK(!nested_dml_targets().empty()); + target_sql = nested_dml_targets().back().first; } ZETASQL_RET_CHECK(!target_sql.empty()); absl::StrAppend(&sql, target_sql, " "); @@ -7059,6 +7140,11 @@ absl::Status SQLBuilder::VisitResolvedMergeWhen(const ResolvedMergeWhen* node) { return absl::OkStatus(); } +absl::Status SQLBuilder::VisitResolvedAlterConnectionStmt( + const ResolvedAlterConnectionStmt* node) { + return GetResolvedAlterObjectStmtSQL(node, "CONNECTION"); +} + absl::Status SQLBuilder::VisitResolvedAlterDatabaseStmt( const ResolvedAlterDatabaseStmt* node) { return GetResolvedAlterObjectStmtSQL(node, "DATABASE"); @@ -7821,10 +7907,10 @@ absl::StatusOr SQLBuilder::GetUpdateItemListSQL( std::vector update_item_list_sql; update_item_list_sql.reserve(update_item_list.size()); for (const auto& update_item : update_item_list) { - update_item_targets_and_offsets_.emplace_back(); + mutable_update_item_targets_and_offsets().emplace_back(); ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr result, ProcessNode(update_item.get())); - update_item_targets_and_offsets_.pop_back(); + mutable_update_item_targets_and_offsets().pop_back(); update_item_list_sql.push_back(result->GetSQL()); } @@ -7862,7 +7948,7 @@ std::string SQLBuilder::GetTableAliasForVisitResolvedTableScan( const ResolvedTableScan& node, std::string* from) { std::string table_alias; // Check collision against columns. - if (col_ref_names_.contains(absl::AsciiStrToLower(node.table()->Name()))) { + if (col_ref_names().contains(absl::AsciiStrToLower(node.table()->Name()))) { table_alias = GetTableAlias(node.table()); absl::StrAppend(from, " AS ", table_alias); } else { @@ -7909,13 +7995,13 @@ absl::Status SQLBuilder::VisitResolvedRecursiveScan( ZETASQL_RET_CHECK_EQ(first_select_list.size(), std::max(columns_excluding_depth.size(), 1)); for (int i = 0; i < columns_excluding_depth.size(); i++) { - if (zetasql_base::ContainsKey(computed_column_alias_, + if (zetasql_base::ContainsKey(computed_column_alias(), node->column_list(i).column_id())) { ZETASQL_RET_CHECK_EQ( - computed_column_alias_.at(node->column_list(i).column_id()), + computed_column_alias().at(node->column_list(i).column_id()), first_select_list[i].second); } else { - zetasql_base::InsertOrDie(&computed_column_alias_, + zetasql_base::InsertOrDie(&mutable_computed_column_alias(), node->column_list(i).column_id(), first_select_list[i].second); } @@ -7974,12 +8060,12 @@ absl::Status SQLBuilder::VisitResolvedRecursiveRefScan( const ResolvedRecursiveRefScan* node) { std::unique_ptr query_expression(new QueryExpression); const std::string alias = GetScanAlias(node); - ZETASQL_RET_CHECK(!recursive_query_info_.empty()) + ZETASQL_RET_CHECK(!state_.recursive_query_info.empty()) << "Found ResolvedRecursiveRefScan node without a corresponding " << "ResolvedRecursiveScan"; - const ResolvedScan* with_scan = recursive_query_info_.top().scan; - std::string query_name = recursive_query_info_.top().query_name; + const ResolvedScan* with_scan = state_.recursive_query_info.top().scan; + std::string query_name = state_.recursive_query_info.top().query_name; ZETASQL_RET_CHECK(with_scan->Is()); const ResolvedColumnList columns_excluding_depth = GetRecursiveScanColumnsExcludingDepth( @@ -7993,14 +8079,14 @@ absl::Status SQLBuilder::VisitResolvedRecursiveRefScan( // VisitResolvedRecursiveScan() while processing the non-recursive term; a // ResolvedRecursiveRefScan node can appear only in the recursive term of a // ResolvedRecursiveScan(). - ZETASQL_RET_CHECK(zetasql_base::ContainsKey(computed_column_alias_, + ZETASQL_RET_CHECK(zetasql_base::ContainsKey(computed_column_alias(), with_scan->column_list(i).column_id())) << "column id: " << node->column_list(i).column_id() << "\nComputed column aliases:\n" << ComputedColumnAliasDebugString(); zetasql_base::InsertOrDie( - &computed_column_alias_, node->column_list(i).column_id(), - computed_column_alias_.at(with_scan->column_list(i).column_id())); + &mutable_computed_column_alias(), node->column_list(i).column_id(), + computed_column_alias().at(with_scan->column_list(i).column_id())); } SetPathForColumnList(node->column_list(), alias); ZETASQL_RET_CHECK(query_expression->TrySetFromClause(from)); @@ -8074,10 +8160,10 @@ absl::Status SQLBuilder::VisitResolvedAuxLoadDataStmt( // Make column aliases available for PARTITION BY, CLUSTER BY and table // constraints. for (const ResolvedColumn& column : node->pseudo_column_list()) { - computed_column_alias_[column.column_id()] = column.name(); + mutable_computed_column_alias()[column.column_id()] = column.name(); } for (const auto& col : node->output_column_list()) { - computed_column_alias_[col->column().column_id()] = col->name(); + mutable_computed_column_alias()[col->column().column_id()] = col->name(); } if (node->partition_filter() != nullptr) { @@ -8117,6 +8203,82 @@ absl::Status SQLBuilder::VisitResolvedAuxLoadDataStmt( return absl::OkStatus(); } +absl::StatusOr> +SQLBuilder::ConvertPipeQueryToRegularQuery(const ResolvedScan& pipe_query_node, + absl::string_view pipe_sql) { + auto pipe_query_expr = std::make_unique(); + // The pipe query has to be wrapped in parentheses to act as a subquery. + std::string wrapped_assert_sql = absl::StrCat("(", pipe_sql, ")"); + ZETASQL_RET_CHECK(pipe_query_expr->TrySetFromClause(wrapped_assert_sql)); + ZETASQL_RETURN_IF_ERROR(WrapQueryExpression(&pipe_query_node, pipe_query_expr.get())); + return pipe_query_expr; +} + +absl::Status SQLBuilder::VisitResolvedStaticDescribeScan( + const ResolvedStaticDescribeScan* node) { + // Generate SQL for the input scan. + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input, + ProcessNode(node->input_scan())); + std::unique_ptr query_expr = + std::move(input->query_expression); + ZETASQL_RETURN_IF_ERROR(WrapQueryExpression(node->input_scan(), query_expr.get())); + + // TODO We just lose |> STATIC_DESCRIBE for now. + // When the SQLBuilder supports adding a pipe operator, we should add it. + PushSQLForQueryExpression(node, query_expr.release()); + + return absl::OkStatus(); +} + +absl::Status SQLBuilder::VisitResolvedAssertScan( + const ResolvedAssertScan* node) { + // Generate SQL for the input scan. + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr input, + ProcessNode(node->input_scan())); + std::unique_ptr query_expr = + std::move(input->query_expression); + // Here we wrap the input scan in a subquery to give it a table alias that + // the ASSERT operator can reference. For example, the following SQL complains + // "Unrecognized name: T": + // + // SELECT * + // FROM KeyValue AS T + // |> ASSERT T.key = 'foo' + // + // because the regular SELECT statement does not produce a table alias. We + // change it to the following to make it work: + // + // SELECT * + // FROM ( + // SELECT * + // FROM KeyValue AS T + // ) AS T + // |> ASSERT T.key = 'foo' + ZETASQL_RETURN_IF_ERROR(WrapQueryExpression(node->input_scan(), query_expr.get())); + + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr condition, + ProcessNode(node->condition())); + + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr message, + ProcessNode(node->message())); + + ZETASQL_RET_CHECK(input != nullptr); + ZETASQL_RET_CHECK(condition != nullptr); + ZETASQL_RET_CHECK(message != nullptr); + + // Construct the SQL for the pipe operator as follows: + // |> ASSERT , + std::string assert_sql = + absl::StrCat(query_expr->GetSQLQuery(), " |> ASSERT ", + condition->GetSQL(), ", ", message->GetSQL()); + + ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr assert_query_expr, + ConvertPipeQueryToRegularQuery(*node, assert_sql)); + + PushSQLForQueryExpression(node, assert_query_expr.release()); + return absl::OkStatus(); +} + absl::Status SQLBuilder::VisitResolvedBarrierScan( const ResolvedBarrierScan* node) { // No SQL syntax corresponding to BarrierScan, so we simply generate SQL for diff --git a/zetasql/resolved_ast/sql_builder.h b/zetasql/resolved_ast/sql_builder.h index 39b9d74d6..3d19a6b5b 100644 --- a/zetasql/resolved_ast/sql_builder.h +++ b/zetasql/resolved_ast/sql_builder.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include "zetasql/resolved_ast/resolved_column.h" #include "zetasql/resolved_ast/resolved_node.h" #include "zetasql/resolved_ast/target_syntax.h" +#include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/status/status.h" @@ -51,6 +53,98 @@ namespace zetasql { class QueryExpression; +struct CopyableState { + // Stores the path used to access the resolved columns, keyed by column_id. + // This map needs to updated to reflect the path of the column in the current + // context (e.g. when inside or outside a join or subquery) as we visit nodes + // up the tree. + std::map column_paths; + + // Stores the unique alias we generate for the ResolvedComputedColumn. + // These aliases are mostly used to replace the internal column names assigned + // to these columns in the resolved tree. The key is column_id here. + std::map computed_column_alias; + + // Stores the unique scan_aliases assigned for the corresponding scan nodes. + absl::flat_hash_map scan_alias_map; + + // Stores the unique aliases assigned to tables. + absl::flat_hash_map table_alias_map; + + // Stores the tables that have been used without an explicit alias. + // This is a map from the implicit alias to the identifier (as returned by + // TableToIdentifierLiteral). + // This is because if we have two tables in different nested catalogs S1.T + // and S2.T, they cannot both use the same implicit alias "t" and one of them + // needs to be explicitly aliased. + absl::flat_hash_map tables_with_implicit_alias; + + // Stores the sql text of the columns, keyed by column_id and populated in + // AggregateScan, AnalyticScan, and TVFScan. We refer to the sql text in its + // parent ProjectScan (while building the select_list) and in + // ResolvedColumnRef pointing to the column. + // NOTE: This map is cleared every time we visit a ProjectScan, once it has + // processed all the columns of its child scan. + std::map pending_columns; + + // Stores the sql text of pending columns that are used as parameters of sub + // expressions. + // NOTE: This map is initialized and cleared in each VisitResolvedSubqueryExpr + // call. Unlike pending_columns_ it is not cleared in every ProjectScan as + // correlated columns must be accessible to all scans inside the sub query. + std::map correlated_pending_columns; + + // Stores the WithScan corresponding to the with-query-name. Used in + // WithRefScan to extract the column aliases of the relevant WithScan. + std::map + with_query_name_to_scan; + + // A stack of stacks. An element of an inner stack corresponds to a + // ResolvedUpdateItem node that is currently being processed, and stores the + // target SQL for that node and the array offset SQL (empty if not applicable) + // of the ResolvedUpdateArrayItem child currently being processed. We start a + // new stack just before we process a ResolvedUpdateItem node from a + // ResolvedUpdateStmt (as opposed to a ResolvedUpdateArrayItem). + // + // Stacks are added/removed in GetUpdateItemListSQL. Target SQL is set in + // VisitResolvedUpdateItem. Offset SQL is set in + // VisitResolvedUpdateArrayItem. An inner stack is used to construct a target + // in VisitResolvedUpdateItem when there are no ResolvedUpdateArrayItem + // children. + std::deque>> + update_item_targets_and_offsets; + + // A stack of dml target paths kept to parallel the nesting of dml statements. + // Populated in VisitResolvedUpdateItem and used in + // VisitResolvedStatement to refer the corresponding target path. + std::deque< + std::pair> + nested_dml_targets; + + // All column names referenced in the query. + absl::flat_hash_set col_ref_names; + + // True if we are unparsing the LHS of a SET statement. + bool in_set_lhs = false; + + struct RecursiveQueryInfo { + // Text to be inserted into the generated query to refer to the recursive + // table being referenced. If backticks are needed, this should already be + // present in the query name. + std::string query_name; + + // The recursive scan being referenced. + const ResolvedRecursiveScan* scan; + }; + + // Stack of names of recursive queries being defined. Any + // ResolvedRecursiveRefScan node must refer to the query at the top of the + // stack. + std::stack recursive_query_info; +}; + // SQLBuilder takes the ZetaSQL Resolved ASTs and generates its equivalent SQL // form. // @@ -154,14 +248,119 @@ class SQLBuilder : public ResolvedASTVisitor { // Returns the sql string for the last visited ResolvedAST. std::string sql(); + // Returns a map of column id to its path expression. + std::map& mutable_column_paths() { + return state_.column_paths; + } + const std::map& column_paths() const { + return state_.column_paths; + } + + // Returns a map of column id to its alias. + std::map& mutable_computed_column_alias() { + return state_.computed_column_alias; + } + const std::map& computed_column_alias() + const { + return state_.computed_column_alias; + } + + // Returns a set of all column names referenced in the query. + absl::flat_hash_set& mutable_col_ref_names() { + return state_.col_ref_names; + } + const absl::flat_hash_set& col_ref_names() const { + return state_.col_ref_names; + } + + // Returns a map of scans to it unique aliases. + absl::flat_hash_map& + mutable_scan_alias_map() { + return state_.scan_alias_map; + } + const absl::flat_hash_map& scan_alias_map() + const { + return state_.scan_alias_map; + } + + // Returns a map of tables to it unique aliases. + absl::flat_hash_map& mutable_table_alias_map() { + return state_.table_alias_map; + } + const absl::flat_hash_map& table_alias_map() + const { + return state_.table_alias_map; + } + + // Returns a map of pending column id to SQL string. + std::map& mutable_pending_columns() { + return state_.pending_columns; + } + const std::map& pending_columns() const { + return state_.pending_columns; + } + + // Returns a map of correlated pending column id to SQL string. + std::map& + mutable_correlated_pending_columns() { + return state_.correlated_pending_columns; + } + const std::map& correlated_pending_columns() + const { + return state_.correlated_pending_columns; + } + + // Returns a map of implicit alias to the table identifier. + absl::flat_hash_map& + mutable_tables_with_implicit_alias() { + return state_.tables_with_implicit_alias; + } + const absl::flat_hash_map& + tables_with_implicit_alias() const { + return state_.tables_with_implicit_alias; + } + + // Returns a deque of dml target path and alias. + std::deque< + std::pair>& + mutable_nested_dml_targets() { + return state_.nested_dml_targets; + } + const std::deque< + std::pair>& + nested_dml_targets() const { + return state_.nested_dml_targets; + } + + // Returns a stack of stack of update item target path and alias. + std::deque>>& + mutable_update_item_targets_and_offsets() { + return state_.update_item_targets_and_offsets; + } + const std::deque>>& + update_item_targets_and_offsets() const { + return state_.update_item_targets_and_offsets; + } + + // Set the max seen alias id. + void set_max_seen_alias_id(int max_seen_alias_id); + + // Returns the max seen alias id. + int* mutable_max_seen_alias_id(); + int max_seen_alias_id() const; + // Visit methods for types of ResolvedStatement. absl::Status VisitResolvedQueryStmt(const ResolvedQueryStmt* node) override; absl::Status VisitResolvedExplainStmt( const ResolvedExplainStmt* node) override; + absl::Status VisitResolvedCreateConnectionStmt( + const ResolvedCreateConnectionStmt* node) override; absl::Status VisitResolvedCreateDatabaseStmt( const ResolvedCreateDatabaseStmt* node) override; absl::Status VisitResolvedCreateIndexStmt( - const ResolvedCreateIndexStmt* node) override; + const ResolvedCreateIndexStmt* node) override; absl::Status VisitResolvedCreateModelStmt( const ResolvedCreateModelStmt* node) override; absl::Status VisitResolvedCreateSchemaStmt( @@ -211,8 +410,7 @@ class SQLBuilder : public ResolvedASTVisitor { const ResolvedDefineTableStmt* node) override; absl::Status VisitResolvedDescribeStmt( const ResolvedDescribeStmt* node) override; - absl::Status VisitResolvedShowStmt( - const ResolvedShowStmt* node) override; + absl::Status VisitResolvedShowStmt(const ResolvedShowStmt* node) override; absl::Status VisitResolvedBeginStmt(const ResolvedBeginStmt* node) override; absl::Status VisitResolvedSetTransactionStmt( const ResolvedSetTransactionStmt* node) override; @@ -225,11 +423,9 @@ class SQLBuilder : public ResolvedASTVisitor { const ResolvedRunBatchStmt* node) override; absl::Status VisitResolvedAbortBatchStmt( const ResolvedAbortBatchStmt* node) override; - absl::Status VisitResolvedDeleteStmt( - const ResolvedDeleteStmt* node) override; + absl::Status VisitResolvedDeleteStmt(const ResolvedDeleteStmt* node) override; absl::Status VisitResolvedUndropStmt(const ResolvedUndropStmt* node) override; - absl::Status VisitResolvedDropStmt( - const ResolvedDropStmt* node) override; + absl::Status VisitResolvedDropStmt(const ResolvedDropStmt* node) override; absl::Status VisitResolvedDropFunctionStmt( const ResolvedDropFunctionStmt* node) override; absl::Status VisitResolvedDropTableFunctionStmt( @@ -246,16 +442,14 @@ class SQLBuilder : public ResolvedASTVisitor { const ResolvedDropIndexStmt* node) override; absl::Status VisitResolvedTruncateStmt( const ResolvedTruncateStmt* node) override; - absl::Status VisitResolvedUpdateStmt( - const ResolvedUpdateStmt* node) override; - absl::Status VisitResolvedInsertStmt( - const ResolvedInsertStmt* node) override; + absl::Status VisitResolvedUpdateStmt(const ResolvedUpdateStmt* node) override; + absl::Status VisitResolvedInsertStmt(const ResolvedInsertStmt* node) override; absl::Status VisitResolvedMergeStmt(const ResolvedMergeStmt* node) override; absl::Status VisitResolvedMergeWhen(const ResolvedMergeWhen* node) override; - absl::Status VisitResolvedGrantStmt( - const ResolvedGrantStmt* node) override; - absl::Status VisitResolvedRevokeStmt( - const ResolvedRevokeStmt* node) override; + absl::Status VisitResolvedGrantStmt(const ResolvedGrantStmt* node) override; + absl::Status VisitResolvedRevokeStmt(const ResolvedRevokeStmt* node) override; + absl::Status VisitResolvedAlterConnectionStmt( + const ResolvedAlterConnectionStmt* node) override; absl::Status VisitResolvedAlterDatabaseStmt( const ResolvedAlterDatabaseStmt* node) override; absl::Status VisitResolvedAlterPrivilegeRestrictionStmt( @@ -280,8 +474,7 @@ class SQLBuilder : public ResolvedASTVisitor { const ResolvedAlterApproxViewStmt* node) override; absl::Status VisitResolvedAlterModelStmt( const ResolvedAlterModelStmt* node) override; - absl::Status VisitResolvedRenameStmt( - const ResolvedRenameStmt* node) override; + absl::Status VisitResolvedRenameStmt(const ResolvedRenameStmt* node) override; absl::Status VisitResolvedImportStmt(const ResolvedImportStmt* node) override; absl::Status VisitResolvedModuleStmt(const ResolvedModuleStmt* node) override; absl::Status VisitResolvedAnalyzeStmt( @@ -397,8 +590,7 @@ class SQLBuilder : public ResolvedASTVisitor { const ResolvedRecursionDepthModifier* node) override; absl::Status VisitResolvedWithRefScan( const ResolvedWithRefScan* node) override; - absl::Status VisitResolvedSampleScan( - const ResolvedSampleScan* node) override; + absl::Status VisitResolvedSampleScan(const ResolvedSampleScan* node) override; absl::Status VisitResolvedSingleRowScan( const ResolvedSingleRowScan* node) override; absl::Status VisitResolvedPivotScan(const ResolvedPivotScan* node) override; @@ -406,6 +598,9 @@ class SQLBuilder : public ResolvedASTVisitor { const ResolvedUnpivotScan* node) override; absl::Status VisitResolvedGroupRowsScan( const ResolvedGroupRowsScan* node) override; + absl::Status VisitResolvedStaticDescribeScan( + const ResolvedStaticDescribeScan* node) override; + absl::Status VisitResolvedAssertScan(const ResolvedAssertScan* node) override; absl::Status VisitResolvedBarrierScan( const ResolvedBarrierScan* node) override; @@ -430,6 +625,9 @@ class SQLBuilder : public ResolvedASTVisitor { absl::Status DefaultVisit(const ResolvedNode* node) override; protected: + SQLBuilder(int* max_seen_alias_id_root_ptr, const SQLBuilderOptions& options, + const CopyableState& state); + // Holds SQL text of nodes representing expressions/subqueries and // QueryExpression for scan nodes. struct QueryFragment { @@ -536,9 +734,6 @@ class SQLBuilder : public ResolvedASTVisitor { // i.e. . std::string GetColumnPath(const ResolvedColumn& column); - // Returns whether the column has an existing alias. - bool HasColumnAlias(const ResolvedColumn& column); - // Returns the alias to be used to select the column. std::string GetColumnAlias(const ResolvedColumn& column); @@ -804,51 +999,20 @@ class SQLBuilder : public ResolvedASTVisitor { // debugging purposes. std::deque> query_fragments_; - // Stores the path used to access the resolved columns, keyed by column_id. - // This map needs to updated to reflect the path of the column in the current - // context (e.g. when inside or outside a join or subquery) as we visit nodes - // up the tree. - std::map column_paths_; - - // Stores the unique alias we generate for the ResolvedComputedColumn. - // These aliases are mostly used to replace the internal column names assigned - // to these columns in the resolved tree. The key is column_id here. - std::map computed_column_alias_; + // The maximum seen alias id so far. This number is used to generate unique + // alias ids. + int max_seen_alias_id_ = 0; - // Stores the unique scan_aliases assigned for the corresponding scan nodes. - absl::flat_hash_map scan_alias_map_; - - // Stores the unique aliases assigned to tables. - absl::flat_hash_map table_alias_map_; - - // Stores the tables that have been used without an explicit alias. - // This is a map from the implicit alias to the identifier (as returned by - // TableToIdentifierLiteral). - // This is because if we have two tables in different nested catalogs S1.T - // and S2.T, they cannot both use the same implicit alias "t" and one of them - // needs to be explicitly aliased. - absl::flat_hash_map tables_with_implicit_alias_; + // Always points to `max_seen_alias_id_` of the root SQLBuilder. + // `max_seen_alias_id_root_ptr_` is nullptr only if it's the root SQLBuilder. + // Otherwise, it should never be nullptr. + int* max_seen_alias_id_root_ptr_ = nullptr; // Stores the target table alias for DML Returning statements. std::string returning_table_alias_; // Used to generate a unique alias id. - zetasql_base::SequenceNumber scan_id_sequence_; - - // Stores the sql text of the columns, keyed by column_id and populated in - // AggregateScan, AnalyticScan, and TVFScan. We refer to the sql text in its - // parent ProjectScan (while building the select_list) and in - // ResolvedColumnRef pointing to the column. - // NOTE: This map is cleared every time we visit a ProjectScan, once it has - // processed all the columns of its child scan. - std::map pending_columns_; - - // Stores the sql text of pending columns that are used as parameters of sub - // expressions. - // NOTE: This map is initialized and cleared in each VisitResolvedSubqueryExpr - // call. Unlike pending_columns_ it is not cleared in every ProjectScan as - // correlated columns must be accessible to all scans inside the sub query. - std::map correlated_pending_columns_; + zetasql_base::SequenceNumber alias_id_sequence_; // Holds sql text for node representing an analytic function. class AnalyticFunctionInfo; @@ -870,62 +1034,19 @@ class SQLBuilder : public ResolvedASTVisitor { // parent node (i.e. AnalyticFunctionGroup). std::unique_ptr pending_analytic_function_; - // Stores the WithScan corresponding to the with-query-name. Used in - // WithRefScan to extract the column aliases of the relevant WithScan. - std::map - with_query_name_to_scan_; - - // A stack of stacks. An element of an inner stack corresponds to a - // ResolvedUpdateItem node that is currently being processed, and stores the - // target SQL for that node and the array offset SQL (empty if not applicable) - // of the ResolvedUpdateArrayItem child currently being processed. We start a - // new stack just before we process a ResolvedUpdateItem node from a - // ResolvedUpdateStmt (as opposed to a ResolvedUpdateArrayItem). - // - // Stacks are added/removed in GetUpdateItemListSQL. Target SQL is set in - // VisitResolvedUpdateItem. Offset SQL is set in - // VisitResolvedUpdateArrayItem. An inner stack is used to construct a target - // in VisitResolvedUpdateItem when there are no ResolvedUpdateArrayItem - // children. - std::deque>> - update_item_targets_and_offsets_; - - // A stack of dml target paths kept to parallel the nesting of dml statements. - // Populated in VisitResolvedUpdateItem and used in - // VisitResolvedStatement to refer the corresponding target path. - std::deque< - std::pair> - nested_dml_targets_; - - // All column names referenced in the query. - absl::flat_hash_set col_ref_names_; - std::string sql_; // Options for building SQL. SQLBuilderOptions options_; - // True if we are unparsing the LHS of a SET statement. - bool in_set_lhs_ = false; - - struct RecursiveQueryInfo { - // Text to be inserted into the generated query to refer to the recursive - // table being referenced. If backticks are needed, this should already be - // present in the query name. - std::string query_name; - - // The recursive scan being referenced. - const ResolvedRecursiveScan* scan; - }; - - // Stack of names of recursive queries being defined. Any - // ResolvedRecursiveRefScan node must refer to the query at the top of the - // stack. - std::stack recursive_query_info_; + // Returns the name alias for a table in VisitResolvedTableScan and appends to + // the string if necessary. + virtual std::string GetTableAliasForVisitResolvedTableScan( + const ResolvedTableScan& node, std::string* from); private: + CopyableState state_; + // Helper function to perform operation on value table's column for // ResolvedTableScan. virtual absl::Status AddValueTableAliasForVisitResolvedTableScan( @@ -941,11 +1062,6 @@ class SQLBuilder : public ResolvedASTVisitor { virtual std::string TableNameToIdentifierLiteral( absl::string_view table_name); - // Returns the name alias for a table in VisitResolvedTableScan and appends to - // the string if necessary. - virtual std::string GetTableAliasForVisitResolvedTableScan( - const ResolvedTableScan& node, std::string* from); - // Returns a new unique alias name. The default implementation generates the // name as "a_". virtual std::string GenerateUniqueAliasName(); @@ -988,6 +1104,23 @@ class SQLBuilder : public ResolvedASTVisitor { absl::StatusOr GetOriginalInputScanForCorresponding( const ResolvedScan* scan); + // Returns a QueryExpression that represents the input `pipe_sql` converted to + // a regular query by wrapping it in a SELECT statement. `pipe_query_node` is + // the ResolvedScan that corresponds to the `pipe_sql`. + // + // SqlBuilder currently cannot handle pipe operators in general, so we need to + // wrap pipe scans in a regular SQL like this: + // + // SELECT ... + // FROM ( + // + // ) AS assert_scan + // + // to allow the outer unparsing to work properly. + absl::StatusOr> + ConvertPipeQueryToRegularQuery(const ResolvedScan& pipe_query_node, + absl::string_view pipe_sql); + // When building function call which defines a side effects scope, we may need // to inline some column refs to input scans. This stack keeps track of the // current error handling context. Note that the same node may now be visited diff --git a/zetasql/resolved_ast/target_syntax.h b/zetasql/resolved_ast/target_syntax.h index 96526a2be..60d9af918 100644 --- a/zetasql/resolved_ast/target_syntax.h +++ b/zetasql/resolved_ast/target_syntax.h @@ -26,7 +26,8 @@ namespace zetasql { // This is a side channel used by SQLBuilder to decide which SQL syntax to // produce when multiple choices are available. enum class SQLBuildTargetSyntax { - kGroupByAll, // Represents GROUP BY ALL produced a ResolvedAggregateScan + kGroupByAll, // Represents GROUP BY ALL produced a + // ResolvedAggregateScan. }; using TargetSyntaxMap = diff --git a/zetasql/resolved_ast/validator.cc b/zetasql/resolved_ast/validator.cc index bd05a9558..f1bda5695 100644 --- a/zetasql/resolved_ast/validator.cc +++ b/zetasql/resolved_ast/validator.cc @@ -589,10 +589,10 @@ absl::Status Validator::ValidateResolvedExpr( visible_columns, visible_parameters, expr->GetAs()); } - case RESOLVED_MAKE_STRUCT: - return ValidateResolvedExprList( - visible_columns, visible_parameters, - expr->GetAs()->field_list()); + case RESOLVED_MAKE_STRUCT: { + return ValidateResolvedMakeStruct(visible_columns, visible_parameters, + expr->GetAs()); + } case RESOLVED_MAKE_PROTO: { for (const auto& resolved_make_proto_field : expr->GetAs()->field_list()) { @@ -898,6 +898,26 @@ absl::Status Validator::ValidateResolvedAnalyticFunctionCall( return absl::OkStatus(); } +absl::Status Validator::ValidateResolvedMakeStruct( + const std::set& visible_columns, + const std::set& visible_parameters, + const ResolvedMakeStruct* expr) { + PushErrorContext push(this, expr); + ZETASQL_RETURN_IF_ERROR(ValidateResolvedExprList( + visible_columns, visible_parameters, + expr->GetAs()->field_list())); + VALIDATOR_RET_CHECK(expr->type()->IsStruct()); + const StructType* struct_type = expr->type()->AsStruct(); + for (int i = 0; i < struct_type->num_fields(); ++i) { + VALIDATOR_RET_CHECK( + struct_type->field(i).type->Equals(expr->field_list(i)->type())) + << "Field type mismatch for index " << i + << "; expected: " << struct_type->field(i).type->DebugString() + << " but found " << expr->field_list(i)->type()->DebugString(); + } + return absl::OkStatus(); +} + absl::Status Validator::ValidateResolvedGetProtoFieldExpr( const std::set& visible_columns, const std::set& visible_parameters, @@ -2005,6 +2025,10 @@ absl::Status Validator::ValidateGroupSelectionThresholdExpr( if (group_threshold_expr == nullptr) { return absl::OkStatus(); } + // The group threshold expression has a special form, which we validate below. + // First and foremost, it has to be a valid expression, though. + ZETASQL_RETURN_IF_ERROR(ValidateResolvedExpr(visible_columns, visible_parameters, + group_threshold_expr)); if (language_options_.LanguageFeatureEnabled( FEATURE_DIFFERENTIAL_PRIVACY_MIN_PRIVACY_UNITS_PER_GROUP)) { auto is_min_privacy_units_per_group = @@ -2530,9 +2554,10 @@ absl::Status Validator::ValidateResolvedSetOperationItem( output_column_list.at(i).type()->Equals(input_column.type())) << "The " << i << "-th SetOperation input column type does not match output type. " - << "Input column type: " << input_column.type()->DebugString() - << " Output column type: " - << output_column_list.at(i).type()->DebugString(); + << "Input column " << input_column.DebugString() + << " with type: " << input_column.type()->DebugString() + << " Output column " << output_column_list.at(i).DebugString() + << " with type: " << output_column_list.at(i).type()->DebugString(); VALIDATOR_RET_CHECK(zetasql_base::ContainsKey(produced_columns, input_column)) << "SetOperation input scan does not produce column referenced in " @@ -3050,7 +3075,9 @@ absl::Status Validator::ValidateResolvedWithRefScan( << scan_column.DebugString(); VALIDATOR_RET_CHECK(scan_column.type()->Equals(subquery_column.type())) << "Type mismatch between ResolvedWithRefScan and with query for " - << "column " << scan_column.DebugString(); + << "column " << scan_column.DebugString() << " has type " + << scan_column.type()->DebugString() << " but expected " + << subquery_column.type()->DebugString(); } return absl::OkStatus(); } @@ -3119,6 +3146,10 @@ absl::Status Validator::ValidateResolvedStatementInternal( status = ValidateResolvedStatementInternal( statement->GetAs()->statement()); break; + case RESOLVED_CREATE_CONNECTION_STMT: + status = ValidateResolvedCreateConnectionStmt( + statement->GetAs()); + break; case RESOLVED_CREATE_DATABASE_STMT: status = ValidateResolvedCreateDatabaseStmt( statement->GetAs()); @@ -3349,6 +3380,10 @@ absl::Status Validator::ValidateResolvedStatementInternal( status = ValidateResolvedAlterObjectStmt( statement->GetAs()); break; + case RESOLVED_ALTER_CONNECTION_STMT: + status = ValidateResolvedAlterObjectStmt( + statement->GetAs()); + break; case RESOLVED_ALTER_TABLE_STMT: status = ValidateResolvedAlterObjectStmt( statement->GetAs()); @@ -3479,7 +3514,17 @@ absl::Status Validator::ValidateResolvedIndexStmt( ZETASQL_RETURN_IF_ERROR(ValidateResolvedExprList(visible_columns, /*visible_parameters=*/{}, stmt->storing_expression_list())); + ZETASQL_RETURN_IF_ERROR(ValidateResolvedExprList(visible_columns, + /*visible_parameters=*/{}, + stmt->partition_by_list())); + return absl::OkStatus(); +} +absl::Status Validator::ValidateResolvedCreateConnectionStmt( + const ResolvedCreateConnectionStmt* stmt) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + PushErrorContext push(this, stmt); + ZETASQL_RETURN_IF_ERROR(ValidateOptionsList(stmt->option_list())); return absl::OkStatus(); } @@ -4832,6 +4877,14 @@ absl::Status Validator::ValidateResolvedScan( scan_subtype_status = ValidateGroupRowsScan(scan->GetAs()); break; + case RESOLVED_STATIC_DESCRIBE_SCAN: + scan_subtype_status = ValidateResolvedStaticDescribeScan( + scan->GetAs(), visible_parameters); + break; + case RESOLVED_ASSERT_SCAN: + scan_subtype_status = ValidateResolvedAssertScan( + scan->GetAs(), visible_parameters); + break; case RESOLVED_BARRIER_SCAN: scan_subtype_status = ValidateResolvedBarrierScan( scan->GetAs(), visible_parameters); @@ -4982,6 +5035,9 @@ absl::Status Validator::ValidateResolvedScanOrdering(const ResolvedScan* scan) { case RESOLVED_EXECUTE_AS_ROLE_SCAN: input_scan = scan->GetAs()->input_scan(); break; + case RESOLVED_STATIC_DESCRIBE_SCAN: + input_scan = scan->GetAs()->input_scan(); + break; // For all other scan types, is_ordered is not allowed. default: @@ -6498,6 +6554,48 @@ absl::Status Validator::ValidateResolvedUnpivotScan( return absl::OkStatus(); } +absl::Status Validator::ValidateResolvedStaticDescribeScan( + const ResolvedStaticDescribeScan* scan, + const std::set& visible_parameters) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + PushErrorContext push(this, scan); + + ZETASQL_RETURN_IF_ERROR(ValidateResolvedScan(scan->input_scan(), visible_parameters)); + + std::set visible_columns; + ZETASQL_RETURN_IF_ERROR( + AddColumnList(scan->input_scan()->column_list(), &visible_columns)); + + ZETASQL_RETURN_IF_ERROR(CheckColumnList(scan, visible_columns)); + + return absl::OkStatus(); +} + +absl::Status Validator::ValidateResolvedAssertScan( + const ResolvedAssertScan* scan, + const std::set& visible_parameters) { + RETURN_ERROR_IF_OUT_OF_STACK_SPACE(); + PushErrorContext push(this, scan); + + ZETASQL_RETURN_IF_ERROR(ValidateResolvedScan(scan->input_scan(), visible_parameters)); + + std::set visible_columns; + ZETASQL_RETURN_IF_ERROR( + AddColumnList(scan->input_scan()->column_list(), &visible_columns)); + + ZETASQL_RETURN_IF_ERROR(ValidateResolvedExpr(visible_columns, visible_parameters, + scan->condition())); + ZETASQL_RETURN_IF_ERROR(ValidateResolvedExpr(visible_columns, visible_parameters, + scan->message())); + + VALIDATOR_RET_CHECK(scan->condition()->type()->IsBool()); + VALIDATOR_RET_CHECK(scan->message()->type()->IsString()); + + ZETASQL_RETURN_IF_ERROR(CheckColumnList(scan, visible_columns)); + + return absl::OkStatus(); +} + absl::Status Validator::ValidateResolvedAuxLoadDataPartitionFilter( const std::set& visible_columns, const ResolvedAuxLoadDataPartitionFilter* partition_filter) { diff --git a/zetasql/resolved_ast/validator.h b/zetasql/resolved_ast/validator.h index 823be86f5..d7819c38b 100644 --- a/zetasql/resolved_ast/validator.h +++ b/zetasql/resolved_ast/validator.h @@ -115,6 +115,8 @@ class Validator { const ResolvedCreatePrivilegeRestrictionStmt* stmt); absl::Status ValidateResolvedCreateRowAccessPolicyStmt( const ResolvedCreateRowAccessPolicyStmt* stmt); + absl::Status ValidateResolvedCreateConnectionStmt( + const ResolvedCreateConnectionStmt* stmt); absl::Status ValidateResolvedCreateConstantStmt( const ResolvedCreateConstantStmt* stmt); absl::Status ValidateResolvedCreateFunctionStmt( @@ -348,6 +350,11 @@ class Validator { const std::set& visible_parameters, const ResolvedAnalyticFunctionCall* call); + absl::Status ValidateResolvedMakeStruct( + const std::set& visible_columns, + const std::set& visible_parameters, + const ResolvedMakeStruct* expr); + absl::Status ValidateResolvedGetProtoFieldExpr( const std::set& visible_columns, const std::set& visible_parameters, @@ -621,6 +628,14 @@ class Validator { const ResolvedUnpivotScan* scan, const std::set& visible_parameters); + absl::Status ValidateResolvedStaticDescribeScan( + const ResolvedStaticDescribeScan* scan, + const std::set& visible_parameters); + + absl::Status ValidateResolvedAssertScan( + const ResolvedAssertScan* scan, + const std::set& visible_parameters); + absl::Status ValidateResolvedWithPartitionColumns( const ResolvedWithPartitionColumns* with_partition_columns, std::set* visible_columns); diff --git a/zetasql/scripting/BUILD b/zetasql/scripting/BUILD index bb817ec9f..130e17d21 100644 --- a/zetasql/scripting/BUILD +++ b/zetasql/scripting/BUILD @@ -61,6 +61,7 @@ cc_library( "//zetasql/public:parse_location", "//zetasql/public:type", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/flags:flag", @@ -111,8 +112,12 @@ cc_library( deps = [ ":parsed_script", "//zetasql/base:status", + "//zetasql/common:errors", "//zetasql/parser", + "//zetasql/public:error_helpers", "//zetasql/public:options_cc_proto", + "@com_google_absl//absl/base", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", ], diff --git a/zetasql/scripting/error_helpers_test.cc b/zetasql/scripting/error_helpers_test.cc index c8c6ddaac..828ad8d0d 100644 --- a/zetasql/scripting/error_helpers_test.cc +++ b/zetasql/scripting/error_helpers_test.cc @@ -74,8 +74,11 @@ void TestConvertErrorWithSource( error_stmt_text); std::unique_ptr parser_output; - ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/true, &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = true, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); ASSERT_EQ(parser_output->script()->statement_list().size(), 2); const ASTStatement* error_stmt = parser_output->script()->statement_list().at(1); @@ -134,8 +137,11 @@ TEST(ConvertLocalErrorToScriptError, InvalidErrorLocation) { const std::string error_stmt_text = "SELECT error_location"; std::unique_ptr parser_output; - ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), ERROR_MESSAGE_WITH_PAYLOAD, - /*keep_error_location_payload=*/true, &parser_output)); + ZETASQL_ASSERT_OK(ParseScript(sql, ParserOptions(), + {.mode = ERROR_MESSAGE_WITH_PAYLOAD, + .attach_error_location_payload = true, + .stability = GetDefaultErrorMessageStability()}, + &parser_output)); ASSERT_EQ(parser_output->script()->statement_list().size(), 2); const ASTStatement* error_stmt = parser_output->script()->statement_list().at(1); diff --git a/zetasql/scripting/parse_helpers.cc b/zetasql/scripting/parse_helpers.cc index effca55e5..7b31a2f70 100644 --- a/zetasql/scripting/parse_helpers.cc +++ b/zetasql/scripting/parse_helpers.cc @@ -18,29 +18,29 @@ #include +#include "zetasql/parser/parser.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/scripting/parsed_script.h" #include "absl/status/statusor.h" +#include "absl/strings/string_view.h" #include "zetasql/base/status_macros.h" namespace zetasql { absl::StatusOr> ParseAndValidateScript( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, + ErrorMessageOptions error_message_options, const ParsedScriptOptions& parsed_script_options) { std::unique_ptr parser_output; - ZETASQL_RETURN_IF_ERROR( - ParseScript(script_string, parser_options, error_message_mode, - /*keep_error_location_payload=*/error_message_mode == - ErrorMessageMode::ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_RETURN_IF_ERROR(ParseScript(script_string, parser_options, + error_message_options, &parser_output)); - // Verify that we can obtain a ParsedScript from the AST. This performs + // Verify that we can obtain a ParsedScript from the AST. This performs // various checks, such as that BREAK and CONTINUE statements have an // enclosing loop. ZETASQL_ASSIGN_OR_RETURN( std::unique_ptr parsed_script, ParsedScript::Create(script_string, parser_output->script(), - error_message_mode, parsed_script_options)); + error_message_options, parsed_script_options)); return parser_output; } } // namespace zetasql diff --git a/zetasql/scripting/parse_helpers.h b/zetasql/scripting/parse_helpers.h index 940fdb6cf..aeaf0806b 100644 --- a/zetasql/scripting/parse_helpers.h +++ b/zetasql/scripting/parse_helpers.h @@ -20,8 +20,10 @@ #include #include "zetasql/parser/parser.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/public/options.pb.h" #include "zetasql/scripting/parsed_script.h" +#include "absl/base/macros.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" @@ -33,9 +35,18 @@ namespace zetasql { // the same block or any enclosing block. absl::StatusOr> ParseAndValidateScript( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, + ErrorMessageOptions error_message_options, const ParsedScriptOptions& parsed_script_options = {}); +ABSL_DEPRECATED("Inline me!") +inline absl::StatusOr> ParseAndValidateScript( + absl::string_view script_string, const ParserOptions& parser_options, + ErrorMessageMode error_message_mode, + const ParsedScriptOptions& parsed_script_options = {}) { + return ParseAndValidateScript(script_string, parser_options, + {.mode = error_message_mode}, + parsed_script_options); +} } // namespace zetasql #endif // ZETASQL_SCRIPTING_PARSE_HELPERS_H_ diff --git a/zetasql/scripting/parsed_script.cc b/zetasql/scripting/parsed_script.cc index ea03de4be..b1de4e149 100644 --- a/zetasql/scripting/parsed_script.cc +++ b/zetasql/scripting/parsed_script.cc @@ -43,6 +43,7 @@ #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/variant.h" @@ -248,7 +249,7 @@ class ValidateVariableDeclarationsVisitor ConvertInternalErrorLocationToExternal( MakeSqlErrorAtPoint(source_location) << source_message, script_text), - parsed_script_->error_message_mode(), script_text); + parsed_script_->error_message_options().mode, script_text); return MakeSqlError().AttachPayload(location) << error_message; } @@ -260,7 +261,7 @@ class ValidateVariableDeclarationsVisitor MakeInternalErrorLocation(node), ConvertInternalErrorLocationToExternal(MakeSqlError() << source_message, script_text), - parsed_script_->error_message_mode(), script_text); + parsed_script_->error_message_options().mode, script_text); return MakeSqlError().AttachPayload(location) << error_message; } @@ -296,10 +297,16 @@ class ValidateVariableDeclarationsVisitor bool IsAllowedBeforeDeclare(const ASTStatement* node) { const auto* assignment = node->GetAsOrNull(); - return assignment != nullptr && - absl::c_linear_search( + if (assignment == nullptr) { + return false; + } + const std::string identifier = + assignment->system_variable()->path()->ToIdentifierPathString(); + return absl::c_find_if( options_.system_variables_allowed_before_declare, - assignment->system_variable()->path()->ToIdentifierPathString()); + [&](absl::string_view system_variable) { + return zetasql_base::CaseEqual(system_variable, identifier); + }) != options_.system_variables_allowed_before_declare.end(); } // Associates each active variable with the location of its declaration. @@ -461,67 +468,63 @@ absl::Status ParsedScript::GatherInformationAndRunChecksInternal( absl::Status ParsedScript::GatherInformationAndRunChecks( const ParsedScriptOptions& options) { return ConvertInternalErrorLocationAndAdjustErrorString( - ErrorMessageOptions{ - .mode = error_message_mode(), - .attach_error_location_payload = - (error_message_mode() == ERROR_MESSAGE_WITH_PAYLOAD), - .stability = ERROR_MESSAGE_STABILITY_PRODUCTION}, - script_text(), GatherInformationAndRunChecksInternal(options)); + error_message_options_, script_text(), + GatherInformationAndRunChecksInternal(options)); } ParsedScript::ParsedScript(absl::string_view script_string, const ASTScript* ast_script, std::unique_ptr parser_output, - ErrorMessageMode error_message_mode, + ErrorMessageOptions error_message_options, ArgumentTypeMap routine_arguments, bool is_procedure) : parser_output_(std::move(parser_output)), ast_script_(ast_script), script_string_(script_string), - error_message_mode_(error_message_mode), + error_message_options_(error_message_options), routine_arguments_(std::move(routine_arguments)), is_procedure_(is_procedure) {} absl::StatusOr> ParsedScript::CreateInternal( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, ArgumentTypeMap routine_arguments, - bool is_procedure, const ParsedScriptOptions& options) { + ErrorMessageOptions error_message_options, + ArgumentTypeMap routine_arguments, bool is_procedure, + const ParsedScriptOptions& options) { std::unique_ptr parser_output; - ZETASQL_RETURN_IF_ERROR(ParseScript(script_string, parser_options, error_message_mode, - /*keep_error_location_payload=*/ - error_message_mode == ERROR_MESSAGE_WITH_PAYLOAD, - &parser_output)); + ZETASQL_RETURN_IF_ERROR(ParseScript(script_string, parser_options, + error_message_options, &parser_output)); const ASTScript* ast_script = parser_output->script(); std::unique_ptr parsed_script = absl::WrapUnique(new ParsedScript( script_string, ast_script, std::move(parser_output), - error_message_mode, std::move(routine_arguments), is_procedure)); + error_message_options, std::move(routine_arguments), is_procedure)); ZETASQL_RETURN_IF_ERROR(parsed_script->GatherInformationAndRunChecks(options)); return parsed_script; } absl::StatusOr> ParsedScript::Create( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, const ParsedScriptOptions& options) { - return CreateInternal(script_string, parser_options, error_message_mode, {}, - false, options); + ErrorMessageOptions error_message_options, + const ParsedScriptOptions& options) { + return CreateInternal(script_string, parser_options, error_message_options, + {}, false, options); } absl::StatusOr> ParsedScript::CreateForRoutine( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, ArgumentTypeMap routine_arguments, - const ParsedScriptOptions& options) { - return CreateInternal(script_string, parser_options, error_message_mode, + ErrorMessageOptions error_message_options, + ArgumentTypeMap routine_arguments, const ParsedScriptOptions& options) { + return CreateInternal(script_string, parser_options, error_message_options, std::move(routine_arguments), /*is_procedure=*/true, options); } absl::StatusOr> ParsedScript::CreateForRoutine( absl::string_view script_string, const ASTScript* ast_script, - ErrorMessageMode error_message_mode, ArgumentTypeMap routine_arguments, - const ParsedScriptOptions& options) { + ErrorMessageOptions error_message_options, + ArgumentTypeMap routine_arguments, const ParsedScriptOptions& options) { std::unique_ptr parsed_script = absl::WrapUnique( new ParsedScript(script_string, ast_script, /*parser_output=*/nullptr, - error_message_mode, std::move(routine_arguments), + error_message_options, std::move(routine_arguments), /*is_procedure=*/true)); ZETASQL_RETURN_IF_ERROR(parsed_script->GatherInformationAndRunChecks(options)); return parsed_script; @@ -529,10 +532,11 @@ absl::StatusOr> ParsedScript::CreateForRoutine( absl::StatusOr> ParsedScript::Create( absl::string_view script_string, const ASTScript* ast_script, - ErrorMessageMode error_message_mode, const ParsedScriptOptions& options) { + ErrorMessageOptions error_message_options, + const ParsedScriptOptions& options) { std::unique_ptr parsed_script = absl::WrapUnique( new ParsedScript(script_string, ast_script, /*parser_output=*/nullptr, - error_message_mode, {}, /*is_procedure=*/false)); + error_message_options, {}, /*is_procedure=*/false)); ZETASQL_RETURN_IF_ERROR(parsed_script->GatherInformationAndRunChecks(options)); return parsed_script; } @@ -615,12 +619,8 @@ ParsedScript::StringSet ParsedScript::GetAllNamedParameters() const { absl::Status ParsedScript::CheckQueryParameters( const ParsedScript::QueryParameters& parameters) const { return ConvertInternalErrorLocationAndAdjustErrorString( - ErrorMessageOptions{ - .mode = error_message_mode(), - .attach_error_location_payload = - (error_message_mode() == ERROR_MESSAGE_WITH_PAYLOAD), - .stability = ERROR_MESSAGE_STABILITY_PRODUCTION}, - script_text(), CheckQueryParametersInternal(parameters)); + error_message_options_, script_text(), + CheckQueryParametersInternal(parameters)); } absl::Status ParsedScript::CheckQueryParametersInternal( diff --git a/zetasql/scripting/parsed_script.h b/zetasql/scripting/parsed_script.h index 6be1a90fb..36556e1eb 100644 --- a/zetasql/scripting/parsed_script.h +++ b/zetasql/scripting/parsed_script.h @@ -26,8 +26,10 @@ #include #include +#include "zetasql/common/errors.h" #include "zetasql/parser/parse_tree.h" #include "zetasql/parser/parser.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/public/id_string.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/parse_location.h" @@ -35,6 +37,7 @@ #include "zetasql/scripting/control_flow_graph.h" #include "zetasql/scripting/type_aliases.h" #include "zetasql/base/case.h" +#include "absl/base/macros.h" #include "absl/container/btree_set.h" #include "absl/container/flat_hash_map.h" #include "absl/flags/declare.h" @@ -69,8 +72,8 @@ class ParsedScript { // - ASTVariableDeclaration // - ASTForInStatement using VariableCreationMap = - absl::flat_hash_map; + absl::flat_hash_map; // Mapping of argument name to zetasql Type. using ArgumentTypeMap = @@ -105,22 +108,37 @@ class ParsedScript { // All results are stored in the returned ParsedScript object. static absl::StatusOr> Create( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, + ErrorMessageOptions error_message_options, const ParsedScriptOptions& options = {}); + ABSL_DEPRECATED("Inline me!") + static absl::StatusOr> Create( + absl::string_view script_string, const ParserOptions& parser_options, + ErrorMessageMode error_message_mode, + const ParsedScriptOptions& options = {}) { + return Create(script_string, parser_options, + ErrorMessageOptions{ + .mode = error_message_mode, + .attach_error_location_payload = + (error_message_mode == ERROR_MESSAGE_WITH_PAYLOAD), + .stability = GetDefaultErrorMessageStability()}, + options); + } + // Similar to the above function, but uses an existing, externally-owned // AST instead of parsing the script. must be kept alive for // the lifetime of the returned ParsedScript. static absl::StatusOr> Create( absl::string_view script_string, const ASTScript* ast_script, - ErrorMessageMode error_message_mode, + ErrorMessageOptions error_message_options, const ParsedScriptOptions& options = {}); // Similar to above function, but also passes arguments for a routine, i.e. a // function or stored procedure whose body is a script. static absl::StatusOr> CreateForRoutine( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, ArgumentTypeMap routine_arguments, + ErrorMessageOptions error_message_options, + ArgumentTypeMap routine_arguments, const ParsedScriptOptions& options = {}); // Similar to the above functions, but allows the caller to provide an AST @@ -128,12 +146,15 @@ class ParsedScript { // a CREATE PROCEDURE statement. static absl::StatusOr> CreateForRoutine( absl::string_view script_string, const ASTScript* ast_script, - ErrorMessageMode error_message_mode, ArgumentTypeMap routine_arguments, + ErrorMessageOptions error_message_options, + ArgumentTypeMap routine_arguments, const ParsedScriptOptions& options = {}); const ASTScript* script() const { return ast_script_; } absl::string_view script_text() const { return script_string_; } - ErrorMessageMode error_message_mode() const { return error_message_mode_; } + ErrorMessageOptions error_message_options() const { + return error_message_options_; + } const ArgumentTypeMap& routine_arguments() const { return routine_arguments_; } @@ -161,7 +182,7 @@ class ParsedScript { // Returns a map of all variables in scope immediately prior to the execution // of . absl::StatusOr GetVariablesInScopeAtNode( - const ControlFlowNode * node) const; + const ControlFlowNode* node) const; // Validates the query parameters (e.g. no missing ones, not mixing named and // positional parameters). @@ -172,8 +193,9 @@ class ParsedScript { private: static absl::StatusOr> CreateInternal( absl::string_view script_string, const ParserOptions& parser_options, - ErrorMessageMode error_message_mode, ArgumentTypeMap routine_arguments, - bool is_procedure, const ParsedScriptOptions& options); + ErrorMessageOptions error_message_options, + ArgumentTypeMap routine_arguments, bool is_procedure, + const ParsedScriptOptions& options); // script_string: The string of the entire script for which parse locations // within are based off of. Owned externally. @@ -193,7 +215,7 @@ class ParsedScript { // represents a top-level script, this should be empty. ParsedScript(absl::string_view script_string, const ASTScript* ast_script, std::unique_ptr parser_output, - ErrorMessageMode error_message_mode, + ErrorMessageOptions error_message_options, ArgumentTypeMap routine_arguments, bool is_procedure); // Called from Create() to walk the parse tree and perform non-trivial work @@ -228,7 +250,7 @@ class ParsedScript { absl::string_view script_string_; // How to report error messages in GatherInformationAndRunChecks(). - ErrorMessageMode error_message_mode_; + ErrorMessageOptions error_message_options_; // Routine arguments existing from the beginning the script. ArgumentTypeMap routine_arguments_; diff --git a/zetasql/scripting/parsed_script_test.cc b/zetasql/scripting/parsed_script_test.cc index 1da574b85..f00c230b0 100644 --- a/zetasql/scripting/parsed_script_test.cc +++ b/zetasql/scripting/parsed_script_test.cc @@ -198,7 +198,7 @@ class ScriptValidationTest ParserOptions options; std::unique_ptr parsed = - ParsedScript::Create(script, options, ERROR_MESSAGE_ONE_LINE, + ParsedScript::Create(script, options, {.mode = ERROR_MESSAGE_ONE_LINE}, parsed_script_options_) .value(); ZETASQL_EXPECT_OK(parsed->CheckQueryParameters(parameters)); @@ -232,7 +232,8 @@ class ScriptValidationTest ParserOptions options; absl::StatusOr> status_or_parsed = - ParsedScript::Create(test_input.sql(), options, ERROR_MESSAGE_ONE_LINE, + ParsedScript::Create(test_input.sql(), options, + {.mode = ERROR_MESSAGE_ONE_LINE}, parsed_script_options_); absl::Status status = status_or_parsed.status(); std::unique_ptr parsed; @@ -368,6 +369,11 @@ std::vector> GetScripts() { SET @@test_system_variable = "test"; DECLARE x INT64; )")); + result.push_back(TestInput(zetasql_base::SourceLocation::current(), R"( + -- Variable declarations preceded by uppercase allowlisted system variable + SET @@TEST_SYSTEM_VARIABLE = "test"; + DECLARE x INT64; + )")); result.push_back( TestInputWithError(zetasql_base::SourceLocation::current(), "Variable declarations are allowed only at the start " diff --git a/zetasql/scripting/script_executor.cc b/zetasql/scripting/script_executor.cc index 34a71de9d..d1d1698bb 100644 --- a/zetasql/scripting/script_executor.cc +++ b/zetasql/scripting/script_executor.cc @@ -54,7 +54,7 @@ void ScriptExecutorOptions::PopulateFromAnalyzerOptions( language_options_ = analyzer_options.language(); engine_owned_system_variables_ = analyzer_options.system_variables(); query_parameters_ = GetQueryParameters(analyzer_options); - error_message_mode_ = analyzer_options.error_message_mode(); + error_message_options_ = analyzer_options.error_message_options(); } absl::StatusOr> ScriptExecutor::Create( diff --git a/zetasql/scripting/script_executor.h b/zetasql/scripting/script_executor.h index 1d1c211ab..a5f0cc3dc 100644 --- a/zetasql/scripting/script_executor.h +++ b/zetasql/scripting/script_executor.h @@ -29,6 +29,7 @@ #include "zetasql/parser/parse_tree.h" #include "zetasql/public/analyzer.h" #include "zetasql/public/analyzer_options.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/public/evaluator.h" #include "zetasql/public/evaluator_table_iterator.h" #include "zetasql/public/function_signature.h" @@ -575,7 +576,9 @@ class ScriptExecutorOptions { parsed_script_options_ = std::move(options); } - ErrorMessageMode error_message_mode() const { return error_message_mode_; } + ErrorMessageOptions error_message_options() const { + return error_message_options_; + } const MemoryLimitOptions& variable_size_limit_options() const { return variable_size_limit_options_; @@ -598,7 +601,7 @@ class ScriptExecutorOptions { SystemVariablesMap engine_owned_system_variables_; ParsedScript::QueryParameters query_parameters_; ParsedScriptOptions parsed_script_options_; - ErrorMessageMode error_message_mode_ = ERROR_MESSAGE_ONE_LINE; + ErrorMessageOptions error_message_options_{.mode = ERROR_MESSAGE_ONE_LINE}; MemoryLimitOptions variable_size_limit_options_ = MemoryLimitOptions::Unlimited(); diff --git a/zetasql/scripting/script_executor_impl.cc b/zetasql/scripting/script_executor_impl.cc index dea82ec83..98be4916e 100644 --- a/zetasql/scripting/script_executor_impl.cc +++ b/zetasql/scripting/script_executor_impl.cc @@ -123,20 +123,19 @@ absl::StatusOr> ScriptExecutorImpl::Create( ZETASQL_RET_CHECK_GE(options.maximum_stack_depth(), 1) << absl::Substitute( "Maximum stack depth must be at least 1, $0 was provided", options.maximum_stack_depth()); - ErrorMessageMode error_message_mode = options.error_message_mode(); - std::unique_ptr parsed_script; if (ast_script != nullptr) { - ZETASQL_ASSIGN_OR_RETURN(parsed_script, ParsedScript::Create( - script, ast_script, error_message_mode, - options.parsed_script_options())); + ZETASQL_ASSIGN_OR_RETURN(parsed_script, + ParsedScript::Create(script, ast_script, + options.error_message_options(), + options.parsed_script_options())); } else { ParserOptions parser_options; parser_options.set_language_options(options.language_options()); - ZETASQL_ASSIGN_OR_RETURN( - parsed_script, - ParsedScript::Create(script, parser_options, error_message_mode, - options.parsed_script_options())); + ZETASQL_ASSIGN_OR_RETURN(parsed_script, + ParsedScript::Create(script, parser_options, + options.error_message_options(), + options.parsed_script_options())); } if (!options.dry_run()) { ZETASQL_RETURN_IF_ERROR( @@ -416,12 +415,7 @@ bool ScriptExecutorImpl::IsComplete() const { absl::Status ScriptExecutorImpl::ExecuteNext() { absl::Status status = ExecuteNextImpl(); return ConvertInternalErrorLocationAndAdjustErrorString( - ErrorMessageOptions{ - .mode = options_.error_message_mode(), - .attach_error_location_payload = - options_.error_message_mode() == ERROR_MESSAGE_WITH_PAYLOAD, - .stability = ERROR_MESSAGE_STABILITY_PRODUCTION}, - CurrentScript()->script_text(), status); + options_.error_message_options(), CurrentScript()->script_text(), status); } absl::Status ScriptExecutorImpl::ExecuteNextImpl() { @@ -1435,7 +1429,7 @@ absl::Status ScriptExecutorImpl::ExecuteCallStatement() { ZETASQL_ASSIGN_OR_RETURN(std::unique_ptr parsed_script, ParsedScript::CreateForRoutine( procedure_definition->body(), GetParserOptions(), - options_.error_message_mode(), arguments_map, + options_.error_message_options(), arguments_map, options_.parsed_script_options())); const ControlFlowNode* start_node = parsed_script->control_flow_graph().start_node(); @@ -1743,7 +1737,7 @@ absl::Status ScriptExecutorImpl::ExecuteDynamicStatement() { std::make_unique(signature, sql_string); absl::StatusOr> parsed_script_or_error = ParsedScript::Create(procedure_definition->body(), GetParserOptions(), - options_.error_message_mode(), + options_.error_message_options(), options_.parsed_script_options()); if (!parsed_script_or_error.ok()) { return MakeScriptExceptionAt(execute_immediate_statement->sql()) @@ -1969,7 +1963,7 @@ absl::Status ScriptExecutorImpl::SetState( ZETASQL_ASSIGN_OR_RETURN(parsed_script, ParsedScript::CreateForRoutine( procedure_definition->body(), GetParserOptions(), - options_.error_message_mode(), arguments_map, + options_.error_message_options(), arguments_map, options_.parsed_script_options())); } else { ZETASQL_RET_CHECK_EQ(new_callstack.size(), 0) diff --git a/zetasql/testdata/BUILD b/zetasql/testdata/BUILD index f1d029d14..4a9e0b249 100644 --- a/zetasql/testdata/BUILD +++ b/zetasql/testdata/BUILD @@ -72,7 +72,6 @@ proto_library( cc_proto_library( name = "test_schema_cc_proto", - testonly = 1, deps = [":test_schema_proto"], ) @@ -113,7 +112,6 @@ java_proto_library( proto_library( name = "test_packageless_proto", - testonly = 1, srcs = [ "packageless.proto", ], @@ -122,7 +120,6 @@ proto_library( cc_proto_library( name = "test_packageless_cc_proto", - testonly = 1, deps = [":test_packageless_proto"], ) @@ -134,7 +131,6 @@ java_proto_library( proto_library( name = "ambiguous_has_proto", - testonly = 1, srcs = [ "ambiguous_has.proto", ], @@ -142,7 +138,6 @@ proto_library( cc_proto_library( name = "ambiguous_has_cc_proto", - testonly = 1, deps = [":ambiguous_has_proto"], ) @@ -157,12 +152,12 @@ cc_library( srcs = ["sample_annotation.cc"], hdrs = ["sample_annotation.h"], deps = [ - "//zetasql/common:errors", + "//zetasql/base:ret_check", + "//zetasql/base:status", "//zetasql/public:type", "//zetasql/public/types", "//zetasql/resolved_ast", "@com_google_absl//absl/status", - "@com_google_absl//absl/strings", ], ) @@ -173,10 +168,13 @@ cc_library( hdrs = ["sample_system_variables.h"], deps = [ ":test_schema_cc_proto", - "//zetasql/base:status", + "//zetasql/base:check", "//zetasql/public:analyzer", + "//zetasql/public:analyzer_options", "//zetasql/public:type", "//zetasql/public/types", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", ], ) @@ -186,6 +184,21 @@ cc_library( testonly = 1, srcs = ["sample_catalog.cc"], hdrs = ["sample_catalog.h"], + deps = [ + ":sample_catalog_impl", + "//zetasql/base:check", + "//zetasql/public:builtin_function_options", + "//zetasql/public:function", + "//zetasql/public:language_options", + "//zetasql/public:type", + "@com_google_absl//absl/status", + ], +) + +cc_library( + name = "sample_catalog_impl", + srcs = ["sample_catalog_impl.cc"], + hdrs = ["sample_catalog_impl.h"], deps = [ ":ambiguous_has_cc_proto", ":sample_annotation", @@ -198,7 +211,6 @@ cc_library( "//zetasql/base:ret_check", "//zetasql/base:source_location", "//zetasql/base:status", - "//zetasql/base/testing:status_matchers", "//zetasql/common:errors", "//zetasql/public:analyzer", "//zetasql/public:analyzer_output", @@ -230,15 +242,19 @@ cc_library( "//zetasql/resolved_ast", "//zetasql/resolved_ast:resolved_ast_enums_cc_proto", "//zetasql/resolved_ast:resolved_node_kind_cc_proto", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/base:no_destructor", "@com_google_absl//absl/container:btree", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/container:node_hash_map", + "@com_google_absl//absl/functional:function_ref", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:cord", + "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", "@com_google_absl//absl/types:span", "@com_google_protobuf//:protobuf", @@ -252,6 +268,12 @@ cc_test( ":sample_catalog", "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", + "//zetasql/public:catalog", + "//zetasql/public:function", + "//zetasql/public:function_headers", + "//zetasql/public:language_options", + "//zetasql/public:sql_view", + "//zetasql/public/types", "//zetasql/resolved_ast:resolved_ast_enums_cc_proto", "@com_google_absl//absl/strings", ], @@ -264,7 +286,6 @@ cc_library( hdrs = ["error_catalog.h"], deps = [ "//zetasql/base:ret_check", - "//zetasql/base:source_location", "//zetasql/base:status", "//zetasql/public:catalog", "@com_google_absl//absl/status", @@ -279,12 +300,12 @@ cc_library( hdrs = ["special_catalog.h"], deps = [ ":test_schema_cc_proto", + "//zetasql/base:check", "//zetasql/base:map_util", "//zetasql/public:catalog", "//zetasql/public:simple_catalog", "//zetasql/public:type", "@com_google_absl//absl/container:btree", - "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", ], @@ -300,12 +321,13 @@ cc_library( ":test_schema_cc_proto", "//zetasql/base:status", "//zetasql/public:catalog", - "//zetasql/public:language_options", "//zetasql/public:simple_catalog", "//zetasql/public:type", "//zetasql/public:value", + "//zetasql/public/types", "//zetasql/reference_impl:evaluation", "//zetasql/testing:test_value", - "@com_google_protobuf//:protobuf", + "@com_google_absl//absl/status", + "@com_google_absl//absl/time", ], ) diff --git a/zetasql/testdata/error_catalog.cc b/zetasql/testdata/error_catalog.cc index 6b8fc9d9c..d25619df4 100644 --- a/zetasql/testdata/error_catalog.cc +++ b/zetasql/testdata/error_catalog.cc @@ -19,8 +19,9 @@ #include #include +#include "zetasql/public/catalog.h" #include "absl/status/status.h" -#include "zetasql/base/source_location.h" +#include "absl/types/span.h" #include "zetasql/base/ret_check.h" #include "zetasql/base/status_builder.h" diff --git a/zetasql/testdata/modules/simple_with_pipes.sqlm b/zetasql/testdata/modules/simple_with_pipes.sqlm new file mode 100644 index 000000000..010f49947 --- /dev/null +++ b/zetasql/testdata/modules/simple_with_pipes.sqlm @@ -0,0 +1,5 @@ +module simple_module; + +CREATE PUBLIC TABLE FUNCTION MakeArray(low INT64, high INT64) AS + FROM UNNEST(GENERATE_ARRAY(low, high)) AS value + |> SELECT value; diff --git a/zetasql/testdata/populate_sample_tables.cc b/zetasql/testdata/populate_sample_tables.cc index c61358d3f..5679cb9ee 100644 --- a/zetasql/testdata/populate_sample_tables.cc +++ b/zetasql/testdata/populate_sample_tables.cc @@ -16,13 +16,15 @@ #include "zetasql/testdata/populate_sample_tables.h" -#include "google/protobuf/descriptor.h" -#include "zetasql/public/catalog.h" -#include "zetasql/public/language_options.h" #include "zetasql/public/simple_catalog.h" +#include "zetasql/public/types/type.h" +#include "zetasql/public/types/type_factory.h" #include "zetasql/public/value.h" +#include "zetasql/testdata/sample_catalog.h" #include "zetasql/testdata/test_schema.pb.h" #include "zetasql/testing/test_value.h" +#include "absl/status/status.h" +#include "absl/time/civil_time.h" #include "zetasql/base/status_macros.h" namespace zetasql { diff --git a/zetasql/testdata/populate_sample_tables.h b/zetasql/testdata/populate_sample_tables.h index af47d24b5..19bcfd0dc 100644 --- a/zetasql/testdata/populate_sample_tables.h +++ b/zetasql/testdata/populate_sample_tables.h @@ -21,6 +21,7 @@ #include "zetasql/public/type.h" #include "zetasql/reference_impl/evaluation.h" #include "zetasql/testdata/sample_catalog.h" +#include "absl/status/status.h" #include "zetasql/base/status.h" namespace zetasql { diff --git a/zetasql/testdata/sample_annotation.cc b/zetasql/testdata/sample_annotation.cc index ed0de53c6..1aa6f834d 100644 --- a/zetasql/testdata/sample_annotation.cc +++ b/zetasql/testdata/sample_annotation.cc @@ -16,10 +16,12 @@ #include "zetasql/testdata/sample_annotation.h" -#include "zetasql/common/errors.h" +#include "zetasql/public/types/annotation.h" +#include "zetasql/public/types/simple_value.h" #include "zetasql/resolved_ast/resolved_ast.h" #include "absl/status/status.h" -#include "absl/strings/substitute.h" +#include "zetasql/base/ret_check.h" +#include "zetasql/base/status_macros.h" // TODO: Extracts helper functions and interfaces from this class. namespace zetasql { diff --git a/zetasql/testdata/sample_catalog.cc b/zetasql/testdata/sample_catalog.cc index c644a1000..9e8bd3a29 100644 --- a/zetasql/testdata/sample_catalog.cc +++ b/zetasql/testdata/sample_catalog.cc @@ -16,8579 +16,37 @@ #include "zetasql/testdata/sample_catalog.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#include "zetasql/base/logging.h" -#include "zetasql/common/errors.h" -#include "zetasql/public/analyzer.h" -#include "zetasql/public/analyzer_output.h" -#include "zetasql/public/annotation/collation.h" -#include "zetasql/public/anon_function.h" -#include "zetasql/public/builtin_function.pb.h" #include "zetasql/public/builtin_function_options.h" -#include "zetasql/public/catalog.h" -#include "zetasql/public/cycle_detector.h" -#include "zetasql/public/deprecation_warning.pb.h" -#include "zetasql/public/error_location.pb.h" -#include "zetasql/public/evaluator_table_iterator.h" -#include "zetasql/public/function.h" -#include "zetasql/public/function.pb.h" -#include "zetasql/public/function_signature.h" -#include "zetasql/public/options.pb.h" -#include "zetasql/public/parse_resume_location.h" -#include "zetasql/public/procedure.h" -#include "zetasql/public/simple_catalog.h" -#include "zetasql/public/simple_catalog_util.h" -#include "zetasql/public/sql_function.h" -#include "zetasql/public/sql_tvf.h" -#include "zetasql/public/strings.h" -#include "zetasql/public/table_valued_function.h" -#include "zetasql/public/templated_sql_function.h" -#include "zetasql/public/templated_sql_tvf.h" -#include "zetasql/public/type.pb.h" -#include "zetasql/public/types/annotation.h" -#include "zetasql/public/types/simple_value.h" -#include "zetasql/public/types/type.h" -#include "zetasql/public/types/type_factory.h" -#include "zetasql/public/value.h" -#include "zetasql/resolved_ast/resolved_ast.h" -#include "zetasql/resolved_ast/resolved_ast_enums.pb.h" -#include "zetasql/resolved_ast/resolved_node_kind.pb.h" -#include "zetasql/testdata/ambiguous_has.pb.h" -#include "zetasql/testdata/referenced_schema.pb.h" -#include "zetasql/testdata/sample_annotation.h" -#include "zetasql/testdata/test_proto3.pb.h" -#include "absl/container/btree_map.h" -#include "zetasql/base/testing/status_matchers.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" +#include "zetasql/public/language_options.h" +#include "zetasql/testdata/sample_catalog_impl.h" #include "zetasql/base/check.h" -#include "absl/memory/memory.h" -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/ascii.h" -#include "absl/strings/cord.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "zetasql/base/source_location.h" -#include "absl/types/span.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/descriptor_database.h" -#include "google/protobuf/message.h" -#include "zetasql/base/map_util.h" -#include "zetasql/base/ret_check.h" -#include "zetasql/base/status_builder.h" -#include "zetasql/base/status_macros.h" namespace zetasql { -namespace { - -// A fluent builder for building FunctionArgumentType instances -class ArgBuilder { - public: - explicit ArgBuilder() = default; - ArgBuilder&& Type(const Type* type) && { - kind_ = ARG_TYPE_FIXED; - type_ = type; - return std::move(*this); - } - // Convenience wrappers for some common Type choices. - // This should _not_ be exhaustive, prefer Type(...) over adding multitudes - // of these wrappers - ArgBuilder&& String() && { - return std::move(*this).Type(types::StringType()); - } - ArgBuilder&& Int64() && { return std::move(*this).Type(types::Int64Type()); } - ArgBuilder&& Bool() && { return std::move(*this).Type(types::BoolType()); } - ArgBuilder&& T1() && { - kind_ = ARG_TYPE_ANY_1; - return std::move(*this); - } - ArgBuilder&& T2() && { - kind_ = ARG_TYPE_ANY_2; - return std::move(*this); - } - ArgBuilder&& Any() && { - kind_ = ARG_TYPE_ARBITRARY; - return std::move(*this); - } - ArgBuilder&& Repeated() && { - options_.set_cardinality(FunctionEnums::REPEATED); - return std::move(*this); - } - ArgBuilder&& Optional() && { - options_.set_cardinality(FunctionEnums::OPTIONAL); - return std::move(*this); - } - // Implies optional. - ArgBuilder&& Default(Value value) && { - options_.set_default(value); - options_.set_cardinality(FunctionEnums::OPTIONAL); - return std::move(*this); - } - ArgBuilder&& Name(absl::string_view name) && { - options_.set_argument_name(name, zetasql::kPositionalOrNamed); - return std::move(*this); - } - ArgBuilder&& NameOnly(absl::string_view name) && { - options_.set_argument_name(name, zetasql::kNamedOnly); - return std::move(*this); - } - - FunctionArgumentType Build() && { - ABSL_CHECK(kind_.has_value()); - if (type_ != nullptr) { - ABSL_CHECK(kind_ = ARG_TYPE_FIXED); - return FunctionArgumentType(type_, options_); - } else { - return FunctionArgumentType(*kind_, options_); - } - } - - private: - FunctionArgumentTypeOptions options_; - std::optional kind_; - const ::zetasql::Type* type_ = nullptr; -}; - -// A fluent class for constructing FuntionSignature objects. -class SignatureBuilder { - public: - explicit SignatureBuilder( - zetasql_base::SourceLocation loc = zetasql_base::SourceLocation::current()) - : loc_(loc) {} - - SignatureBuilder&& AddArg(FunctionArgumentType t) && { - args_.push_back(std::move(t)); - return std::move(*this); - } - SignatureBuilder&& AddArg(ArgBuilder&& t) && { - return std::move(*this).AddArg(std::move(t).Build()); - } - - // By default, result type is string (many tests don't care about the return - // type). - SignatureBuilder&& Returns(FunctionArgumentType t) && { - ret_type_ = std::move(t); - return std::move(*this); - } - SignatureBuilder&& Returns(ArgBuilder&& t) && { - return std::move(*this).Returns(std::move(t).Build()); - } - - // Sets context_id to line number, which can speed up debugging. - FunctionSignature Build() && { - return FunctionSignature(ret_type_, std::move(args_), - /*context_id=*/loc_.line()); - } - - private: - FunctionArgumentTypeList args_; - // Default to return string. - FunctionArgumentType ret_type_ = {types::StringType()}; - zetasql_base::SourceLocation loc_; -}; - -} // namespace - -SampleCatalog::SampleCatalog() - : internal_type_factory_(new TypeFactory), - types_(internal_type_factory_.get()) { - catalog_ = std::make_unique("sample_catalog", types_); - LoadCatalog(LanguageOptions()); +SampleCatalog::SampleCatalog() : SampleCatalogImpl() { + ZETASQL_CHECK_OK(SampleCatalogImpl::LoadCatalogImpl( + ZetaSQLBuiltinFunctionOptions(LanguageOptions()))); } +// Constructor given 'language_options' and an optional 'type_factory'. +// If 'type_factory' is specified then it must outlive this SampleCatalog +// and this SampleCatalog does not take ownership of it. If 'type_factory' +// is not specified then a locally owned TypeFactory is created and +// used instead. SampleCatalog::SampleCatalog(const LanguageOptions& language_options, TypeFactory* type_factory) - : SampleCatalog(ZetaSQLBuiltinFunctionOptions(language_options), - type_factory) {} + : SampleCatalog(BuiltinFunctionOptions(language_options), type_factory) {} +// Constructor given 'builtin_function_options' and optional 'type_factory'. +// If 'type_factory' is specified then it must outlive this SampleCatalog +// and this SampleCatalog does not take ownership of it. If 'type_factory' +// is not specified then a locally owned TypeFactory is created and +// used instead. SampleCatalog::SampleCatalog( - const ZetaSQLBuiltinFunctionOptions& builtin_function_options, - TypeFactory* type_factory) { - if (type_factory == nullptr) { - internal_type_factory_ = std::make_unique(); - types_ = internal_type_factory_.get(); - } else { - types_ = type_factory; - } - catalog_ = std::make_unique("sample_catalog", types_); - LoadCatalogBuiltins(builtin_function_options); - LoadCatalogImpl(builtin_function_options.language_options); -} - -SampleCatalog::~SampleCatalog() = default; - -SimpleTable* SampleCatalog::GetTableOrDie(absl::string_view name) { - return zetasql_base::FindOrDie(tables_, name); -} - -absl::StatusOr SampleCatalog::GetTable(absl::string_view name) { - SimpleTable** table = zetasql_base::FindOrNull(tables_, name); - if (table != nullptr) { - return *table; - } else { - return zetasql_base::NotFoundErrorBuilder() - << "SampleCatalog: Table " << name << " not found"; - } -} - -const ProtoType* SampleCatalog::GetProtoType( - const google::protobuf::Descriptor* descriptor) { - const Type* type; - ZETASQL_CHECK_OK(catalog_->FindType({descriptor->full_name()}, &type)); - ABSL_CHECK(type != nullptr); - ABSL_CHECK(type->IsProto()); - return type->AsProto(); -} - -const EnumType* SampleCatalog::GetEnumType( - const google::protobuf::EnumDescriptor* descriptor) { - const Type* type; - ZETASQL_CHECK_OK(catalog_->FindType({descriptor->full_name()}, &type)); - ABSL_CHECK(type != nullptr); - ABSL_CHECK(type->IsEnum()); - return type->AsEnum(); -} - -static absl::StatusOr ComputeResultTypeCallbackForNullOfType( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - ZETASQL_RET_CHECK_EQ(arguments.size(), 1); - ZETASQL_RET_CHECK_EQ(signature.NumConcreteArguments(), arguments.size()); - const LanguageOptions& language_options = analyzer_options.language(); - if (!arguments[0].is_literal() || arguments[0].is_literal_null()) { - return MakeSqlError() - << "Argument to NULL_OF_TYPE must be a literal string"; - } - ZETASQL_RET_CHECK(arguments[0].type()->IsString()); - const Value& value = *arguments[0].literal_value(); - ZETASQL_RET_CHECK(!value.is_null()); - const absl::string_view type_name = value.string_value(); - const TypeKind type_kind = - Type::ResolveBuiltinTypeNameToKindIfSimple(type_name, language_options); - if (type_kind == TYPE_UNKNOWN) { - return MakeSqlError() << "Type not implemented for NULL_OF_TYPE: " - << ToStringLiteral(absl::AsciiStrToUpper(type_name)); - } - // We could parse complex type names here too by calling the type analyzer. - return type_factory->MakeSimpleType(type_kind); -} - -// A ComputeResultTypeCallback that looks for the 'type_name' argument from the -// input list and uses its value to generate the result type. -static absl::StatusOr ComputeResultTypeFromStringArgumentValue( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - ZETASQL_RET_CHECK_EQ(signature.NumConcreteArguments(), arguments.size()); - const LanguageOptions& language_options = analyzer_options.language(); - std::string type_name; - for (int i = 0; i < arguments.size(); ++i) { - if (!signature.ConcreteArgument(i).has_argument_name() || - signature.ConcreteArgument(i).argument_name() != "type_name") { - continue; - } - - const InputArgumentType& arg = arguments[i]; - ZETASQL_RET_CHECK(arg.type()->IsString()); - ZETASQL_RET_CHECK(arg.is_literal()); - if (arg.is_literal_null()) { - return MakeSqlError() << "Argument 'type_name' cannot be NULL"; - } - - const Value& value = *arg.literal_value(); - ZETASQL_RET_CHECK(!value.is_null()); - type_name = value.string_value(); - const TypeKind type_kind = - Type::ResolveBuiltinTypeNameToKindIfSimple(type_name, language_options); - if (type_kind != TYPE_UNKNOWN) { - return type_factory->MakeSimpleType(type_kind); - } - // Try to find the type in catalog. - const Type* type = nullptr; - std::vector path; - ZETASQL_RETURN_IF_ERROR(ParseIdentifierPath(type_name, LanguageOptions(), &path)); - if (catalog->FindType(path, &type).ok()) { - return type; - } - break; - } - if (!type_name.empty()) { - return MakeSqlError() << "Invalid type name provided: " << type_name; - } - // Use INT64 as return type if not overridden. - return type_factory->get_int64(); -} - -static absl::StatusOr ComputeResultTypeCallbackToStruct( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - const StructType* struct_type; - std::vector struct_fields; - struct_fields.reserve(arguments.size()); - for (int i = 0; i < arguments.size(); ++i) { - struct_fields.push_back({absl::StrCat("field", i), arguments[i].type()}); - } - ZETASQL_CHECK_OK( - type_factory->MakeStructType(std::move(struct_fields), &struct_type)); - return struct_type; -} - -// Similar to `ComputeResultTypeCallbackToStruct`, but use the argument aliases -// in `arguments`, if specified, as the struct field names. -static absl::StatusOr -ComputeResultTypeCallbackToStructUseArgumentAliases( - Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, - const FunctionSignature& signature, - absl::Span arguments, - const AnalyzerOptions& analyzer_options) { - const StructType* struct_type; - std::vector struct_fields; - struct_fields.reserve(arguments.size()); - for (int i = 0; i < arguments.size(); ++i) { - std::string field_name; - if (arguments[i].argument_alias().has_value()) { - field_name = arguments[i].argument_alias()->ToString(); - } else { - field_name = absl::StrCat("no_alias_", i); - } - struct_fields.push_back({field_name, arguments[i].type()}); - } - ZETASQL_CHECK_OK(type_factory->MakeStructType(struct_fields, &struct_type)); - return struct_type; -} - -// f(collation_1, ..., collation_n) -> STRUCT. -// If the return value is nullptr it means no collations. -static absl::StatusOr -ComputeResultAnnotationsCallbackToStruct(const AnnotationCallbackArgs& args, - TypeFactory& type_factory) { - const Type* result_type = args.result_type; - const std::vector& arguments = - args.argument_annotations; - // Can only handle struct type, where each argument becomes a field of the - // struct. - ZETASQL_RET_CHECK(result_type->IsStruct()); - ZETASQL_RET_CHECK_EQ(result_type->AsStruct()->num_fields(), arguments.size()); - - std::unique_ptr annotation_map = - AnnotationMap::Create(result_type); - ZETASQL_RET_CHECK(annotation_map->IsStructMap()); - StructAnnotationMap* struct_annotation_map = annotation_map->AsStructMap(); - ZETASQL_RET_CHECK_EQ(struct_annotation_map->num_fields(), arguments.size()); - - // We only check collation because currently the only supported annotation for - // functions is collation. - const int collation_annotation_id = CollationAnnotation::GetId(); - - // The annotation of each input argument is added to the corresponding - // struct field. - for (int i = 0; i < arguments.size(); ++i) { - if (arguments[i] == nullptr || - arguments[i]->GetAnnotation(collation_annotation_id) == nullptr) { - continue; - } - struct_annotation_map->mutable_field(i)->SetAnnotation( - collation_annotation_id, - *arguments[i]->GetAnnotation(collation_annotation_id)); - } - if (annotation_map->Empty()) { - // Use nullptr rather than an empty annotation map when there are no - // annotations. - return nullptr; - } - return type_factory.TakeOwnership(std::move(annotation_map)); -} - -// f(ARRAY, ..., ARRAY) -> ARRAY>. -// If the return value is nullptr it means no collations. -static absl::StatusOr -ComputeResultAnnotationsCallbackArraysToArrayOfStruct( - const AnnotationCallbackArgs& args, TypeFactory& type_factory) { - const Type* result_type = args.result_type; - const std::vector& arguments = - args.argument_annotations; - - // The return type must be ARRAY. - ZETASQL_RET_CHECK(result_type->IsArray()); - ZETASQL_RET_CHECK(result_type->AsArray()->element_type()->IsStruct()); - ZETASQL_RET_CHECK_EQ(result_type->AsArray()->element_type()->AsStruct()->num_fields(), - arguments.size()); - - // We only check collation because currently the only supported annotation for - // functions is collation. - const int collation_annotation_id = CollationAnnotation::GetId(); - - // Validations on the annotations of the input arguments. - for (const AnnotationMap* argument : arguments) { - if (argument == nullptr) { - continue; - } - ZETASQL_RET_CHECK(argument->IsArrayMap()); - // Only the array elements may have collations, not the array themselves. - ZETASQL_RET_CHECK_EQ(argument->AsArrayMap()->GetAnnotation(collation_annotation_id), - nullptr); - } - - std::unique_ptr annotation_map = - AnnotationMap::Create(result_type); - ZETASQL_RET_CHECK(annotation_map->IsArrayMap()); - ZETASQL_RET_CHECK(annotation_map->AsArrayMap()->element()->IsStructMap()); - - ArrayAnnotationMap* array_annotation_map = annotation_map->AsArrayMap(); - StructAnnotationMap* element_struct_annotation_map = - array_annotation_map->mutable_element()->AsStructMap(); - ZETASQL_RET_CHECK_EQ(element_struct_annotation_map->num_fields(), arguments.size()); - - // Propagate the array element collations to the elements of the result array. - for (int i = 0; i < arguments.size(); ++i) { - if (arguments[i] == nullptr) { - continue; - } - const AnnotationMap* argument_element_annotation_map = - arguments[i]->AsArrayMap()->element(); - if (argument_element_annotation_map == nullptr || - argument_element_annotation_map->GetAnnotation( - collation_annotation_id) == nullptr) { - continue; - } - element_struct_annotation_map->mutable_field(i)->SetAnnotation( - collation_annotation_id, - *argument_element_annotation_map->GetAnnotation( - collation_annotation_id)); - } - if (annotation_map->Empty()) { - // Use nullptr rather than an empty annotation map when there are no - // annotations. - return nullptr; - } - return type_factory.TakeOwnership(std::move(annotation_map)); -} - -// f(annotation_1, ..., annotation_n) -> annotation_n. -static absl::StatusOr -ComputeResultAnnotationsCallbackUseTheFinalAnnotation( - const AnnotationCallbackArgs& args, TypeFactory& type_factory) { - const Type* result_type = args.result_type; - ZETASQL_RET_CHECK_EQ(result_type, type_factory.get_string()); - const std::vector& arguments = - args.argument_annotations; - std::unique_ptr annotation_map = - AnnotationMap::Create(result_type); - // We only check collation because currently the only supported annotation for - // functions is collation. - const int collation_annotation_id = CollationAnnotation::GetId(); - for (int i = 0; i < arguments.size(); ++i) { - if (arguments[i] == nullptr) { - annotation_map->UnsetAnnotation(collation_annotation_id); - } else { - annotation_map->SetAnnotation( - collation_annotation_id, - *arguments[i]->GetAnnotation(collation_annotation_id)); - } - } - if (annotation_map->Empty()) { - return nullptr; - } - return type_factory.TakeOwnership(std::move(annotation_map)); -} - -// f(collation_1, ..., collation_n) -> SQL error. -// This is to verify that the thrown error is used as why a function call is -// invalid. -static absl::StatusOr -ComputeResultAnnotationsCallbackSqlError(const AnnotationCallbackArgs& args, - TypeFactory& type_factory) { - for (const AnnotationMap* argument : args.argument_annotations) { - if (argument != nullptr && !argument->Empty()) { - return MakeSqlError() << "Arguments should not have annotations"; - } - } - return nullptr; -} - -void SampleCatalog::LoadCatalog(const LanguageOptions& language_options) { - // We split these up because these methods (particularly loading builtins) - // use too much stack and may cause overflows. - LoadCatalogBuiltins(language_options); - LoadCatalogImpl(language_options); -} - -ZetaSQLBuiltinFunctionOptions SampleCatalog::LoadDefaultSuppliedTypes( - const ZetaSQLBuiltinFunctionOptions& options) { - ZetaSQLBuiltinFunctionOptions options_copy = options; - const ProtoType* approx_distance_function_options_proto_type; - ZETASQL_CHECK_OK(types_->MakeProtoType( - zetasql_test__::TestApproxDistanceFunctionOptionsProto::GetDescriptor(), - &approx_distance_function_options_proto_type)); - absl::flat_hash_set approx_distance_function_ids = { - FN_APPROX_COSINE_DISTANCE_FLOAT_WITH_PROTO_OPTIONS, - FN_APPROX_COSINE_DISTANCE_DOUBLE_WITH_PROTO_OPTIONS, - FN_APPROX_EUCLIDEAN_DISTANCE_FLOAT_WITH_PROTO_OPTIONS, - FN_APPROX_EUCLIDEAN_DISTANCE_DOUBLE_WITH_PROTO_OPTIONS, - FN_APPROX_DOT_PRODUCT_INT64_WITH_PROTO_OPTIONS, - FN_APPROX_DOT_PRODUCT_FLOAT_WITH_PROTO_OPTIONS, - FN_APPROX_DOT_PRODUCT_DOUBLE_WITH_PROTO_OPTIONS}; - // Note that only argument index `2` is specified because only the third - // argument in approximate distance functions supports supplied argument - // types. - for (const auto& id : approx_distance_function_ids) { - std::pair id_idx_pair = {id, 2}; - options_copy.argument_types[id_idx_pair] = - approx_distance_function_options_proto_type; - } - return options_copy; -} - -void SampleCatalog::LoadCatalogBuiltins( - const LanguageOptions& language_options) { - // Populate the sample catalog with the ZetaSQL functions using the - // specified LanguageOptions. - LoadCatalogBuiltins(ZetaSQLBuiltinFunctionOptions(language_options)); -} - -void SampleCatalog::LoadCatalogBuiltins( - const ZetaSQLBuiltinFunctionOptions& builtin_function_options) { - // Populate the sample catalog with the ZetaSQL functions using the - // specified ZetaSQLBuiltinFunctionOptions. - ZETASQL_CHECK_OK(catalog_->AddBuiltinFunctionsAndTypes( - LoadDefaultSuppliedTypes(builtin_function_options))); -} - -void SampleCatalog::LoadCatalogImpl(const LanguageOptions& language_options) { - // Make all proto Descriptors linked into this binary available. - catalog_->SetDescriptorPool(google::protobuf::DescriptorPool::generated_pool()); - - // Create a Catalog called alt_descriptor_pool which has a duplicate copy - // of all protos in the main catalog, but in a different DescriptorPool. - alt_descriptor_database_ = std::make_unique( - *google::protobuf::DescriptorPool::generated_pool()); - alt_descriptor_pool_ = - std::make_unique(alt_descriptor_database_.get()); - - SimpleCatalog* alt_descriptor_pool_catalog = - catalog_->MakeOwnedSimpleCatalog("alt_descriptor_pool"); - alt_descriptor_pool_catalog->SetDescriptorPool(alt_descriptor_pool_.get()); - - // Create a Catalog called ambiguous_has_descriptor_pool which has the - // (modified) AmbiguousHasPB proto, obtained by changing the - // "confusing_name_to_be_rewritten" field's name changed to "confusing_name". - // This makes it such that the modified AmbiguousHasPB has a field called - // "has_confusing_name" and "confusing_name". Such proto descriptors can occur - // in practice, but not when C++ code is generated, hence this hack. - google::protobuf::FileDescriptorProto modified_descriptor_proto; - zetasql_test__::AmbiguousHasPB::descriptor()->file()->CopyTo( - &modified_descriptor_proto); - bool found_message = false; - for (google::protobuf::DescriptorProto& message_descriptor_proto : - *modified_descriptor_proto.mutable_message_type()) { - if (message_descriptor_proto.name() == "AmbiguousHasPB") { - found_message = true; - bool found_field = false; - for (google::protobuf::FieldDescriptorProto& field_descriptor_proto : - *message_descriptor_proto.mutable_field()) { - if (field_descriptor_proto.name() == "confusing_name_to_be_rewritten") { - found_field = true; - field_descriptor_proto.set_name("confusing_name"); - break; - } - } - ABSL_CHECK(found_field) << message_descriptor_proto; - } - } - ABSL_CHECK(found_message) << modified_descriptor_proto; - ambiguous_has_descriptor_pool_ = std::make_unique(); - ambiguous_has_descriptor_pool_->BuildFile(modified_descriptor_proto); - - auto ambiguous_has_descriptor_pool_catalog = - std::make_unique("ambiguous_has_descriptor_pool"); - ambiguous_has_descriptor_pool_catalog->SetDescriptorPool( - ambiguous_has_descriptor_pool_.get()); - catalog_->AddOwnedCatalog(ambiguous_has_descriptor_pool_catalog.release()); - - // Add various kinds of objects to the catalog(s). - LoadTypes(); - LoadTables(); - LoadConnections(); - LoadSequences(); - LoadProtoTables(); - LoadViews(language_options); - LoadNestedCatalogs(); - LoadFunctions(); - LoadFunctions2(); - LoadExtendedSubscriptFunctions(); - LoadFunctionsWithDefaultArguments(); - LoadFunctionsWithStructArgs(); - LoadTemplatedSQLUDFs(); - LoadTableValuedFunctions1(); - LoadTableValuedFunctions2(); - LoadTableValuedFunctionsWithEvaluators(); - LoadTVFWithExtraColumns(); - LoadDescriptorTableValuedFunctions(); - LoadConnectionTableValuedFunctions(); - LoadTableValuedFunctionsWithDeprecationWarnings(); - LoadTemplatedSQLTableValuedFunctions(); - LoadTableValuedFunctionsWithAnonymizationUid(); - LoadProcedures(); - LoadConstants(); - LoadWellKnownLambdaArgFunctions(); - LoadContrivedLambdaArgFunctions(); - LoadSqlFunctions(language_options); - LoadNonTemplatedSqlTableValuedFunctions(language_options); -} - -void SampleCatalog::LoadTypes() { - enum_TestEnum_ = GetEnumType(zetasql_test__::TestEnum_descriptor()); - enum_AnotherTestEnum_ = - GetEnumType(zetasql_test__::AnotherTestEnum_descriptor()); - enum_TestEnumWithAnnotations_ = - GetEnumType(zetasql_test__::TestEnumWithAnnotations_descriptor()); - proto_KitchenSinkPB_ = - GetProtoType(zetasql_test__::KitchenSinkPB::descriptor()); - proto_MessageWithKitchenSinkPB_ = - GetProtoType(zetasql_test__::MessageWithKitchenSinkPB::descriptor()); - proto_CivilTimeTypesSinkPB_ = - GetProtoType(zetasql_test__::CivilTimeTypesSinkPB::descriptor()); - proto_TestExtraPB_ = GetProtoType(zetasql_test__::TestExtraPB::descriptor()); - proto_abPB_ = GetProtoType(zetasql_test__::TestAbPB::descriptor()); - proto_bcPB_ = GetProtoType(zetasql_test__::TestBcPB::descriptor()); - proto_EmptyMessage_ = - GetProtoType(zetasql_test__::EmptyMessage::descriptor()); - proto3_KitchenSinkPB_ = - GetProtoType(zetasql_test__::Proto3KitchenSink::descriptor()); - proto3_MessageWithInvalidMap_ = - GetProtoType(zetasql_test__::MessageWithInvalidMap::descriptor()); - proto_field_formats_proto_ = - GetProtoType(zetasql_test__::FieldFormatsProto::descriptor()); - proto_MessageWithMapField_ = - GetProtoType(zetasql_test__::MessageWithMapField::descriptor()); - proto_approx_distance_function_options_ = GetProtoType( - zetasql_test__::TestApproxDistanceFunctionOptionsProto::descriptor()); - - // We want to pull AmbiguousHasPB from the descriptor pool where it was - // modified, not the generated pool. - const google::protobuf::Descriptor* ambiguous_has_descriptor = - ambiguous_has_descriptor_pool_->FindMessageTypeByName( - "zetasql_test__.AmbiguousHasPB"); - ABSL_CHECK(ambiguous_has_descriptor); - ZETASQL_CHECK_OK( - types_->MakeProtoType(ambiguous_has_descriptor, &proto_ambiguous_has_)); - - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_int32()}, {"b", types_->get_string()}}, - &struct_type_)); - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"c", types_->get_int32()}, {"d", struct_type_}}, &nested_struct_type_)); - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"e", types_->get_int32()}, {"f", nested_struct_type_}}, - &doubly_nested_struct_type_)); - - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_int32(), &int32array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_int64(), &int64array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_uint32(), &uint32array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_uint64(), &uint64array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_bytes(), &bytes_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_bool(), &bool_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_float(), &float_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_double(), &double_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_date(), &date_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_string(), &string_array_type_)); - ZETASQL_CHECK_OK( - types_->MakeArrayType(types_->get_timestamp(), ×tamp_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(proto_TestExtraPB_, &proto_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(struct_type_, &struct_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_json(), &json_array_type_)); - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_numeric(), &numeric_array_type_)); - ZETASQL_CHECK_OK( - types_->MakeArrayType(types_->get_bignumeric(), &bignumeric_array_type_)); - ZETASQL_CHECK_OK( - types_->MakeArrayType(types_->get_interval(), &interval_array_type_)); - - ZETASQL_ASSERT_OK_AND_ASSIGN( - int32map_type_, - types_->MakeMapType(types_->get_int32(), types_->get_int32())); - ZETASQL_ASSERT_OK_AND_ASSIGN( - int64map_type_, - types_->MakeMapType(types_->get_int64(), types_->get_int64())); - ZETASQL_ASSERT_OK_AND_ASSIGN( - bytesmap_type_, - types_->MakeMapType(types_->get_bytes(), types_->get_bytes())); - - ZETASQL_CHECK_OK(types_->MakeStructType({{"x", types_->get_int64()}, - {"y", struct_type_}, - {"z", struct_array_type_}}, - &struct_with_array_field_type_)); - - ZETASQL_CHECK_OK(types_->MakeStructType({{"x", types_->get_int64()}}, - &struct_with_one_field_type_)); - - const StructType* struct_with_just_kitchen_sink_type; - ZETASQL_CHECK_OK(types_->MakeStructType({{"kitchen_sink", proto_KitchenSinkPB_}}, - &struct_with_just_kitchen_sink_type)); - ZETASQL_CHECK_OK(types_->MakeStructType({{"kitchen_sink", proto_KitchenSinkPB_}, - {"s", struct_with_just_kitchen_sink_type}}, - &struct_with_kitchen_sink_type_)); - const ArrayType* array_of_struct_with_kitchen_sink_type; - ZETASQL_CHECK_OK(types_->MakeArrayType(struct_with_just_kitchen_sink_type, - &array_of_struct_with_kitchen_sink_type)); - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_int64()}, - {"b", array_of_struct_with_kitchen_sink_type}}, - &struct_of_array_of_struct_with_kitchen_sink_type_)); - - // Add a named struct type for testing name collisions. - const StructType* name_conflict_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}, - &name_conflict_type)); - catalog_->AddType("NameConflictType", name_conflict_type); - - // Add a simple type for testing alias type from engine catalog - catalog_->AddType("INT64AliasType", types_->get_int64()); -} - -namespace { - -// Implementation of EvaluatorTableIterator which ignores SetReadTime(), but -// delegates all other methods to an underlying iterator passed to the -// constructor. -class IgnoreReadTimeIterator : public EvaluatorTableIterator { - public: - explicit IgnoreReadTimeIterator( - std::unique_ptr iterator) - : iterator_(std::move(iterator)) {} - - int NumColumns() const override { return iterator_->NumColumns(); } - std::string GetColumnName(int i) const override { - return iterator_->GetColumnName(i); - } - const Type* GetColumnType(int i) const override { - return iterator_->GetColumnType(i); - } - absl::Status SetColumnFilterMap( - absl::flat_hash_map> filter_map) - override { - return iterator_->SetColumnFilterMap(std::move(filter_map)); - } - absl::Status SetReadTime(absl::Time read_time) override { - return absl::OkStatus(); - } - bool NextRow() override { return iterator_->NextRow(); } - const Value& GetValue(int i) const override { return iterator_->GetValue(i); } - absl::Status Status() const override { return iterator_->Status(); } - absl::Status Cancel() override { return iterator_->Cancel(); } - void SetDeadline(absl::Time deadline) override { - iterator_->SetDeadline(deadline); - } - - private: - std::unique_ptr iterator_; -}; - -// Minimal table implementation to support testing of FOR SYSTEM TIME AS OF. -// This is just a modified version of SimpleTable which ignores the read time. -class SimpleTableWithReadTimeIgnored : public SimpleTable { - public: - SimpleTableWithReadTimeIgnored(absl::string_view name, - const std::vector& columns, - const int64_t id = 0) - : SimpleTable(name, columns, id) {} - - absl::StatusOr> - CreateEvaluatorTableIterator( - absl::Span column_idxs) const override { - std::unique_ptr iterator; - ZETASQL_ASSIGN_OR_RETURN(iterator, - SimpleTable::CreateEvaluatorTableIterator(column_idxs)); - return std::make_unique(std::move(iterator)); - } -}; - -} // namespace - -void SampleCatalog::AddGeneratedColumnToTable( - std::string column_name, std::vector expression_columns, - std::string generated_expr, SimpleTable* table) { - AnalyzerOptions options; - options.mutable_language()->SetSupportsAllStatementKinds(); - std::unique_ptr output; - for (const std::string& expression_column : expression_columns) { - ZETASQL_ASSERT_OK( - options.AddExpressionColumn(expression_column, types::Int64Type())); - } - ZETASQL_ASSERT_OK(AnalyzeExpression(generated_expr, options, catalog_.get(), - catalog_->type_factory(), &output)); - SimpleColumn::ExpressionAttributes expr_attributes( - SimpleColumn::ExpressionAttributes::ExpressionKind::GENERATED, - generated_expr, output->resolved_expr()); - ZETASQL_ASSERT_OK(table->AddColumn( - new SimpleColumn(table->Name(), column_name, types::Int64Type(), - {.column_expression = expr_attributes}), - /*is_owned=*/true)); - sql_object_artifacts_.push_back(std::move(output)); -} - -void SampleCatalog::LoadTables() { - SimpleTable* value_table = new SimpleTable( - "Value", {{"Value", types_->get_int64()}, - // to test while() loop in SQLBuilder::GetScanAlias - {"Value_1", types_->get_int64()}}); - AddOwnedTable(value_table); - - SimpleTable* key_value_table = new SimpleTable( - "KeyValue", - {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}); - key_value_table->SetContents({{Value::Int64(1), Value::String("a")}, - {Value::Int64(2), Value::String("b")}}); - - AddOwnedTable(key_value_table); - key_value_table_ = key_value_table; - - SimpleTable* multiple_columns_table = - new SimpleTable("MultipleColumns", {{"int_a", types_->get_int64()}, - {"string_a", types_->get_string()}, - {"int_b", types_->get_int64()}, - {"string_b", types_->get_string()}, - {"int_c", types_->get_int64()}, - {"int_d", types_->get_int64()}}); - AddOwnedTable(multiple_columns_table); - - SimpleTable* update_to_default_table = new SimpleTable( - "UpdateToDefaultTable", {{"writable", types_->get_int64()}}); - ZETASQL_CHECK_OK(update_to_default_table->AddColumn( - new SimpleColumn(update_to_default_table->Name(), "readonly", - types_->get_int64(), {.is_writable_column = false}), - /*is_owned=*/true)); - ZETASQL_CHECK_OK(update_to_default_table->AddColumn( - new SimpleColumn(update_to_default_table->Name(), - "readonly_settable_to_default", types_->get_int64(), - {.is_writable_column = false, - .can_update_unwritable_to_default = true}), - /*is_owned=*/true)); - AddOwnedTable(update_to_default_table); - - SimpleTable* ab_table = new SimpleTable( - "abTable", {{"a", types_->get_int64()}, {"b", types_->get_string()}}); - AddOwnedTable(ab_table); - SimpleTable* bc_table = new SimpleTable( - "bcTable", {{"b", types_->get_int64()}, {"c", types_->get_string()}}); - AddOwnedTable(bc_table); - - SimpleTable* key_value_table_read_time_ignored = - new SimpleTableWithReadTimeIgnored( - "KeyValueReadTimeIgnored", - {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}); - AddOwnedTable(key_value_table_read_time_ignored); - - SimpleTable* another_key_value = new SimpleTable( - "AnotherKeyValue", - {{"Key", types_->get_int64()}, {"value", types_->get_string()}}); - AddOwnedTable(another_key_value); - - const SimpleModel* one_double_model = - new SimpleModel("OneDoubleModel", {{"a", types_->get_double()}}, - {{"label", types_->get_double()}}, /*id=*/0); - const SimpleModel* one_double_one_string_model = new SimpleModel( - "OneDoubleOneStringModel", - {{"a", types_->get_double()}, {"b", types_->get_string()}}, - {{"label", types_->get_double()}}, /*id=*/1); - const SimpleModel* one_double_two_output_model = new SimpleModel( - "OneDoubleTwoOutputModel", {{"a", types_->get_double()}}, - {{"label1", types_->get_double()}, {"label2", types_->get_double()}}, - /*id=*/2); - catalog_->AddOwnedModel(one_double_model); - catalog_->AddOwnedModel(one_double_one_string_model); - catalog_->AddOwnedModel(one_double_two_output_model); - - SimpleTable* key_value2_table = new SimpleTable( - "KeyValue2", - {{"Key", types_->get_int64()}, {"Value2", types_->get_string()}}); - AddOwnedTable(key_value2_table); - - SimpleTable* space_value_table = new SimpleTable( - " Value", - {{" Key", types_->get_int64()}, {" Value", types_->get_string()}}); - AddOwnedTable(space_value_table); - - SimpleTable* table_with_default_column = new SimpleTable( - "TableWithDefaultColumn", - {{"id", types_->get_int64()}, {"a", types_->get_int64()}}); - - const std::string default_expr = "10"; - AnalyzerOptions analyzer_options; - std::unique_ptr output; - ZETASQL_CHECK_OK(AnalyzeExpression(default_expr, analyzer_options, catalog_.get(), - catalog_->type_factory(), &output)); - - SimpleColumn::ExpressionAttributes expr_attributes( - SimpleColumn::ExpressionAttributes::ExpressionKind::DEFAULT, default_expr, - output->resolved_expr()); - ZETASQL_CHECK_OK(table_with_default_column->AddColumn( - new SimpleColumn(table_with_default_column->Name(), "default_col", - types_->get_int64(), - {.column_expression = expr_attributes}), - /*is_owned=*/true)); - - sql_object_artifacts_.emplace_back(std::move(output)); - AddOwnedTable(table_with_default_column); - - SimpleTable* table_with_generated_column = new SimpleTable( - "TableWithGeneratedColumn", std::vector{}); - // Adding column A AS (B+C) to table. - AddGeneratedColumnToTable("A", {"B", "C"}, "B+C", - table_with_generated_column); - // Adding column B AS (C+1) to table. - AddGeneratedColumnToTable("B", {"C"}, "C+1", table_with_generated_column); - // Adding column C int64_t to table. - ZETASQL_ASSERT_OK(table_with_generated_column->AddColumn( - new SimpleColumn(table_with_generated_column->Name(), "C", - types::Int64Type()), - /*is_owned=*/true)); - // Adding column D AS (A+B+C) to table. - AddGeneratedColumnToTable("D", {"A", "B", "C"}, "A+B+C", - table_with_generated_column); - AddOwnedTable(table_with_generated_column); - - // Create two tables with the following schema. - // GeoStructTable1: geo STRUCT - // GeoStructTable2: geo STRUCT - const StructType* struct_with_geo_type1; - ZETASQL_CHECK_OK(types_->MakeStructType({{"a", types_->get_geography()}}, - &struct_with_geo_type1)); - const StructType* struct_with_geo_type2; - ZETASQL_CHECK_OK(types_->MakeStructType({{"b", types_->get_geography()}}, - &struct_with_geo_type2)); - - SimpleTable* geo_struct_table1 = - new SimpleTable("GeoStructTable1", {{"geo", struct_with_geo_type1}}); - AddOwnedTable(geo_struct_table1); - - SimpleTable* geo_struct_table2 = - new SimpleTable("GeoStructTable2", {{"geo", struct_with_geo_type2}}); - AddOwnedTable(geo_struct_table2); - - auto collatedTable = new SimpleTable("CollatedTable"); - const AnnotationMap* annotation_map_string_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(types_->get_string()); - annotation_map->SetAnnotation( - SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_string_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* annotation_map_string_binary; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(types_->get_string()); - annotation_map->SetAnnotation( - SimpleValue::String("binary")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_string_binary, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* annotation_map_struct_with_string_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(struct_type_); - annotation_map->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_struct_with_string_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* annotation_map_array_with_string_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(string_array_type_); - annotation_map->AsArrayMap() - ->mutable_element() - ->SetAnnotation(SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_array_with_string_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - auto string_ci = new SimpleColumn( - collatedTable->Name(), "string_ci", - AnnotatedType(types_->get_string(), annotation_map_string_ci)); - - auto string_cs = new SimpleColumn( - collatedTable->Name(), "string_binary", - AnnotatedType(types_->get_string(), annotation_map_string_binary)); - - auto struct_ci = new SimpleColumn( - collatedTable->Name(), "struct_with_string_ci", - AnnotatedType(struct_type_, annotation_map_struct_with_string_ci)); - - auto array_ci = new SimpleColumn( - collatedTable->Name(), "array_with_string_ci", - AnnotatedType(string_array_type_, annotation_map_array_with_string_ci)); - - ZETASQL_CHECK_OK(collatedTable->AddColumn(string_ci, /*is_owned=*/true)); - ZETASQL_CHECK_OK(collatedTable->AddColumn(string_cs, /*is_owned=*/true)); - ZETASQL_CHECK_OK(collatedTable->AddColumn(struct_ci, /*is_owned=*/true)); - ZETASQL_CHECK_OK(collatedTable->AddColumn(array_ci, /*is_owned=*/true)); - AddOwnedTable(collatedTable); - - auto complex_collated_table = new SimpleTable("ComplexCollatedTable"); - - const StructType* struct_with_bool_string_type; - // We let the first field have bool (rather than int32_t) type to avoid - // implicitly coercing ARRAY> to - // ARRAY> in the testing, which is currently not - // supported. - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_bool()}, {"b", types_->get_string()}}, - &struct_with_bool_string_type)); - const ArrayType* array_of_struct_type; - ZETASQL_CHECK_OK(types_->MakeArrayType(struct_with_bool_string_type, - &array_of_struct_type)); - const StructType* struct_with_array_of_struct_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_int32()}, {"b", array_of_struct_type}}, - &struct_with_array_of_struct_type)); - - const StructType* struct_of_multiple_string_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_string()}, {"b", types_->get_string()}}, - &struct_of_multiple_string_type)); - - const StructType* struct_of_double_and_string_and_array_type; - ZETASQL_CHECK_OK(types_->MakeStructType({{"a", types_->get_double()}, - {"b", types_->get_string()}, - {"c", string_array_type_}}, - &struct_of_double_and_string_and_array_type)); - - const AnnotationMap* annotation_map_array_of_struct_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(array_of_struct_type); - annotation_map->AsArrayMap() - ->mutable_element() - ->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_array_of_struct_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* annotation_map_struct_with_array_of_struct_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(struct_with_array_of_struct_type); - annotation_map->AsStructMap() - ->mutable_field(1) - ->AsArrayMap() - ->mutable_element() - ->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_struct_with_array_of_struct_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* annotation_map_struct_of_struct_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(nested_struct_type_); - annotation_map->AsStructMap() - ->mutable_field(1) - ->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_struct_of_struct_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* annotation_map_struct_with_string_ci_binary; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(struct_of_multiple_string_type); - annotation_map->AsStructMap() - ->mutable_field(0) - ->SetAnnotation(SimpleValue::String("und:ci")); - annotation_map->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::String("binary")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_struct_with_string_ci_binary, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* - annotation_map_struct_of_double_and_string_ci_and_array_ci; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(struct_of_double_and_string_and_array_type); - // Set collation for the second field of STRING type. - annotation_map->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::String("und:ci")); - // Set collation for the third field of ARRAY type. - annotation_map->AsStructMap() - ->mutable_field(2) - ->AsArrayMap() - ->mutable_element() - ->SetAnnotation(SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN( - annotation_map_struct_of_double_and_string_ci_and_array_ci, - types_->TakeOwnership(std::move(annotation_map))); - } - - auto string_no_collation = new SimpleColumn( - complex_collated_table->Name(), "string_no_collation", - AnnotatedType(types_->get_string(), /*annotation_map=*/nullptr)); - - auto array_of_struct_ci = new SimpleColumn( - complex_collated_table->Name(), "array_of_struct_ci", - AnnotatedType(array_of_struct_type, annotation_map_array_of_struct_ci)); - - auto struct_with_array_of_struct_ci = new SimpleColumn( - complex_collated_table->Name(), "struct_with_array_of_struct_ci", - AnnotatedType(struct_with_array_of_struct_type, - annotation_map_struct_with_array_of_struct_ci)); - - auto struct_of_struct_ci = new SimpleColumn( - complex_collated_table->Name(), "struct_of_struct_ci", - AnnotatedType(nested_struct_type_, annotation_map_struct_of_struct_ci)); - - auto struct_with_string_ci_binary = new SimpleColumn( - complex_collated_table->Name(), "struct_with_string_ci_binary", - AnnotatedType(struct_of_multiple_string_type, - annotation_map_struct_with_string_ci_binary)); - - auto struct_of_double_and_string_ci_and_array_ci = new SimpleColumn( - complex_collated_table->Name(), - "struct_of_double_and_string_ci_and_array_ci", - AnnotatedType( - struct_of_double_and_string_and_array_type, - annotation_map_struct_of_double_and_string_ci_and_array_ci)); - - ZETASQL_CHECK_OK(complex_collated_table->AddColumn(string_no_collation, - /*is_owned=*/true)); - ZETASQL_CHECK_OK(complex_collated_table->AddColumn(array_of_struct_ci, - /*is_owned=*/true)); - ZETASQL_CHECK_OK(complex_collated_table->AddColumn(struct_with_array_of_struct_ci, - /*is_owned=*/true)); - ZETASQL_CHECK_OK(complex_collated_table->AddColumn(struct_of_struct_ci, - /*is_owned=*/true)); - ZETASQL_CHECK_OK(complex_collated_table->AddColumn(struct_with_string_ci_binary, - /*is_owned=*/true)); - ZETASQL_CHECK_OK(complex_collated_table->AddColumn( - struct_of_double_and_string_ci_and_array_ci, - /*is_owned=*/true)); - AddOwnedTable(complex_collated_table); - - auto collated_table_with_proto = new SimpleTable("CollatedTableWithProto"); - const AnnotationMap* annotation_map_proto; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(proto_KitchenSinkPB_); - annotation_map->SetAnnotation( - SimpleValue::String("und:ci")); - ZETASQL_ASSERT_OK_AND_ASSIGN(annotation_map_proto, - types_->TakeOwnership(std::move(annotation_map))); - } - - auto proto_with_collation = new SimpleColumn( - collated_table_with_proto->Name(), "proto_with_collation", - AnnotatedType(proto_KitchenSinkPB_, annotation_map_proto)); - - ZETASQL_CHECK_OK(collated_table_with_proto->AddColumn(proto_with_collation, - /*is_owned=*/true)); - AddOwnedTable(collated_table_with_proto); - - auto generic_annotation_test_table = new SimpleTable("AnnotatedTable"); - - const AnnotationMap* generic_test_annotation_map_for_string_field; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(types_->get_string()); - annotation_map->SetAnnotation(SimpleValue::Int64(0)); - ZETASQL_ASSERT_OK_AND_ASSIGN(generic_test_annotation_map_for_string_field, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* generic_test_annotation_map_for_int_field; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(types_->get_int64()); - annotation_map->SetAnnotation(SimpleValue::Int64(1)); - ZETASQL_ASSERT_OK_AND_ASSIGN(generic_test_annotation_map_for_int_field, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* generic_annotation_map_for_struct_field; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(struct_type_); - annotation_map->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::Int64(2)); - ZETASQL_ASSERT_OK_AND_ASSIGN(generic_annotation_map_for_struct_field, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* generic_test_annotation_map_for_string_array_field; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(string_array_type_); - annotation_map->SetAnnotation(SimpleValue::Int64(3)); - ZETASQL_ASSERT_OK_AND_ASSIGN(generic_test_annotation_map_for_string_array_field, - types_->TakeOwnership(std::move(annotation_map))); - } - - const AnnotationMap* generic_annotation_map_for_nested_struct_field; - { - std::unique_ptr annotation_map = - AnnotationMap::Create(nested_struct_type_); - annotation_map->AsStructMap() - ->mutable_field(0) - ->SetAnnotation(SimpleValue::Int64(4)); - annotation_map->AsStructMap() - ->mutable_field(1) - ->AsStructMap() - ->mutable_field(1) - ->SetAnnotation(SimpleValue::Int64(5)); - ZETASQL_ASSERT_OK_AND_ASSIGN(generic_annotation_map_for_nested_struct_field, - types_->TakeOwnership(std::move(annotation_map))); - } - - auto generic_annotation_test_string_column = new SimpleColumn( - generic_annotation_test_table->Name(), "string", - AnnotatedType(types_->get_string(), - generic_test_annotation_map_for_string_field)); - auto generic_annotation_test_int_column = new SimpleColumn( - generic_annotation_test_table->Name(), "int_a", - AnnotatedType(types_->get_int64(), - generic_test_annotation_map_for_int_field)); - auto generic_annotation_test_int_column_unannotated = new SimpleColumn( - generic_annotation_test_table->Name(), "int_b", types_->get_int64()); - auto generic_annotation_test_struct_column = new SimpleColumn( - generic_annotation_test_table->Name(), "struct_a", - AnnotatedType(struct_type_, generic_annotation_map_for_struct_field)); - auto generic_annotation_test_string_array_column = new SimpleColumn( - generic_annotation_test_table->Name(), "string_array", - AnnotatedType(string_array_type_, - generic_test_annotation_map_for_string_array_field)); - auto generic_annotation_test_nested_struct_column = new SimpleColumn( - generic_annotation_test_table->Name(), "struct_b", - AnnotatedType(nested_struct_type_, - generic_annotation_map_for_nested_struct_field)); - - ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( - generic_annotation_test_string_column, /*is_owned=*/true)); - ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( - generic_annotation_test_int_column, /*is_owned=*/true)); - ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( - generic_annotation_test_int_column_unannotated, /*is_owned=*/true)); - ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( - generic_annotation_test_struct_column, /*is_owned=*/true)); - ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( - generic_annotation_test_string_array_column, /*is_owned=*/true)); - ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( - generic_annotation_test_nested_struct_column, /*is_owned=*/true)); - AddOwnedTable(generic_annotation_test_table); - - AddOwnedTable(new SimpleTable( - "SimpleTypes", - {{"int32", types_->get_int32()}, - {"int64", types_->get_int64()}, - {"uint32", types_->get_uint32()}, - {"uint64", types_->get_uint64()}, - {"string", types_->get_string()}, - {"bytes", types_->get_bytes()}, - {"bool", types_->get_bool()}, - {"float", types_->get_float()}, - {"double", types_->get_double()}, - {"date", types_->get_date()}, - // These types were removed, but we keep the fields in the sample - // table in order not to disturb analyzer test results too much (all - // the variable ids would change if we were to remove them). - // TODO: Remove them when all other changes settled down. - {"timestamp_seconds", types_->get_timestamp()}, - {"timestamp_millis", types_->get_timestamp()}, - {"timestamp_micros", types_->get_timestamp()}, - {"timestamp_nanos", types_->get_timestamp()}, - // Real types resume here. - {"timestamp", types_->get_timestamp()}, - {"numeric", types_->get_numeric()}, - {"bignumeric", types_->get_bignumeric()}, - {"json", types_->get_json()}, - {"uuid", types_->get_uuid()}})); - - { - auto simple_table_with_uid = - std::make_unique("SimpleTypesWithAnonymizationUid", - std::vector{ - {"int32", types_->get_int32()}, - {"int64", types_->get_int64()}, - {"uint32", types_->get_uint32()}, - {"uint64", types_->get_uint64()}, - {"string", types_->get_string()}, - {"bytes", types_->get_bytes()}, - {"bool", types_->get_bool()}, - {"float", types_->get_float()}, - {"double", types_->get_double()}, - {"date", types_->get_date()}, - {"uid", types_->get_int64()}, - {"numeric", types_->get_numeric()}}); - ZETASQL_CHECK_OK(simple_table_with_uid->SetAnonymizationInfo("uid")); - AddOwnedTable(simple_table_with_uid.release()); - } - - { - auto array_table_with_uid = std::make_unique( - "ArrayWithAnonymizationUid", std::vector{ - {"int64_array", int64array_type_}, - {"double_array", double_array_type_}, - {"uid", types_->get_int64()}}); - ZETASQL_CHECK_OK(array_table_with_uid->SetAnonymizationInfo("uid")); - AddOwnedTable(array_table_with_uid.release()); - } - - { - auto table_with_string_uid = std::make_unique( - "T1StringAnonymizationUid", - std::vector{{"uid", types_->get_string()}, - {"c2", types_->get_string()}}); - ZETASQL_CHECK_OK(table_with_string_uid->SetAnonymizationInfo("uid")); - AddOwnedTable(table_with_string_uid.release()); - } - - { - auto table_with_string_uid = std::make_unique( - "T2StringAnonymizationUid", - std::vector{{"c1", types_->get_string()}, - {"uid", types_->get_string()}}); - ZETASQL_CHECK_OK(table_with_string_uid->SetAnonymizationInfo("uid")); - AddOwnedTable(table_with_string_uid.release()); - } - - { - auto table_with_proto_uid = std::make_unique( - "ProtoAnonymizationUid", - std::vector{{"uid", proto_KitchenSinkPB_}}); - ZETASQL_CHECK_OK(table_with_proto_uid->SetAnonymizationInfo("uid")); - AddOwnedTable(table_with_proto_uid.release()); - } - - { - auto value_table_with_uid = std::make_unique( - "KitchenSinkWithUidValueTable", proto_KitchenSinkPB_); - ZETASQL_CHECK_OK(value_table_with_uid->SetAnonymizationInfo("string_val")); - AddOwnedTable(value_table_with_uid.release()); - } - - { - auto value_table_with_uid = std::make_unique( - "TestStructWithUidValueTable", struct_type_); - ZETASQL_CHECK_OK(value_table_with_uid->SetAnonymizationInfo("a")); - AddOwnedTable(value_table_with_uid.release()); - } - - { - auto value_table_with_doubly_nested_uid = std::make_unique( - "TestWithDoublyNestedStructUidValueTable", doubly_nested_struct_type_); - ZETASQL_CHECK_OK(value_table_with_doubly_nested_uid->SetAnonymizationInfo( - {"f", "d", "a"})); - AddOwnedTable(value_table_with_doubly_nested_uid.release()); - } - - { - auto value_table_with_proto_uid = std::make_unique( - "TestWithProtoUidValueTable", proto_MessageWithKitchenSinkPB_); - ZETASQL_CHECK_OK(value_table_with_proto_uid->SetAnonymizationInfo( - {"kitchen_sink", "nested_value", "nested_int64"})); - AddOwnedTable(value_table_with_proto_uid.release()); - } - - { - auto value_table_with_proto_uid_of_wrong_type = - std::make_unique("TestWithWrongTypeProtoUidValueTable", - proto_MessageWithKitchenSinkPB_); - ZETASQL_CHECK_OK(value_table_with_proto_uid_of_wrong_type->SetAnonymizationInfo( - std::vector({"kitchen_sink", "nested_value"}))); - AddOwnedTable(value_table_with_proto_uid_of_wrong_type.release()); - } - - AddOwnedTable( - new SimpleTable("GeographyTable", {{"key", types_->get_int64()}, - {"text", types_->get_string()}, - {"geo1", types_->get_geography()}, - {"geo2", types_->get_geography()}})); - - AddOwnedTable(new SimpleTable("NumericTypeTable", - {{"numeric_col", types_->get_numeric()}})); - - AddOwnedTable(new SimpleTable( - "BigNumericTypeTable", {{"bignumeric_col", types_->get_bignumeric()}})); - - AddOwnedTable( - new SimpleTable("JSONTable", {{"json_col", types_->get_json()}})); - - SimpleTable* two_integers = new SimpleTable( - "TwoIntegers", - {{"key", types_->get_int64()}, {"value", types_->get_int64()}}); - ZETASQL_CHECK_OK(two_integers->SetPrimaryKey({0})); - AddOwnedTable(two_integers); - - SimpleTable* four_integers = - new SimpleTable("FourIntegers", {{"key1", types_->get_int64()}, - {"value1", types_->get_int64()}, - {"key2", types_->get_int64()}, - {"value2", types_->get_int64()}}); - ZETASQL_CHECK_OK(four_integers->SetPrimaryKey({0, 2})); - AddOwnedTable(four_integers); - - // Tables with no columns are legal. - AddOwnedTable(new SimpleTable("NoColumns")); - - // Add tables for testing name collisions. - AddOwnedTable( - new SimpleTable("NameConflictTable", {{"key", types_->get_int32()}})); - AddOwnedTable(new SimpleTable( - "name_conflict_table", {{"a", types_->get_string()}, - {"name_conflict_field", types_->get_string()}})); - - // Add tables for testing create model with aliased queries. - AddOwnedTable( - new SimpleTable("user_training_data", {{"data", types_->get_int32()}})); - AddOwnedTable(new SimpleTable("user_custom_holiday", - {{"region", types_->get_string()}, - {"holiday_name", types_->get_string()}, - {"primary_date", types_->get_date()}})); - - // Create two tables with common prefix. - // One table "Int32Array" exists under the catalog "ArrayTableOrCatalog", so - // its full path is "ArrayTableOrCatalog.Int32Array". - // Another table "ArrayTableOrCatalog" exists under the root catalog, so its - // full path is "ArrayTableOrCatalog". "ArrayTableOrCatalog.Int32Array" refers - // to an array column. - SimpleCatalog* array_catalog = - catalog_->MakeOwnedSimpleCatalog("ArrayTableOrCatalog"); - SimpleTable* array_table_1 = - new SimpleTable("Int32Array", {{"Int32Array", int32array_type_}}); - ZETASQL_CHECK_OK(array_table_1->set_full_name("ArrayTableOrCatalog.Int32Array")); - SimpleTable* array_table_2 = new SimpleTable( - "ArrayTableOrCatalog", {{"Int32Array", int32array_type_}}); - - array_catalog->AddOwnedTable("Int32Array", array_table_1); - AddOwnedTable(array_table_2); - - // Add table for testing case insensitive lookup of column names. - const StructType* struct_with_unicode_column_table; - ZETASQL_CHECK_OK(types_->MakeStructType({{"1𐌰:aô", types_->get_string()}}, - &struct_with_unicode_column_table)); - AddOwnedTable(new SimpleTable("unicode_column_table", - {{"å学", types_->get_int64()}, - {"ô", types_->get_string()}, - {"a", struct_with_unicode_column_table}})); -} // NOLINT(readability/fn_size) - -void SampleCatalog::LoadProtoTables() { - // Add a named struct type. - const StructType* struct_TestStruct; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}, - &struct_TestStruct)); - catalog_->AddType("TestStruct", struct_TestStruct); - - const StructType* struct_AnotherTestStruct; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"K", types_->get_int32()}, {"v", types_->get_bytes()}}, - &struct_AnotherTestStruct)); - catalog_->AddType("AnotherTestStruct", struct_TestStruct); - - AddOwnedTable( - new SimpleTable("TestTable", {{"key", types_->get_int32()}, - {"TestEnum", enum_TestEnum_}, - {"KitchenSink", proto_KitchenSinkPB_}})); - - // We want to be sure that AmbiguousHasTestTable is not the first table - // serialized by SimpleCatalog::Serialize. See b/125914476 for more detail. - AddOwnedTable(new SimpleTable( - "ZZZ_AmbiguousHasTestTable", - {{"key", types_->get_int32()}, {"AmbiguousHas", proto_ambiguous_has_}})); - - AddOwnedTable( - new SimpleTable("CivilTimeTestTable", - {{"key", types_->get_int32()}, - {"CivilTimeTypesSink", proto_CivilTimeTypesSinkPB_}})); - - AddOwnedTable( - new SimpleTable("FieldFormatsTable", - {{"key", types_->get_int32()}, - {"FieldFormatsProto", proto_field_formats_proto_}})); - - AddOwnedTable( - new SimpleTable("ApproxDistanceFunctionOptionsProtoTable", - {{"key", types_->get_int32()}, - {"options", proto_approx_distance_function_options_}})); - - // EnumTable has two pseudo-columns Filename and RowId. - AddOwnedTable(new SimpleTable( - "EnumTable", - {new SimpleColumn("EnumTable", "key", types_->get_int32()), - new SimpleColumn("EnumTable", "TestEnum", enum_TestEnum_), - new SimpleColumn("EnumTable", "AnotherTestEnum", enum_AnotherTestEnum_), - new SimpleColumn("EnumTable", "Filename", types_->get_string(), - {.is_pseudo_column = true}), - new SimpleColumn("EnumTable", "RowId", types_->get_bytes(), - {.is_pseudo_column = true})}, - true /* take_ownership */)); - - AddOwnedTable(new SimpleTable( - "MapFieldTable", {{"key", types_->get_int32()}, - {"MessageWithMapField", proto_MessageWithMapField_}})); - - AddOwnedTable(new SimpleTable( - "Proto3Table", {{"key", types_->get_int32()}, - {"Proto3KitchenSink", proto3_KitchenSinkPB_}})); - - AddOwnedTable(new SimpleTable( - "Proto3InvalidMapTable", - {{"key", types_->get_int32()}, - {"MessageWithInvalidMap", proto3_MessageWithInvalidMap_}})); - - // This table only has pseudo-columns. - AddOwnedTable(new SimpleTable( - "AllPseudoColumns", - { - new SimpleColumn("AllPseudoColumns", "Key", types_->get_int32(), - {.is_pseudo_column = true}), - new SimpleColumn("AllPseudoColumns", "Value", types_->get_string(), - {.is_pseudo_column = true}), - }, - true /* take_ownership */)); - - // Another table with only pseudo-columns, this time with a repeated field. We - // don't extend AllPseudoColumns to avoid breaking pre-existing tests. - AddOwnedTable(new SimpleTable( - "AllPseudoColumnsWithRepeated", - { - new SimpleColumn("AllPseudoColumns", "Key", types_->get_int32(), - {.is_pseudo_column = true}), - new SimpleColumn("AllPseudoColumns", "Value", types_->get_string(), - {.is_pseudo_column = true}), - new SimpleColumn("AllPseudoColumns", "RepeatedValue", - string_array_type_, {.is_pseudo_column = true}), - }, - true /* take_ownership */)); - - { - // This table has an anonymous pseudo-column, which should be inaccessible. - auto table = new SimpleTable("AnonymousPseudoColumn"); - AddOwnedTable(table); - ZETASQL_CHECK_OK(table->set_allow_anonymous_column_name(true)); - ZETASQL_CHECK_OK(table->AddColumn( - new SimpleColumn("AnonymousPseudoColumn", "key", types_->get_int32()), - true /* take_ownership */)); - ZETASQL_CHECK_OK(table->AddColumn( - new SimpleColumn("AnonymousPseudoColumn", "", types_->get_string(), - {.is_pseudo_column = true}), - true /* take_ownership */)); - } - - { - // This table has two duplicate columns. - auto table = new SimpleTable("DuplicateColumns"); - ZETASQL_CHECK_OK(table->set_allow_duplicate_column_names(true)); - AddOwnedTable(table); - ZETASQL_CHECK_OK( - table->AddColumn(new SimpleColumn("DuplicateColumns", "DuplicateColumn", - types_->get_int32()), - /*is_owned=*/true)); - ZETASQL_CHECK_OK( - table->AddColumn(new SimpleColumn("DuplicateColumns", "DuplicateColumn", - types_->get_string()), - /*is_owned=*/true)); - } - - AddOwnedTable(new SimpleTable( - "AllNonKeysNonWritable", - { - new SimpleColumn("AllNonKeysNonWritable", "Key", types_->get_int32(), - {.is_writable_column = true}), - new SimpleColumn("AllNonKeysNonWritable", "Value", - types_->get_string(), {.is_writable_column = false}), - new SimpleColumn("AllNonKeysNonWritable", "RepeatedValue", - int32array_type_, {.is_writable_column = false}), - new SimpleColumn("AllNonKeysNonWritable", "ProtoValue", - proto_TestExtraPB_, {.is_writable_column = false}), - new SimpleColumn("AllNonKeysNonWritable", "StructValue", struct_type_, - {.is_writable_column = false}), - }, - true /* take_ownership */)); - - SimpleTable* complex_types = - new SimpleTable("ComplexTypes", {{"key", types_->get_int32()}, - {"TestEnum", enum_TestEnum_}, - {"KitchenSink", proto_KitchenSinkPB_}, - {"Int32Array", int32array_type_}, - {"TestStruct", nested_struct_type_}, - {"TestProto", proto_TestExtraPB_}}); - ZETASQL_CHECK_OK(complex_types->SetPrimaryKey({0})); - AddOwnedTable(complex_types); - - AddOwnedTable(new SimpleTable( - "MoreComplexTypes", - {{"key", types_->get_int32()}, - {"ArrayOfStruct", struct_array_type_}, - {"StructOfArrayOfStruct", struct_with_array_field_type_}})); - - AddOwnedTable(new SimpleTable( - "StructWithKitchenSinkTable", - {{"kitchen_sink", proto_KitchenSinkPB_}, - {"s", struct_with_kitchen_sink_type_}, - {"t", struct_of_array_of_struct_with_kitchen_sink_type_}})); - - AddOwnedTable( - new SimpleTable("DoublyNestedStructTable", - {{"key", types_->get_int32()}, - {"doubly_nested_struct", doubly_nested_struct_type_}})); - - AddOwnedTable(new SimpleTable("KitchenSinkValueTable", proto_KitchenSinkPB_)); - - AddOwnedTable(new SimpleTable("MessageWithKitchenSinkValueTable", - proto_MessageWithKitchenSinkPB_)); - - AddOwnedTable(new SimpleTable("EmptyMessageValueTable", proto_EmptyMessage_)); - - catalog_->AddOwnedTable( - new SimpleTable("TestExtraPBValueTable", proto_TestExtraPB_)); - - catalog_->AddOwnedTable(new SimpleTable("TestAbPBValueTable", proto_abPB_)); - - catalog_->AddOwnedTable(new SimpleTable("TestBcPBValueTable", proto_bcPB_)); - - catalog_->AddOwnedTable(new SimpleTable("TestBcPBValueProtoTable", - {{"value", proto_bcPB_}, - {"Filename", types_->get_string()}, - {"RowId", types_->get_int64()}})); - - // TestExtraValueTable also has pseudo-columns Filename and RowID. - SimpleTable* extra_value_table; - AddOwnedTable(( - extra_value_table = new SimpleTable( - "TestExtraValueTable", - {new SimpleColumn("TestExtraValueTable", "value", proto_TestExtraPB_), - new SimpleColumn("TestExtraValueTable", "Filename", - types_->get_string(), {.is_pseudo_column = true}), - new SimpleColumn("TestExtraValueTable", "RowId", types_->get_bytes(), - {.is_pseudo_column = true})}, - true /* take_ownership */))); - extra_value_table->set_is_value_table(true); - - // AmbiguousFieldValueTable has a pseudo-column int32_val1 that is - // also a field name. - SimpleTable* ambiguous_field_value_table; - AddOwnedTable((ambiguous_field_value_table = new SimpleTable( - "AmbiguousFieldValueTable", - { - new SimpleColumn("TestExtraValueTable", "value", - proto_TestExtraPB_), - new SimpleColumn("TestExtraValueTable", "int32_val1", - types_->get_string(), - {.is_pseudo_column = true}), - }, - true /* take_ownership */))); - ambiguous_field_value_table->set_is_value_table(true); - - SimpleTable* int64_value_table; - AddOwnedTable(int64_value_table = new SimpleTable( - "Int64ValueTable", - {new SimpleColumn("Int64ValueTable", "IntValue", - types_->get_int64())}, - /*take_ownership=*/true)); - int64_value_table->set_is_value_table(true); - ZETASQL_CHECK_OK(int64_value_table->SetPrimaryKey({0})); - - AddOwnedTable(new SimpleTable( - "ArrayTypes", - {{"Int32Array", int32array_type_}, - {"Int64Array", int64array_type_}, - {"UInt32Array", uint32array_type_}, - {"UInt64Array", uint64array_type_}, - {"StringArray", string_array_type_}, - {"BytesArray", bytes_array_type_}, - {"BoolArray", bool_array_type_}, - {"FloatArray", float_array_type_}, - {"DoubleArray", double_array_type_}, - {"DateArray", date_array_type_}, - // These corresponding legacy types were removed, but we keep the fields - // in the sample table in order not to disturb analyzer test results too - // much (all the variable ids would change if we were to remove them). - // TODO: Eventually remove these. - {"TimestampSecondsArray", timestamp_array_type_}, - {"TimestampMillisArray", timestamp_array_type_}, - {"TimestampMicrosArray", timestamp_array_type_}, - // Real types resume here. - {"TimestampArray", timestamp_array_type_}, - {"ProtoArray", proto_array_type_}, - {"StructArray", struct_array_type_}, - {"JsonArray", json_array_type_}, - {"NumericArray", numeric_array_type_}, - {"BigNumericArray", bignumeric_array_type_}, - {"IntervalArray", interval_array_type_}})); - - const EnumType* enum_TestEnum = - GetEnumType(zetasql_test__::TestEnum_descriptor()); - AddOwnedTable(new SimpleTable("SimpleTypesWithStruct", - {{"key", types_->get_int32()}, - {"TestEnum", enum_TestEnum}, - {"TestStruct", nested_struct_type_}})); - - const ProtoType* proto_recursive_type = - GetProtoType(zetasql_test__::RecursivePB::descriptor()); - AddOwnedTable(new SimpleTable("RecursivePBTable", - {{"RecursivePB", proto_recursive_type}})); - - AddOwnedTable(new SimpleTable( - "KeywordTable", - {{"current_date", types_->get_date()}, - {"current_timestamp", types_->get_timestamp()}, - // The corresponding legacy types were removed, but we keep the fields in - // the sample table in order not to disturb analyzer test results too - // much (all the variable ids would change if we were to remove them). - // TODO: Eventually remove these. - {"current_timestamp_seconds", types_->get_timestamp()}, - {"current_timestamp_millis", types_->get_timestamp()}, - {"current_timestamp_micros", types_->get_timestamp()}})); - - AddOwnedTable(new SimpleTable("TestStructValueTable", struct_type_)); - - AddOwnedTable(new SimpleTable("TestNestedStructValueTable", - doubly_nested_struct_type_)); - - AddOwnedTable(new SimpleTable("StructWithOneFieldValueTable", - struct_with_one_field_type_)); - - AddOwnedTable(new SimpleTable("Int32ValueTable", types_->get_int32())); - - AddOwnedTable(new SimpleTable("Int32ArrayValueTable", int32array_type_)); - - AddOwnedTable( - new SimpleTable("AnnotatedEnumTable", enum_TestEnumWithAnnotations_)); -} - -void SampleCatalog::LoadViews(const LanguageOptions& language_options) { - // Ensure the language options used allow CREATE FUNCTION - LanguageOptions language = language_options; - language.AddSupportedStatementKind(RESOLVED_CREATE_VIEW_STMT); - language.EnableLanguageFeature(FEATURE_CREATE_VIEW_WITH_COLUMN_LIST); - AnalyzerOptions analyzer_options; - analyzer_options.set_language(language); - - auto add_view = [&analyzer_options, this](absl::string_view create_view) { - std::unique_ptr analyzer_output; - ZETASQL_CHECK_OK(AddViewFromCreateView(create_view, analyzer_options, - /*allow_non_temp=*/true, analyzer_output, - *catalog_)); - sql_object_artifacts_.emplace_back(std::move(analyzer_output)); - }; - add_view("CREATE VIEW TwoIntsView SQL SECURITY INVOKER AS SELECT 1 a, 2 b;"); - add_view( - "CREATE VIEW UnprojectedColumnView SQL SECURITY INVOKER AS " - "SELECT a, b FROM (SELECT 1 AS a, 2 AS b, 3 AS c);"); - add_view( - "CREATE VIEW ColumnListView(a, b) SQL SECURITY INVOKER AS " - "SELECT 1, 2;"); - add_view( - "CREATE VIEW CteView SQL SECURITY INVOKER AS " - "WITH t AS (SELECT 1 a, 2 b) SELECT * FROM t;"); - add_view( - "CREATE VIEW OneStructView SQL SECURITY INVOKER AS " - "SELECT STRUCT(1 AS a, 2 AS b) AS ab;"); - add_view( - "CREATE VIEW AsStructView SQL SECURITY INVOKER AS " - "SELECT AS STRUCT 1 a, 2 b;"); - add_view( - "CREATE VIEW OneScalarView SQL SECURITY INVOKER AS " - "SELECT '123' AS ab"); - add_view( - "CREATE VIEW AsScalarView SQL SECURITY INVOKER AS " - "SELECT AS VALUE '123'"); - add_view( - "CREATE VIEW ScanTableView SQL SECURITY INVOKER AS " - "SELECT key AS a, value AS b FROM TwoIntegers;"); - add_view( - "CREATE VIEW ScanViewView SQL SECURITY INVOKER AS " - "SELECT a, 'b' AS b FROM ScanTableView;"); - add_view( - "CREATE VIEW UnspecifiedRightsView SQL SECURITY DEFINER AS " - "SELECT 1 AS a;"); - add_view( - "CREATE VIEW DefinerRightsView SQL SECURITY DEFINER AS " - "SELECT 1 AS a, 'x' AS b, false AS c;"); -} - -void SampleCatalog::LoadNestedCatalogs() { - SimpleCatalog* nested_catalog = - catalog_->MakeOwnedSimpleCatalog("nested_catalog"); - - // Add nested_catalog with some tables with the same and different names. - nested_catalog->AddTable(key_value_table_); - nested_catalog->AddTable("NestedKeyValue", key_value_table_); - - { - // Add a table that only appears in this nested catalog (and in turn, can - // only be found via the nested name). - SimpleTable* nested_key_value_table = new SimpleTable( - "KeyValueNested", - {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}); - ZETASQL_CHECK_OK( - nested_key_value_table->set_full_name("nested_catalog.KeyValueNested")); - nested_catalog->AddOwnedTable(nested_key_value_table); - } - - // Add nested_catalog with some connections - nested_catalog->AddConnection( - owned_connections_.find("connection1")->second.get()); - nested_catalog->AddConnection( - owned_connections_.find("connection2")->second.get()); - nested_catalog->AddConnection( - owned_connections_.find("NestedConnection")->second.get()); - - // Add recursive_catalog which points back to the same catalog. - // This allows resolving names like - // recursive_catalog.recursive_catalog.recursive_catalog.TestTable - catalog_->AddCatalog("recursive_catalog", catalog_.get()); - - // Add a function to the nested catalog: - // nested_catalog.nested_function() -> - FunctionSignature signature( - {types_->get_int64(), {types_->get_int64()}, /*context_id=*/-1}); - std::vector function_name_path = {"nested_catalog", - "nested_function"}; - Function* function = new Function(function_name_path, "sample_functions", - Function::SCALAR, {signature}); - nested_catalog->AddOwnedFunction(function); - - // A scalar function with argument alias support in the nested catalog. - { - FunctionArgumentType aliased( - ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - FunctionArgumentType non_aliased(ARG_TYPE_ANY_2); - std::vector signatures = { - {types_->get_int64(), {aliased, non_aliased}, /*context_id=*/-1}}; - Function* function = new Function( - std::vector{"nested_catalog", "fn_for_argument_alias"}, - "sample_functions", Function::SCALAR, signatures); - nested_catalog->AddOwnedFunction(function); - } - // Add a procedure to the nested catalog: - // nested_catalog.nested_procedure() -> - Procedure* procedure = - new Procedure({"nested_catalog", "nested_procedure"}, signature); - nested_catalog->AddOwnedProcedure(procedure); - - // Add a doubly nested catalog, and a function to the doubly nested catalog: - // nested_catalog.nested_nested_catalog.nested_function() -> - SimpleCatalog* nested_nested_catalog = - nested_catalog->MakeOwnedSimpleCatalog("nested_nested_catalog"); - function_name_path = {"nested_catalog", "nested_nested_catalog", - "nested_function"}; - function = new Function(function_name_path, "sample_functions", - Function::SCALAR, {signature}); - nested_nested_catalog->AddOwnedFunction(function); - - // Add table "nested" to the nested catalog and doubly nested catalog - // with the same name "nested": - // nested_catalog.nested - // nested_catalog.nested.nested - nested_catalog->AddTable("nested", key_value_table_); - SimpleCatalog* duplicate_name_nested_catalog = - nested_catalog->MakeOwnedSimpleCatalog("nested"); - duplicate_name_nested_catalog->AddTable("nested", key_value_table_); - - // Add a struct-typed constant to the doubly nested catalog. - const StructType* nested_constant_struct_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"eee", types_->get_int32()}, {"fff", nested_struct_type_}}, - &nested_constant_struct_type)); - std::unique_ptr constant_struct; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"nested_catalog", "nested_nested_catalog", - "TestConstantStruct"}, - Value::Struct(nested_constant_struct_type, - {Value::Int32(-3456), - Value::Struct(nested_struct_type_, - {Value::Int32(3), - Value::Struct(struct_type_, - {Value::Int32(223), - Value::String("foo")})})}), - &constant_struct)); - nested_nested_catalog->AddOwnedConstant(constant_struct.release()); - - // Add an enum and a proto to the nested catalog. - nested_catalog->AddType(enum_TestEnum_->enum_descriptor()->full_name(), - enum_TestEnum_); - nested_catalog->AddType(proto_KitchenSinkPB_->descriptor()->full_name(), - proto_KitchenSinkPB_); - nested_catalog->AddType( - proto_CivilTimeTypesSinkPB_->descriptor()->full_name(), - proto_CivilTimeTypesSinkPB_); - - // Add TVFs to the nested catalogs. We use this to test name resolution during - // serialization/deserialization. - const std::string kColumnNameKey = "key"; - TVFRelation::ColumnList columns; - columns.emplace_back(kColumnNameKey, types::Int64Type()); - TVFRelation single_key_col_schema(columns); - - int context_id = -1; - nested_catalog->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"nested_catalog", "nested_tvf_one"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - single_key_col_schema, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id), - single_key_col_schema)); - nested_nested_catalog->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"nested_catalog", "nested_nested_catalog", "nested_tvf_two"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - single_key_col_schema, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id), - single_key_col_schema)); - - // Load a nested catalog with a constant whose names conflict with a table - // and its field. - SimpleCatalog* name_conflict_catalog = - catalog_->MakeOwnedSimpleCatalog("name_conflict_table"); - std::unique_ptr constant; - ZETASQL_CHECK_OK( - SimpleConstant::Create({"name_conflict_table", "name_conflict_field"}, - Value::Bool(false), &constant)); - name_conflict_catalog->AddOwnedConstant(constant.release()); - - // Add for testing named constants in catalogs. - SimpleCatalog* nested_catalog_with_constant = - catalog_->MakeOwnedSimpleCatalog("nested_catalog_with_constant"); - ZETASQL_CHECK_OK( - SimpleConstant::Create({"nested_catalog_with_constant", "KnownConstant"}, - Value::Bool(false), &constant)); - nested_catalog_with_constant->AddOwnedConstant(constant.release()); - - // Add for testing conflicts with named - // constants. - SimpleCatalog* nested_catalog_with_catalog = - catalog_->MakeOwnedSimpleCatalog("nested_catalog_with_catalog"); - ZETASQL_CHECK_OK(SimpleConstant::Create( - {"nested_catalog_with_catalog", "TestConstantBool"}, Value::Bool(false), - &constant)); - nested_catalog_with_catalog->AddOwnedConstant(constant.release()); - ZETASQL_CHECK_OK(SimpleConstant::Create({"nested_catalog_with_catalog", "c"}, - Value::Double(-9999.999), &constant)); - nested_catalog_with_catalog->AddOwnedConstant(constant.release()); - SimpleCatalog* nested_catalog_catalog = - nested_catalog_with_catalog->MakeOwnedSimpleCatalog( - "nested_catalog_catalog"); - ZETASQL_CHECK_OK(SimpleConstant::Create( - {"nested_catalog_with_catalog", "nested_catalog_catalog", "a"}, - Value::Float(-1.4987f), &constant)); - nested_catalog_catalog->AddOwnedConstant(constant.release()); - ZETASQL_CHECK_OK(SimpleConstant::Create( - {"nested_catalog_with_catalog", "nested_catalog_catalog", "c"}, - Value::String("foo"), &constant)); - nested_catalog_catalog->AddOwnedConstant(constant.release()); - - // Add a constant to . - ZETASQL_CHECK_OK(SimpleConstant::Create({"nested_catalog", "TestConstantBool"}, - Value::Bool(false), &constant)); - nested_catalog->AddOwnedConstant(constant.release()); - - // Add another constant to that conflicts with a procedure. - ZETASQL_CHECK_OK(SimpleConstant::Create({"nested_catalog", "nested_procedure"}, - Value::Int64(2345), &constant)); - nested_catalog->AddOwnedConstant(constant.release()); - - // Add a constant to which requires backticks. - std::unique_ptr string_constant_nonstandard_name; - - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"nested_catalog", "Test Constant-String"}, - Value::String("Test constant in nested catalog"), - &string_constant_nonstandard_name)); - nested_catalog->AddOwnedConstant(string_constant_nonstandard_name.release()); - - // Add struct constant with the same name as a nested catalog - const StructType* nested_nested_catalog_type; - ZETASQL_CHECK_OK(types_->MakeStructType({{"xxxx", types_->get_int64()}}, - &nested_nested_catalog_type)); - - SimpleCatalog* wwww_catalog = nested_catalog->MakeOwnedSimpleCatalog("wwww"); - - std::unique_ptr wwww_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"nested_catalog", "wwww"}, - Value::Struct(nested_nested_catalog_type, {Value::Int64(8)}), - &wwww_constant)); - nested_catalog->AddOwnedConstant(wwww_constant.release()); - - std::unique_ptr xxxx_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"nested_catalog", "wwww", "xxxx"}, - Value::Struct(nested_nested_catalog_type, {Value::Int64(8)}), - &xxxx_constant)); - wwww_catalog->AddOwnedConstant(xxxx_constant.release()); - - // Load a nested catalog with a name that resembles a system variable. - SimpleCatalog* at_at_nested_catalog = - catalog_->MakeOwnedSimpleCatalog("@@nested_catalog"); - std::unique_ptr at_at_nested_catalog_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"@@nested_catalog", "sysvar2"}, Value::Int64(8), - &at_at_nested_catalog_constant)); - at_at_nested_catalog->AddOwnedConstant( - at_at_nested_catalog_constant.release()); - - { - std::unique_ptr rounding_mode_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"nested_catalog", "constant_rounding_mode"}, - Value::Enum(types::RoundingModeEnumType(), "ROUND_HALF_EVEN"), - &rounding_mode_constant)); - nested_catalog->AddOwnedConstant(std::move(rounding_mode_constant)); - } - - auto udf_catalog = nested_catalog->MakeOwnedSimpleCatalog("udf"); - function = - new Function("timestamp_add", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::REQUIRED}, - {types_->get_int64(), FunctionArgumentType::REQUIRED}, - {types_->get_int64(), FunctionArgumentType::REQUIRED}}, - /*context_id=*/-1}); - udf_catalog->AddOwnedFunction(function); -} - -static FreestandingDeprecationWarning CreateDeprecationWarning( - int id, - DeprecationWarning_Kind kind = DeprecationWarning::PROTO3_FIELD_PRESENCE) { - FreestandingDeprecationWarning warning; - const std::string foo_id = absl::StrCat("foo_", id); - warning.set_message(absl::StrCat("Operation is deprecated")); - warning.set_caret_string(absl::StrCat("some caret string for ", foo_id, "\n", - " ^")); - warning.mutable_deprecation_warning()->set_kind(kind); - - ErrorLocation* warning_location = warning.mutable_error_location(); - warning_location->set_line(10 + id); - warning_location->set_column(20 + id); - warning_location->set_filename(absl::StrCat("module", id, ".sqlm")); - - return warning; -} - -void SampleCatalog::AddFunctionWithArgumentType(std::string type_name, - const Type* arg_type) { - auto function = std::make_unique( - absl::StrCat("fn_on_", type_name), "sample_functions", Function::SCALAR); - function->AddSignature({types_->get_bool(), {arg_type}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); -} - -void SampleCatalog::LoadExtendedSubscriptFunctions() { - // Add new signatures for '$subscript_with_offset' so we can do some - // additional testing. The signatures are: - // 1) [OFFSET()]: - // $subscript_with_offset(string, int64_t) -> (string) - // 2) [OFFSET()]: - // $subscript_with_offset(string, string) -> (string) - const Function* subscript_offset_function; - ZETASQL_CHECK_OK(catalog_->GetFunction("$subscript_with_offset", - &subscript_offset_function)); - ABSL_CHECK(subscript_offset_function != nullptr); - // If we ever update the builtin function implementation to actually include - // a signature, then take a look at this code to see if it is still needed. - ABSL_CHECK_EQ(subscript_offset_function->NumSignatures(), 0); - Function* mutable_subscript_offset_function = - const_cast(subscript_offset_function); - mutable_subscript_offset_function->AddSignature( - {types_->get_string(), - {types_->get_string(), types_->get_int64()}, - /*context_id=*/-1}); - mutable_subscript_offset_function->AddSignature( - {types_->get_string(), - {types_->get_string(), types_->get_string()}, - /*context_id=*/-1}); -} - -const Function* SampleCatalog::AddFunction( - absl::string_view name, Function::Mode mode, - std::vector function_signatures, - FunctionOptions function_options) { - for (const FunctionSignature& sig : function_signatures) { - ZETASQL_CHECK_OK(sig.IsValid(PRODUCT_INTERNAL)); - ZETASQL_CHECK_OK(sig.IsValid(PRODUCT_EXTERNAL)); - } - auto function = std::make_unique(name, "sample_functions", mode, - std::move(function_signatures), - std::move(function_options)); - const Function* function_ptr = function.get(); - catalog_->AddOwnedFunction(std::move(function)); - return function_ptr; -} - -void SampleCatalog::LoadFunctions() { - // Add a function to illustrate how repeated/optional arguments are resolved. - Function* function = - new Function("test_function", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::REQUIRED}, - {types_->get_int64(), FunctionArgumentType::REPEATED}, - {types_->get_int64(), FunctionArgumentType::REPEATED}, - {types_->get_int64(), FunctionArgumentType::REQUIRED}, - {types_->get_int64(), FunctionArgumentType::OPTIONAL}}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - function = - new Function("volatile_function", "sample_functions", Function::SCALAR, - {{types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::REQUIRED}}, - /*context_id=*/-1}}, - FunctionOptions().set_volatility(FunctionEnums::VOLATILE)); - catalog_->AddOwnedFunction(function); - - function = - new Function("stable_function", "sample_functions", Function::SCALAR, - {{types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::REQUIRED}}, - /*context_id=*/-1}}, - FunctionOptions().set_volatility(FunctionEnums::STABLE)); - catalog_->AddOwnedFunction(function); - - // Add a function that takes a specific proto as an argument. - function = - new Function("fn_on_KitchenSinkPB", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {proto_KitchenSinkPB_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes a specific enum as an argument. - function = - new Function("fn_on_TestEnum", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {enum_TestEnum_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // These sample functions are named 'fn_on_' with one argument of - // type that returns a bool. - AddFunctionWithArgumentType("bool", types_->get_bool()); - AddFunctionWithArgumentType("int32", types_->get_int32()); - AddFunctionWithArgumentType("int64", types_->get_int64()); - AddFunctionWithArgumentType("uint32", types_->get_uint32()); - AddFunctionWithArgumentType("uint64", types_->get_uint64()); - AddFunctionWithArgumentType("float", types_->get_float()); - AddFunctionWithArgumentType("double", types_->get_double()); - AddFunctionWithArgumentType("date", types_->get_date()); - AddFunctionWithArgumentType("timestamp", types_->get_timestamp()); - AddFunctionWithArgumentType("string", types_->get_string()); - AddFunctionWithArgumentType("bytes", types_->get_bytes()); - - // Add a function with bytes and string overload. - function = new Function("fn_overloaded_bytes_and_string", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_string(), {types_->get_string()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bytes(), {types_->get_bytes()}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with bytes and date overload. - function = new Function("fn_overloaded_bytes_and_date", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), {types_->get_date()}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with bytes and timestamp overload. - function = new Function("fn_overloaded_bytes_and_timestamp", - "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), {types_->get_timestamp()}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with bytes and time overload. - function = new Function("fn_overloaded_bytes_and_time", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), {types_->get_time()}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with bytes and datetime overload. - function = new Function("fn_overloaded_bytes_and_datetime", - "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), {types_->get_datetime()}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with bytes and enum overload. - function = new Function("fn_overloaded_bytes_and_enum", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), {enum_TestEnum_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with bytes and proto overload. - function = new Function("fn_overloaded_bytes_and_proto", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), {proto_KitchenSinkPB_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes an arbitrary type argument. - function = new Function("fn_on_arbitrary_type_argument", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {ARG_TYPE_ARBITRARY}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes any type enum. - function = - new Function("fn_on_any_enum", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {ARG_ENUM_ANY}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes any type proto. - function = - new Function("fn_on_any_proto", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {ARG_PROTO_ANY}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes any type struct. - function = - new Function("fn_on_any_struct", "sample_functions", Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {ARG_STRUCT_ANY}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes any array and returns element type. - function = new Function("fn_on_any_array_returns_element", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{ARG_TYPE_ANY_1}, {ARG_ARRAY_TYPE_ANY_1}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes any type and returns an array of that type. - function = new Function("fn_on_any_element_returns_array", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{ARG_ARRAY_TYPE_ANY_1}, {ARG_TYPE_ANY_1}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes an array and returns an int32_t type. - function = new Function("fn_on_int32_array_returns_int32", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{types_->get_int32()}, {int32array_type_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes an array and returns an int64_t type. - function = new Function("fn_on_int64_array_returns_int64", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{types_->get_int64()}, {int64array_type_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes a STRUCT and returns bool. - function = new Function("fn_on_struct_int32_string", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {struct_type_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - const StructType* struct_int32_date_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_int32()}, {"b", types_->get_date()}}, - &struct_int32_date_type)); - - // Add a function that takes a STRUCT and returns bool. - function = new Function("fn_on_struct_int32_date", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {struct_int32_date_type}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - const StructType* struct_int64_string_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_int64()}, {"b", types_->get_string()}}, - &struct_int64_string_type)); - - // Add a function that takes a STRUCT and returns bool. - function = new Function("fn_on_struct_int64_string", "sample_functions", - Function::SCALAR); - function->AddSignature( - {types_->get_bool(), {struct_int64_string_type}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(absl::WrapUnique(function)); - - // Add a function that takes a MAP and returns an int32_t type. - function = new Function("fn_on_int32_map_returns_int32", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{types_->get_int32()}, {int32map_type_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes a MAP and returns an int64_t type. - function = new Function("fn_on_int64_map_returns_int64", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{types_->get_int64()}, {int64map_type_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes a MAP and returns an bytes type. - function = new Function("fn_on_bytes_map_returns_bytes", "sample_functions", - Function::SCALAR); - function->AddSignature( - {{types_->get_bytes()}, {bytesmap_type_}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Adds an scalar function that takes multiple repeated and optional - // arguments. - function = new Function( - "fn_rep_opt", "sample_functions", Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("a0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("r0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REPEATED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("r1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REPEATED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("r2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REPEATED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("a1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - ZETASQL_CHECK_OK(function->signatures()[0].IsValid(ProductMode::PRODUCT_EXTERNAL)); - - AddFunction("fn_repeated_with_optional_named_only", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Optional().String().NameOnly("o1")) - .Build()}); - - AddFunction("fn_repeated_diff_args_optional_named_only", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Repeated().Int64()) - .AddArg(ArgBuilder().Optional().Bool().NameOnly("o1")) - .Build()}); - - AddFunction("fn_repeated_arbitrary_with_optional_named_only", - Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Optional().Any().Name("o1")) - .Build()}); - - AddFunction("fn_repeated_with_optional_named_or_positional", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Optional().String().Name("o1")) - .Build()}); - - AddFunction("fn_repeated_diff_args_optional_named_or_positional", - Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Repeated().Int64()) - .AddArg(ArgBuilder().Optional().Bool().Name("o1")) - .Build()}); - - AddFunction("fn_repeated_arbitrary_with_optional_named_or_positional", - Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Optional().Any().Name("o1")) - .Build()}); - - AddFunction("fn_repeated_with_required_named", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().String().NameOnly("r1")) - .Build()}); - - AddFunction("fn_repeated_diff_args_required_named", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(ArgBuilder().Repeated().Int64()) - .AddArg(ArgBuilder().Bool().NameOnly("r1")) - .Build()}); - - AddFunction("fn_repeated_arbitrary_with_required_named", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Any().NameOnly("r1")) - .Build()}); - - AddFunction("fn_repeated_t1_t2_with_optional_named_t1", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().T1()) - .AddArg(ArgBuilder().Repeated().T2()) - .AddArg(ArgBuilder().Optional().T1().NameOnly("o1")) - .Build()}); - - AddFunction("fn_repeated_t1_arbitrary_with_optional_named_t1", - Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().T1()) - .AddArg(ArgBuilder().Repeated().Any()) - .AddArg(ArgBuilder().Optional().T1().NameOnly("o1")) - .Build()}); - - AddFunction( - "fn_optional_any", Function::SCALAR, - {SignatureBuilder().AddArg(ArgBuilder().Optional().Any()).Build()}); - - AddFunction( - "fn_repeated_any", Function::SCALAR, - {SignatureBuilder().AddArg(ArgBuilder().Repeated().Any()).Build()}); - - AddFunction( - "fn_optional_t1", Function::SCALAR, - {SignatureBuilder().AddArg(ArgBuilder().Optional().T1()).Build()}); - - AddFunction("fn_optional_t1_ret_t1", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Optional().T1()) - .Returns(ArgBuilder().T1()) - .Build()}); - - AddFunction( - "fn_repeated_t1", Function::SCALAR, - {SignatureBuilder().AddArg(ArgBuilder().Repeated().T1()).Build()}); - - AddFunction("fn_repeated_t1_ret_t1", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().T1()) - .Returns(ArgBuilder().T1()) - .Build()}); - - // Adds an aggregate function that takes no argument but supports order by. - function = new Function("sort_count", "sample_functions", Function::AGGREGATE, - {{types_->get_int64(), {}, /*context_id=*/-1}}, - FunctionOptions().set_supports_order_by(true)); - catalog_->AddOwnedFunction(function); - - // Adds an aggregate function that takes multiple arguments and supports - // order by arguments. - function = new Function( - "multi_sort_count", "sample_functions", Function::AGGREGATE, - {{types_->get_int64(), - {types_->get_int32(), types_->get_int64(), types_->get_string()}, - /*context_id=*/-1}}, - FunctionOptions().set_supports_order_by(true)); - catalog_->AddOwnedFunction(function); - - // Adds fn_agg_string_string_collation(STRING, STRING) -> INT64. - // Enables uses_operation_collation to test collation resolution. - function = new Function( - "fn_agg_string_string_collation", "sample_functions", Function::AGGREGATE, - {{types_->get_int64(), - {types_->get_string(), types_->get_string()}, - /*context_id=*/-1, - FunctionSignatureOptions().set_uses_operation_collation(true)}}, - FunctionOptions(FunctionOptions::ORDER_UNSUPPORTED, - /*window_framing_support_in=*/true)); - catalog_->AddOwnedFunction(function); - - // Adds fn_reject_collation(STRING, ANY TYPE) -> INT64. - // Enables rejects_collation to test collation resolution. - function = - new Function("fn_reject_collation", "sample_functions", Function::SCALAR, - {{types_->get_int64(), - {types_->get_string(), - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("second_arg", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}}, - /*context_id=*/-1, - FunctionSignatureOptions().set_rejects_collation(true)}}); - catalog_->AddOwnedFunction(function); -} - -void SampleCatalog::LoadFunctions2() { - // Add the following test analytic functions. All functions have the same - // list of function signatures: - // arguments: (), (ARG_TYPE_ANY_1) and (, )) - // return: - // - // They differ in the window support: - // --------------------------------------------------------------------------- - // Mode ORDER BY Window Frame Null Handling - // Modifier - // -------- ----------- --------------------------- - // afn_order ANALYTIC Required Unsupported Unsupported - // afn_no_order_no_frame ANALYTIC Unsupported Unsupported Unsupported - // afn_agg AGGREGATE Optional Supported Unsupported - // afn_null_handling AGGREGATE Optional Unsupported Supported - // -------------------------------------------------------------------------- - std::vector function_signatures; - function_signatures.push_back({types_->get_int64(), {}, /*context_id=*/-1}); - function_signatures.push_back( - {types_->get_int64(), {ARG_TYPE_ANY_1}, /*context_id=*/-1}); - function_signatures.push_back( - {types_->get_int64(), - {types_->get_int64(), - FunctionArgumentType(types_->get_string(), - FunctionArgumentTypeOptions().set_argument_name( - "weight", kPositionalOrNamed))}, - -1 /* context */}); - - Function* function = new Function( - "afn_order", "sample_functions", Function::ANALYTIC, function_signatures, - FunctionOptions(FunctionOptions::ORDER_REQUIRED, - /*window_framing_support_in=*/false)); - catalog_->AddOwnedFunction(function); - - function = new Function("afn_no_order_no_frame", "sample_functions", - Function::ANALYTIC, function_signatures, - FunctionOptions(FunctionOptions::ORDER_UNSUPPORTED, - /*window_framing_support_in=*/false)); - catalog_->AddOwnedFunction(function); - - function = new Function("afn_agg", "sample_functions", Function::AGGREGATE, - function_signatures, - FunctionOptions(FunctionOptions::ORDER_OPTIONAL, - /*window_framing_support_in=*/true)); - catalog_->AddOwnedFunction(function); - - function = new Function("afn_null_handling", "sample_functions", - Function::AGGREGATE, function_signatures, - FunctionOptions(FunctionOptions::ORDER_OPTIONAL, - /*window_framing_support_in=*/false) - .set_supports_order_by(true) - .set_supports_limit(true) - .set_supports_null_handling_modifier(true)); - catalog_->AddOwnedFunction(function); - - // NULL_OF_TYPE(string) -> (a NULL of type matching the named simple type). - // This is testing resolving functions where the return type is determined - // dynamically based on literal values of the arguments. - // The callback overrides the INT64 return type in the signature. - function = new Function( - "null_of_type", "sample_functions", Function::SCALAR, - {{{types::Int64Type()}, {types::StringType()}, /*context_id=*/-1}}, - FunctionOptions().set_compute_result_type_callback( - &ComputeResultTypeCallbackForNullOfType)); - catalog_->AddOwnedFunction(function); - - catalog_->AddOwnedFunction(new Function( - "safe_supported_function", "sample_functions", Function::SCALAR, - {{types_->get_int64(), {}, /*context_id=*/-1}}, FunctionOptions())); - - catalog_->AddOwnedFunction(new Function( - "safe_unsupported_function", "sample_functions", Function::SCALAR, - {{types_->get_int64(), {}, /*context_id=*/-1}}, - FunctionOptions().set_supports_safe_error_mode(false))); - - // Add a function that triggers a deprecation warning. - function = - new Function("deprecation_warning", "sample_functions", Function::SCALAR); - - FunctionSignature deprecation_warning_signature( - types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); - deprecation_warning_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/1)}); - function->AddSignature(deprecation_warning_signature); - catalog_->AddOwnedFunction(function); - - function = new Function("deprecation_warning2", "sample_functions", - Function::SCALAR); - FunctionSignature deprecation_warning2_signature( - types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); - deprecation_warning2_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/2)}); - function->AddSignature(deprecation_warning2_signature); - catalog_->AddOwnedFunction(function); - - // Add a function that triggers two deprecation warnings with the same kind. - function = new Function("two_deprecation_warnings_same_kind", - "sample_functions", Function::SCALAR); - - FunctionSignature two_deprecation_warnings_same_kind_signature( - types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); - two_deprecation_warnings_same_kind_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/2), CreateDeprecationWarning(/*id=*/3)}); - function->AddSignature(two_deprecation_warnings_same_kind_signature); - catalog_->AddOwnedFunction(function); - - // Add a function that triggers two deprecation warnings with different kinds. - function = new Function("two_deprecation_warnings", "sample_functions", - Function::SCALAR); - FunctionSignature two_deprecation_warnings_signature( - types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); - two_deprecation_warnings_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/4), - CreateDeprecationWarning( - /*id=*/5, DeprecationWarning::DEPRECATED_FUNCTION_SIGNATURE)}); - function->AddSignature(two_deprecation_warnings_signature); - catalog_->AddOwnedFunction(function); - - catalog_->AddOwnedFunction(new Function( - "anon_non_anon", "sample_functions", Function::SCALAR, - {{types_->get_int64(), {}, /*context_id=*/-1}}, FunctionOptions())); - - function = new AnonFunction( - "anon_test", "sample_functions", - {{types_->get_int64(), - {/*expr=*/types_->get_int64(), - /*lower_bound=*/{types_->get_int64(), FunctionArgumentType::OPTIONAL}, - /*upper_bound=*/{types_->get_int64(), FunctionArgumentType::OPTIONAL}}, - /*context_id=*/-1}}, - FunctionOptions() - .set_supports_clamped_between_modifier(true) - .set_supports_over_clause(false), - "sum"); - catalog_->AddOwnedFunction(function); - - // Add a function that takes two named arguments with one signature. - const auto named_required_format_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( - "format_string", kPositionalOrNamed)); - const auto named_required_date_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( - "date_string", kPositionalOrNamed)); - const auto named_required_format_arg_error_if_positional = - FunctionArgumentType(types_->get_string(), - FunctionArgumentTypeOptions().set_argument_name( - "format_string", kNamedOnly)); - const auto named_required_date_arg_error_if_positional = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( - "date_string", kNamedOnly)); - const auto named_optional_date_arg_error_if_positional = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("date_string", kNamedOnly)); - const auto named_optional_format_arg = FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("format_string", kPositionalOrNamed)); - const auto named_optional_date_arg = FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("date_string", kPositionalOrNamed)); - const auto named_optional_const_format_arg = FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_be_constant() - .set_argument_name("format_string", kPositionalOrNamed)); - const auto non_named_required_format_arg = - FunctionArgumentType(types_->get_string(), FunctionArgumentTypeOptions()); - const auto non_named_required_date_arg = - FunctionArgumentType(types_->get_string(), FunctionArgumentTypeOptions()); - const auto non_named_optional_format_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)); - const auto non_named_optional_date_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)); - const auto named_optional_arg_named_not_null = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_be_non_null() - .set_argument_name("arg", kNamedOnly)); - const auto named_rounding_mode = - FunctionArgumentType(types::RoundingModeEnumType(), - FunctionArgumentTypeOptions().set_argument_name( - "rounding_mode", kPositionalOrNamed)); - - const auto mode = Function::SCALAR; - - function = new Function("fn_named_args", "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {named_required_format_arg, named_required_date_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - function = new Function("fn_const_named_arg", "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {named_optional_const_format_arg, named_optional_date_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add functions with two named optional/repeated arguments on one signature. - function = new Function("fn_named_args_optional", "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {named_optional_format_arg, named_optional_date_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes two named arguments with two signatures with - // optional argument types. - function = - new Function("fn_named_args_two_signatures", "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {named_required_format_arg, named_required_date_arg}, - /*context_id=*/-1}); - function->AddSignature({types_->get_bool(), - {named_required_date_arg, named_required_format_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function that takes two non-named arguments and one named argument in - // each of two signatures. - function = new Function("fn_three_named_args_two_signatures", - "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {non_named_required_format_arg, non_named_required_date_arg, - named_required_format_arg}, - /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), - {non_named_required_format_arg, non_named_required_date_arg, - named_required_date_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with two named arguments where neither may be specified - // positionally. - function = new Function("fn_named_args_error_if_positional", - "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {named_required_format_arg_error_if_positional, - named_required_date_arg_error_if_positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with two named arguments where the first may not be - // specified positionally. - function = new Function("fn_named_args_error_if_positional_first_arg", - "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {named_required_format_arg_error_if_positional, named_required_date_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with two named arguments where the second may not be - // specified positionally. - function = new Function("fn_named_args_error_if_positional_second_arg", - "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {named_required_format_arg, named_required_date_arg_error_if_positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with two named arguments, one required and one optional, - // and neither may be specified positionally. - function = new Function("fn_named_optional_args_error_if_positional", - "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {named_required_format_arg_error_if_positional, - named_optional_date_arg_error_if_positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with two optional arguments, one regular and one named that - // cannot be specified positionally. - function = - new Function("fn_optional_named_optional_args", "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {{types_->get_string(), FunctionArgumentType::OPTIONAL}, - named_optional_date_arg_error_if_positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with three optional arguments, one regular and two named, - // both cannot be specified positionally and first cannot be NULL. - function = new Function("fn_optional_named_optional_not_null_args", - "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {{types_->get_string(), FunctionArgumentType::OPTIONAL}, - named_optional_arg_named_not_null, - named_optional_date_arg_error_if_positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with two signatures, one using regular arguments and one - // using named arguments that cannot be specified positionally. - function = - new Function("fn_regular_and_named_signatures", "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {{types_->get_string(), FunctionArgumentType::REQUIRED}, - {types_->get_string(), FunctionArgumentType::OPTIONAL}}, - /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), - {{types_->get_string(), FunctionArgumentType::REQUIRED}, - named_optional_date_arg_error_if_positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add a function with one unnamed and two named STRING arguments and - // returning a STRING. - function = new Function("fn_named_arguments_returns_string", - "sample_functions", mode); - function->AddSignature( - {types_->get_string(), - {{types_->get_string(), FunctionArgumentType::REQUIRED}, - named_required_format_arg, - named_required_date_arg}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // A FunctionSignatureArgumentConstraintsCallback that checks for NULL - // arguments. - auto sanity_check_nonnull_arg_constraints = - [](const FunctionSignature& signature, - absl::Span arguments) -> std::string { - ABSL_CHECK(signature.IsConcrete()); - ABSL_CHECK_EQ(signature.NumConcreteArguments(), arguments.size()); - for (int i = 0; i < arguments.size(); ++i) { - ABSL_CHECK(arguments[i].type()->Equals(signature.ConcreteArgumentType(i))); - if (arguments[i].is_null()) { - if (signature.ConcreteArgument(i).has_argument_name()) { - return absl::StrCat("Argument `", - signature.ConcreteArgument(i).argument_name(), - "`: NULL argument is not allowed"); - } else { - return absl::StrCat("Argument ", i + 1, - ": NULL argument is not allowed"); - } - } - } - return ""; - }; - - // A PostResolutionArgumentConstraintsCallback that restricts all the provided - // INT64 arguments to be nonnegative if they are literals. - auto post_resolution_arg_constraints = - [](const FunctionSignature& signature, - absl::Span arguments, - const LanguageOptions& language_options) -> absl::Status { - for (int i = 0; i < arguments.size(); ++i) { - ABSL_CHECK(arguments[i].type()->Equals(signature.ConcreteArgumentType(i))); - if (!arguments[i].type()->IsInt64() || !arguments[i].is_literal()) { - continue; - } - if (arguments[i].literal_value()->int64_value() < 0) { - return MakeSqlError() - << "Argument " - << (signature.ConcreteArgument(i).has_argument_name() - ? signature.ConcreteArgument(i).argument_name() - : std::to_string(i + 1)) - << " must not be negative"; - } - } - return absl::OkStatus(); - }; - - // Add a function with an argument constraint that verifies the concrete - // arguments in signature matches the input argument list, and rejects - // any NULL arguments. - function = - new Function("fn_named_opt_args_nonnull_nonnegative_constraints", - "sample_functions", mode, - FunctionOptions().set_post_resolution_argument_constraint( - post_resolution_arg_constraints)); - FunctionSignature signature_with_constraints{ - types_->get_bool(), - {{types_->get_string(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1_string", kPositionalOrNamed)}, - {types_->get_int64(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_argument_name("o2_int64", kPositionalOrNamed)}, - {types_->get_double(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_argument_name("o3_double", kPositionalOrNamed)}}, - /*context_id=*/-1, - FunctionSignatureOptions().set_constraints( - sanity_check_nonnull_arg_constraints)}; - function->AddSignature(signature_with_constraints); - catalog_->AddOwnedFunction(function); - - // Similar as the previous function, but the arguments are unnamed. - function = - new Function("fn_unnamed_opt_args_nonnull_nonnegative_constraints", - "sample_functions", mode, - FunctionOptions().set_post_resolution_argument_constraint( - post_resolution_arg_constraints)); - FunctionSignature signature_with_unnamed_args_constraints{ - types_->get_bool(), - {{types_->get_string(), FunctionArgumentType::OPTIONAL}, - {types_->get_int64(), FunctionArgumentType::OPTIONAL}, - {types_->get_double(), FunctionArgumentType::OPTIONAL}}, - /*context_id=*/-1, - FunctionSignatureOptions().set_constraints( - sanity_check_nonnull_arg_constraints)}; - function->AddSignature(signature_with_unnamed_args_constraints); - catalog_->AddOwnedFunction(function); - - // Adds a templated function that generates its result type via the callback. - function = new Function("fn_result_type_from_arg", "sample_functions", mode, - FunctionOptions().set_compute_result_type_callback( - &ComputeResultTypeFromStringArgumentValue)); - function->AddSignature( - {{types_->get_string()}, - {{types_->get_string(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kPositionalOrNamed)}, - {types_->get_string(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_argument_name("type_name", kPositionalOrNamed)}}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Function with signatures that should not show up in signature mismatch - // error messages. - function = new Function("fn_with_hidden_signatures", "sample_functions", mode, - FunctionOptions()); - // Two good signatures - function->AddSignature({{types_->get_string()}, - {{types_->get_int64()}}, - /*context_id=*/-1}); - function->AddSignature({{types_->get_string()}, - {{types_->get_int32()}}, - /*context_id=*/-1}); - // Two hidden signatures - FunctionSignature deprecated_signature{{types_->get_string()}, - {{types_->get_string()}}, - /*context_id=*/-1}; - deprecated_signature.SetIsDeprecated(true); - function->AddSignature(deprecated_signature); - FunctionSignature internal_signature{ - {types_->get_string()}, - {{types_->get_string()}, {types_->get_string()}}, - /*context_id=*/-1, - FunctionSignatureOptions().set_is_internal(true)}; - function->AddSignature(internal_signature); - catalog_->AddOwnedFunction(function); - - // Adds a function accepting a Sequence argument. - FunctionOptions function_options; - std::function(const absl::Span)> - evaluator = [&](absl::Span args) -> absl::StatusOr { - return Value::Int64(1); - }; - function_options.set_evaluator(FunctionEvaluator(evaluator)); - - function = new Function("fn_with_sequence_arg", "sample_functions", mode, - function_options); - function->AddSignature({{types_->get_int64()}, - {FunctionArgumentType::AnySequence()}, - /*context_id=*/-1}); - // Adds a function signature for accepting both a Sequence argument and a - // lambda to ensure one doesn't break the other. - function->AddSignature( - {{types_->get_int64()}, - {ARG_TYPE_ANY_1, FunctionArgumentType::AnySequence(), - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2)}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - // Add function with constant expression argument. - function = new Function("fn_with_constant_expr_arg", "sample_functions", - Function::SCALAR); - const auto string_const_expression_arg = zetasql::FunctionArgumentType( - types_->get_string(), zetasql::FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::REQUIRED) - .set_must_be_constant_expression()); - function->AddSignature( - {types_->get_bool(), {string_const_expression_arg}, /*context_id=*/-1}); - catalog_->AddOwnedFunction(function); - - { - auto function = std::make_unique("fn_with_named_rounding_mode", - "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {named_rounding_mode}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // Function with non-concrete return type but with a - // ComputeResultTypeCallback. Calling this function should not cause function - // resolver to complain the signature is not concrete. - { - auto function = std::make_unique( - "non_concrete_return_type_with_compute_result_type_callback", - "sample_functions", mode, - FunctionOptions().set_compute_result_type_callback( - &ComputeResultTypeCallbackToStruct)); - const FunctionArgumentType positional(ARG_TYPE_ANY_1); - function->AddSignature({ARG_TYPE_ARBITRARY, - {positional, positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // Arguments and return type are both ARG_TYPE_ANY_3. - { - auto function = std::make_unique("fn_with_arg_type_any_3", - "sample_functions", mode); - const FunctionArgumentType positional(ARG_TYPE_ANY_3); - function->AddSignature({ARG_TYPE_ANY_3, - {positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_TYPE_ANY_3 arguments + ARRAY_ARG_TYPE_ANY_3 result. - { - auto function = std::make_unique("fn_arg_type_any_3_array_result", - "sample_functions", mode); - const FunctionArgumentType optional( - ARG_TYPE_ANY_3, FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kPositionalOrNamed)); - function->AddSignature({ARG_ARRAY_TYPE_ANY_3, - {optional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_ARRAY_TYPE_ANY_3 arguments + ARG_TYPE_ANY_3 result. - { - auto function = std::make_unique( - "fn_arg_type_array_any_3_result_type_any_3", "sample_functions", mode); - const FunctionArgumentType named_optional( - ARG_ARRAY_TYPE_ANY_3, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kNamedOnly)); - function->AddSignature({ARG_TYPE_ANY_3, - {named_optional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_ARRAY_TYPE_ANY_3 arguments + ARG_ARRAY_TYPE_ANY_3 result. - { - auto function = std::make_unique( - "fn_repeated_array_any_3_return_type_array_any_3", "sample_functions", - mode); - const FunctionArgumentType repeated( - ARG_ARRAY_TYPE_ANY_3, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REPEATED)); - function->AddSignature({ARG_ARRAY_TYPE_ANY_3, - {repeated}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // Arguments and return type are both ARG_TYPE_ANY_4. - { - auto function = std::make_unique("fn_with_arg_type_any_4", - "sample_functions", mode); - const FunctionArgumentType positional(ARG_TYPE_ANY_4); - function->AddSignature({ARG_TYPE_ANY_4, - {positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_TYPE_ANY_4 arguments + ARRAY_ARG_TYPE_ANY_4 result. - { - auto function = std::make_unique("fn_arg_type_any_4_array_result", - "sample_functions", mode); - const FunctionArgumentType optional( - ARG_TYPE_ANY_4, FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kPositionalOrNamed)); - function->AddSignature({ARG_ARRAY_TYPE_ANY_4, - {optional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_ARRAY_TYPE_ANY_4 arguments + ARG_TYPE_ANY_4 result. - { - auto function = std::make_unique( - "fn_arg_type_array_any_4_result_type_any_4", "sample_functions", mode); - const FunctionArgumentType named_optional( - ARG_ARRAY_TYPE_ANY_4, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kNamedOnly)); - function->AddSignature({ARG_TYPE_ANY_4, - {named_optional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_ARRAY_TYPE_ANY_4 arguments + ARG_ARRAY_TYPE_ANY_4 result. - { - auto function = std::make_unique( - "fn_repeated_array_any_4_return_type_array_any_4", "sample_functions", - mode); - const FunctionArgumentType repeated( - ARG_ARRAY_TYPE_ANY_4, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REPEATED)); - function->AddSignature({ARG_ARRAY_TYPE_ANY_4, - {repeated}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // Arguments and return type are both ARG_TYPE_ANY_5. - { - auto function = std::make_unique("fn_with_arg_type_any_5", - "sample_functions", mode); - const FunctionArgumentType positional(ARG_TYPE_ANY_5); - function->AddSignature({ARG_TYPE_ANY_5, - {positional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_TYPE_ANY_5 arguments + ARRAY_ARG_TYPE_ANY_5 result. - { - auto function = std::make_unique("fn_arg_type_any_5_array_result", - "sample_functions", mode); - const FunctionArgumentType optional( - ARG_TYPE_ANY_5, FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kPositionalOrNamed)); - function->AddSignature({ARG_ARRAY_TYPE_ANY_5, - {optional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_ARRAY_TYPE_ANY_5 arguments + ARG_TYPE_ANY_5 result. - { - auto function = std::make_unique( - "fn_arg_type_array_any_5_result_type_any_5", "sample_functions", mode); - const FunctionArgumentType named_optional( - ARG_ARRAY_TYPE_ANY_5, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("o1", kNamedOnly)); - function->AddSignature({ARG_TYPE_ANY_5, - {named_optional}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // ARG_ARRAY_TYPE_ANY_5 arguments + ARG_ARRAY_TYPE_ANY_5 result. - { - auto function = std::make_unique( - "fn_repeated_array_any_5_return_type_array_any_5", "sample_functions", - mode); - const FunctionArgumentType repeated( - ARG_ARRAY_TYPE_ANY_5, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REPEATED)); - function->AddSignature({ARG_ARRAY_TYPE_ANY_5, - {repeated}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - - // Regular non-aggregate function with argument alias support. - { - auto function = std::make_unique("fn_for_argument_alias", - "sample_functions", mode); - // Signature with alias on an optional argument. - const FunctionArgumentType aliased_1( - types_->get_bool(), - FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - const FunctionArgumentType non_aliased_1(types_->get_bool()); - const FunctionArgumentType optional_arg( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED)); - function->AddSignature({types_->get_bool(), - {aliased_1, non_aliased_1, optional_arg}, - /*context_id=*/-1}); - - // Signature with alias on a repeated argument. - const FunctionArgumentType aliased_2( - types_->get_string(), - FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - const FunctionArgumentType non_aliased_2(types_->get_bool()); - const FunctionArgumentType repeated( - types_->get_int64(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::REPEATED) - .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED)); - function->AddSignature({types_->get_bool(), - {aliased_2, non_aliased_2, repeated}, - /*context_id=*/-1}); - - catalog_->AddOwnedFunction(std::move(function)); - } - // Aggregate function with argument alias support. - { - FunctionArgumentType aliased( - ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - FunctionArgumentType non_aliased(ARG_TYPE_ANY_2); - std::vector function_signatures = { - {types_->get_int64(), {aliased, non_aliased}, /*context_id=*/-1}}; - catalog_->AddOwnedFunction( - new Function("aggregate_fn_for_argument_alias", "sample_functions", - Function::AGGREGATE, function_signatures)); - } - // Analytic functions with argument alias support. - { - FunctionArgumentType aliased( - ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - FunctionArgumentType non_aliased(ARG_TYPE_ANY_2); - std::vector function_signatures = { - {types_->get_int64(), {aliased, non_aliased}, /*context_id=*/-1}}; - catalog_->AddOwnedFunction( - new Function("analytic_fn_for_argument_alias", "sample_functions", - Function::ANALYTIC, function_signatures, - FunctionOptions(FunctionOptions::ORDER_REQUIRED, - /*window_framing_support_in=*/false))); - } - - // Function with argument aliases and ComputeReturnTypeCallback. - { - auto function = std::make_unique( - "fn_to_struct_with_optional_aliases", "sample_functions", mode, - FunctionOptions().set_compute_result_type_callback( - &ComputeResultTypeCallbackToStructUseArgumentAliases)); - // Signature where no argument aliases are allowed. - { - const FunctionArgumentType arg_1(types_->get_bool()); - const FunctionArgumentType arg_2(types_->get_bool()); - function->AddSignature({ARG_TYPE_ARBITRARY, - {arg_1, arg_2}, - /*context_id=*/-1}); - } - // Signature where one of the arguments can have an alias. - { - const FunctionArgumentType arg_1( - types_->get_int64(), - FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - const FunctionArgumentType arg_2(types_->get_int64()); - function->AddSignature({ARG_TYPE_ARBITRARY, - {arg_1, arg_2}, - /*context_id=*/-1}); - } - // Signature where both the arguments can have aliases. - { - const FunctionArgumentType arg_1( - types_->get_string(), - FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - const FunctionArgumentType arg_2( - types_->get_string(), - FunctionArgumentTypeOptions().set_argument_alias_kind( - FunctionEnums::ARGUMENT_ALIASED)); - function->AddSignature({ARG_TYPE_ARBITRARY, - {arg_1, arg_2}, - /*context_id=*/-1}); - } - // Signature with optional arguments. - { - const FunctionArgumentType optional_supports_alias( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("default_string"))); - const FunctionArgumentType optional_not_supports_alias( - types_->get_int64(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int64(100))); - function->AddSignature( - {ARG_TYPE_ARBITRARY, - {optional_supports_alias, optional_not_supports_alias}, - /*context_id=*/-1}); - } - // Signature with repeated arguments. - { - // Use one required positional arguments to avoid ambiguity. - const FunctionArgumentType positional(types_->get_bytes()); - const FunctionArgumentType repeated_supports_alias( - types_->get_int64(), - FunctionArgumentTypeOptions() - .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED) - .set_cardinality(FunctionArgumentType::REPEATED)); - const FunctionArgumentType repeated_not_supports_alias( - types_->get_string(), FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REPEATED)); - function->AddSignature( - {ARG_TYPE_ARBITRARY, - {positional, repeated_supports_alias, repeated_not_supports_alias}, - /*context_id=*/-1}); - } - catalog_->AddOwnedFunction(std::move(function)); - } - // Window function with named lambda. - { - FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions().set_argument_name("named_lambda", - kNamedOnly)); - Function* function = - new Function("afn_named_lambda", "sample_functions", Function::ANALYTIC, - {{ARG_TYPE_ANY_1, - {ARG_TYPE_ANY_1, named_lambda}, - /*context_id=*/-1}}, - FunctionOptions(FunctionOptions::ORDER_REQUIRED, - /*window_framing_support_in=*/false)); - catalog_->AddOwnedFunction(function); - } - // Scalar function with named lambda. - { - FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions().set_argument_name("named_lambda", - kNamedOnly)); - Function* function = - new Function("fn_named_lambda", "sample_functions", Function::SCALAR, - {{ARG_TYPE_ANY_1, - {ARG_TYPE_ANY_1, named_lambda}, - /*context_id=*/-1}}); - catalog_->AddOwnedFunction(function); - } - // Aggregate function with named lambda. - { - FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("named_lambda", kNamedOnly) - .set_is_not_aggregate()); - Function* function = new Function("fn_aggregate_named_lambda", - "sample_functions", Function::AGGREGATE, - {{ARG_TYPE_ANY_1, - {ARG_TYPE_ANY_1, named_lambda}, - /*context_id=*/-1}}); - catalog_->AddOwnedFunction(function); - } - // Function with multiple named arguments, including named lambdas. This - // function can be used to verify that named lambdas can be specified in any - // order. - { - FunctionArgumentType named_1 = FunctionArgumentType( - ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_name( - "named_1", kPositionalOrNamed)); - FunctionArgumentType named_2 = FunctionArgumentType( - ARG_TYPE_ANY_2, FunctionArgumentTypeOptions().set_argument_name( - "named_2", kPositionalOrNamed)); - FunctionArgumentType named_only_lambda = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1, ARG_TYPE_ANY_2}, ARG_TYPE_ANY_3, - FunctionArgumentTypeOptions().set_argument_name("named_lambda", - kPositionalOrNamed)); - std::vector function_signatures = { - {ARG_TYPE_ANY_3, - {named_1, named_2, named_only_lambda}, - /*context_id=*/-1}}; - catalog_->AddOwnedFunction( - new Function("fn_multiple_named_arguments", "sample_functions", - Function::SCALAR, function_signatures)); - } - // Function with multiple named arguments. - { - FunctionArgumentType named_lambda_1 = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions().set_argument_name("named_lambda_1", - kPositionalOrNamed)); - FunctionArgumentType named_lambda_2 = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_3, - FunctionArgumentTypeOptions().set_argument_name("named_lambda_2", - kPositionalOrNamed)); - std::vector function_signatures = { - {ARG_TYPE_ANY_3, - {ARG_TYPE_ANY_1, named_lambda_1, named_lambda_2}, - /*context_id=*/-1}}; - catalog_->AddOwnedFunction( - new Function("fn_multiple_named_lambda_arguments", "sample_functions", - Function::SCALAR, function_signatures)); - } - // Function with signatures having a custom ComputeResultAnnotationsCallback. - { - auto function = std::make_unique("fn_custom_annotation_callback", - "sample_functions", mode); - // Signature with custom annotation callback. - const StructType* struct_type; - ZETASQL_CHECK_OK(type_factory()->MakeStructType( - {{"key", type_factory()->get_string()}, - {"value", type_factory()->get_string()}}, - &struct_type)); - function->AddSignature( - {struct_type, - {types_->get_string(), types_->get_string()}, - /*context_id=*/-1, - FunctionSignatureOptions().set_compute_result_annotations_callback( - &ComputeResultAnnotationsCallbackToStruct)}); - // Signature without custom annotation callback. - function->AddSignature( - {struct_type, - {types_->get_int64(), types_->get_string(), types_->get_string()}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function with array input and a custom ComputeResultAnnotationsCallback. - { - auto function = std::make_unique( - "fn_custom_annotation_callback_array_arg", "sample_functions", mode); - const Type* array_string_type = nullptr; - const Type* result_array_type = nullptr; - { - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_string(), &array_string_type)); - const StructType* struct_type; - ZETASQL_CHECK_OK(type_factory()->MakeStructType( - {{"key", type_factory()->get_string()}, - {"value", type_factory()->get_string()}}, - &struct_type)); - ZETASQL_CHECK_OK(types_->MakeArrayType(struct_type, &result_array_type)); - } - // Signature with custom annotation callback. - function->AddSignature( - {result_array_type, - {array_string_type, array_string_type}, - /*context_id=*/-1, - FunctionSignatureOptions().set_compute_result_annotations_callback( - &ComputeResultAnnotationsCallbackArraysToArrayOfStruct)}); - // Signature without custom annotation callback. - function->AddSignature( - {result_array_type, - {types_->get_int64(), array_string_type, array_string_type}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function that throws an error when input arguments have annotations. - { - auto function = std::make_unique( - "fn_custom_annotation_callback_sql_error", "sample_functions", mode); - function->AddSignature( - {types_->get_string(), - {types_->get_string(), types_->get_string()}, - /*context_id=*/-1, - FunctionSignatureOptions().set_compute_result_annotations_callback( - &ComputeResultAnnotationsCallbackSqlError)}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function with generic argument list with custom annotation callback. - { - auto function = std::make_unique( - "fn_custom_annotation_with_generic_argument_list", "sample_functions", - mode); - FunctionArgumentType lambda = FunctionArgumentType::Lambda( - {types_->get_string()}, types_->get_string()); - function->AddSignature( - {types_->get_string(), - {lambda, types_->get_string(), types_->get_string()}, - /*context_id=*/-1, - FunctionSignatureOptions().set_compute_result_annotations_callback( - &ComputeResultAnnotationsCallbackUseTheFinalAnnotation)}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking an array of any element type and returning the element - // type. - { - auto function = std::make_unique( - "fn_req_any_array_returns_any_arg", "sample_functions", mode); - function->AddSignature({ARG_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking a map type and returning the key arg type - { - auto function = std::make_unique( - "fn_map_type_any_1_2_return_type_any_1", "sample_functions", mode); - function->AddSignature({ARG_TYPE_ANY_1, - {ARG_MAP_TYPE_ANY_1_2}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking a map type and returning the value arg type - { - auto function = std::make_unique( - "fn_map_type_any_1_2_return_type_any_2", "sample_functions", mode); - function->AddSignature({ARG_TYPE_ANY_2, - {ARG_MAP_TYPE_ANY_1_2}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking a map type and optional key arg type, and returning value - // arg type. - // Models MAP_GET(). - { - auto function = std::make_unique( - "fn_req_map_type_any_1_2_req_any_1_opt_any_2_returns_any_2", - "sample_functions", mode); - function->AddSignature({ARG_TYPE_ANY_2, - {ARG_MAP_TYPE_ANY_1_2, - ARG_TYPE_ANY_1, - {ARG_TYPE_ANY_2, FunctionEnums::OPTIONAL}}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking ANY_1 and ANY_2 types, and returning a MAP_TYPE_ANY_1_2 - // arg type. - { - auto function = std::make_unique( - "fn_any_1_any_2_return_map_type_any_1_2", "sample_functions", mode); - function->AddSignature({ARG_MAP_TYPE_ANY_1_2, - {ARG_TYPE_ANY_1, ARG_TYPE_ANY_2}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking a MAP_TYPE_ANY_1_2 arg type, and returning a bool. - { - auto function = std::make_unique( - "fn_map_type_any_1_2_any_2_return_bool", "sample_functions", mode); - function->AddSignature({types_->get_bool(), - {ARG_MAP_TYPE_ANY_1_2, ARG_TYPE_ANY_2}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking a bool, and returning a bool. Requires that V_1_4_MAP_TYPE - // is enabled. - { - auto function = std::make_unique("fn_requires_map_type", - "sample_functions", mode); - function->AddSignature( - {types_->get_bool(), - {types_->get_bool()}, - /*context_id=*/-1, - FunctionSignatureOptions().AddRequiredLanguageFeature( - FEATURE_V_1_4_MAP_TYPE)}); - catalog_->AddOwnedFunction(std::move(function)); - } - // Function taking a bool, and returning a bool. Requires that V_1_4_MAP_TYPE - // is enabled. - { - auto function = std::make_unique( - "fn_requires_map_type_for_bool_signature", "sample_functions", mode); - function->AddSignature( - {types_->get_string(), {types_->get_string()}, /*context_id=*/-1}); - function->AddSignature( - {types_->get_bool(), - {types_->get_bool()}, - /*context_id=*/-1, - FunctionSignatureOptions().AddRequiredLanguageFeature( - FEATURE_V_1_4_MAP_TYPE)}); - catalog_->AddOwnedFunction(std::move(function)); - } -} // NOLINT(readability/fn_size) - -void SampleCatalog::LoadFunctionsWithDefaultArguments() { - // Adds an scalar function that takes multiple optional named arguments with - // some of them having default values. - Function* function = new Function( - "fn_optional_named_default_args", "sample_functions", Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("a0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("o1_default"))}, - {types_->get_double(), - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Double(0.2))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - - // Similar to the one above, but the optional arguments are templated. - function = new Function( - "fn_optional_named_default_args_templated", "sample_functions", - Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("a0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("o0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("o1_default"))}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int32(2))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - - // Similar to the one above, but the default argument value is -2 rather than - // 2. - function = new Function( - "fn_optional_named_default_args_templated_negative", "sample_functions", - Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("a0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("o0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("o1_default"))}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int32(-2))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - - // Adds an scalar function that takes multiple unnamed optional arguments with - // some of them having default values. - function = new Function( - "fn_optional_unnamed_default_args", "sample_functions", Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("o1_default"))}, - {types_->get_double(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Double(0.3))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - - // Adds an scalar function that takes (concrete typed, templated type). See - // b/333445090. - function = new Function( - "fn_optional_unnamed_default_any_args", "sample_functions", - Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("abc"))}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("def"))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - - // Similar to the one above, but the optional arguments are templated. - function = new Function( - "fn_optional_unnamed_default_args_templated", "sample_functions", - Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("o1_default"))}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int32(5))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - - // A scalar function with both named and unnamed optional arguments plus the - // last argument with a default value. - function = new Function( - "fn_req_opt_unnamed_named_default", "sample_functions", Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("dv"))}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - ZETASQL_CHECK_OK(function->signatures()[0].IsValid(ProductMode::PRODUCT_EXTERNAL)); - catalog_->AddOwnedFunction(function); - - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_optional_named_default_args"}, - FunctionSignature( - /*result_type=*/ARG_TYPE_RELATION, - /*arguments=*/ - { - {ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("relation", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {types_->get_bool(), - FunctionArgumentTypeOptions() - .set_argument_name("r1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {types_->get_double(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Double(3.14))}, - {types_->get_uint32(), - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Uint32(10086))}, - }, - /*context_id=*/-1))); - - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_optional_named_default_args_templated"}, - FunctionSignature( - /*result_type=*/ARG_TYPE_RELATION, - /*arguments=*/ - { - {ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("relation", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {types_->get_bool(), - FunctionArgumentTypeOptions() - .set_argument_name("r1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::REQUIRED)}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("o0", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Double(3.14))}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("abc"))}, - }, - /*context_id=*/-1))); - - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_optional_unnamed_default_args"}, - FunctionSignature( - /*result_type=*/ARG_TYPE_RELATION, - /*arguments=*/ - { - {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_bool(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {types_->get_float(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Float(0.618))}, - {types_->get_uint32(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Uint32(168))}, - }, - /*context_id=*/-1))); - - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_optional_unnamed_default_args_templated"}, - FunctionSignature( - /*result_type=*/ARG_TYPE_RELATION, - /*arguments=*/ - { - {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_bool(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::String("xyz"))}, - {ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int32(-1))}, - }, - /*context_id=*/-1))); - - // A TVF with a combined of named and unnamed optional arguments. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_optional_unnamed_named"}, - FunctionSignature( - /*result_type=*/ARG_TYPE_RELATION, - /*arguments=*/ - { - {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_bool(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - }, - /*context_id=*/-1))); - - // A TVF with a combined of named and unnamed optional arguments plus a - // default argument at last. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_optional_unnamed_named_default"}, - FunctionSignature( - /*result_type=*/ARG_TYPE_RELATION, - /*arguments=*/ - { - {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_bool(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - {types_->get_int32(), - FunctionArgumentTypeOptions() - .set_argument_name("o2", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int32(314))}, - }, - /*context_id=*/-1))); - - // A scalar function with both unnamed and named optional arguments. - function = new Function( - "fn_req_opt_unnamed_named", "sample_functions", Function::SCALAR, - /*function_signatures=*/ - { - {/*result_type=*/types_->get_int64(), - /*arguments=*/ - { - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::REQUIRED)}, - {types_->get_string(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)}, - {types_->get_string(), - FunctionArgumentTypeOptions() - .set_argument_name("o1", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)}, - }, - /*context_id=*/-1}, - }, - FunctionOptions()); - catalog_->AddOwnedFunction(function); - ZETASQL_CHECK_OK(function->signatures()[0].IsValid(ProductMode::PRODUCT_EXTERNAL)); - - // A scalar function with one optional argument of any type, returning that - // type. - function = new Function("fn_optional_any_arg_returns_any", "sample_functions", - Function::SCALAR, - /*function_signatures=*/ - {{ARG_TYPE_ANY_1, - {{ARG_TYPE_ANY_1, FunctionEnums::OPTIONAL}}, - /*context_id=*/-1}}, - FunctionOptions()); - catalog_->AddOwnedFunction(std::move(function)); - - // A scalar function with one optional argument of any type, returning an - // array with that element type. - function = new Function("fn_optional_any_arg_returns_any_array", - "sample_functions", Function::SCALAR, - /*function_signatures=*/ - {{ARG_ARRAY_TYPE_ANY_1, - {{ARG_TYPE_ANY_1, FunctionEnums::OPTIONAL}}, - /*context_id=*/-1}}, - FunctionOptions()); - catalog_->AddOwnedFunction(std::move(function)); - - // A scalar function with a required array of any type and an optional of any - // type, returning the element type. - function = new Function( - "fn_req_any_array_optional_any_arg_returns_any_arg", "sample_functions", - Function::SCALAR, - /*function_signatures=*/ - {{ARG_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, {ARG_TYPE_ANY_1, FunctionEnums::OPTIONAL}}, - /*context_id=*/-1}}, - FunctionOptions()); - catalog_->AddOwnedFunction(std::move(function)); -} - -void SampleCatalog::LoadTemplatedSQLUDFs() { - // Return an empty struct as the result type for now. - // The function resolver will dynamically compute a different result type at - // analysis time based on the function SQL body. - const FunctionArgumentType result_type(ARG_TYPE_ARBITRARY); - int context_id = 0; - - // Add a UDF with a simple valid templated SQL body. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_one"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, ParseResumeLocation::FromString("1"))); - - // Add a templated SQL function that calls another templated SQL function. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_call_udf_templated_return_one"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, - ParseResumeLocation::FromString("udf_templated_return_one()"))); - - // Add a templated SQL function that calls another templated SQL function - // twice. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_call_udf_templated_return_one_twice"}, - FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, - ParseResumeLocation::FromString("udf_call_udf_templated_return_one() + " - "udf_call_udf_templated_return_one()"))); - - // Add a UDF with a valid templated SQL body that refers to an argument. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_bool_arg"}, - FunctionSignature(result_type, {FunctionArgumentType(types::BoolType())}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a valid templated SQL body that refers to an argument. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_int64_arg"}, - FunctionSignature(result_type, {FunctionArgumentType(types::Int64Type())}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a valid templated SQL body that refers to an argument. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_any_scalar_arg"}, - FunctionSignature(result_type, {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a valid templated SQL body that performs addition on an - // argument. The function signature accepts a single argument of any type. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_arg_plus_integer"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x + 42"))); - - // Add a UDF with a valid templated SQL body that accepts an input argument - // where the name contains '$'. The function signature accepts a single - // argument of any type. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_arg_plus_integer_accept_dollars_col_name"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"$col1"}, - ParseResumeLocation::FromString("`$col1`"))); - - // Add a UDF with a valid templated SQL body that performs concatenation on an - // argument. The function signature accepts a single argument of any type. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_arg_concat_string"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("concat(x, 'abc')"))); - - // Add a UDF with a valid templated SQL body that performs concatenation on - // two arguments. The function signature accepts two arguments of any type. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_arg_concat_two_strings"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x", "y"}, - ParseResumeLocation::FromString("concat(x, y)"))); - - // Add a UDF with a valid templated SQL body that performs a proto field - // access on an argument. The function signature accepts a single argument of - // any type. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_arg_proto_field_access"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("x.int32_field"))); - - // Add an invalid templated SQL function with a parse error in the function - // body. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_parse_error"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, ParseResumeLocation::FromString("a b c d e"))); - - // Add an invalid templated SQL function with an analysis error in the - // function body. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_analysis_error"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, ParseResumeLocation::FromString("'abc' + 42"))); - - // Add a UDF that refers to 'udf_templated_analysis_error' to show two levels - // of nested error messages. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_call_udf_templated_analysis_error"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, - ParseResumeLocation::FromString("udf_templated_analysis_error() + 1"))); - - // Add a UDF that refers to 'udf_call_udf_templated_analysis_error' to show - // three levels of nested error messages. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_call_udf_call_udf_templated_analysis_error"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, - ParseResumeLocation::FromString( - "udf_call_udf_templated_analysis_error() + 1"))); - - // Add a UDF that refers to 'udf_call_udf_call_udf_templated_analysis_error' - // to show four levels of nested error messages. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_call_udf_call_udf_call_udf_templated_analysis_error"}, - FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, - ParseResumeLocation::FromString( - "udf_call_udf_call_udf_templated_analysis_error() + 1"))); - - // Add an invalid templated SQL function that attempts to refer to a query - // parameter. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_function_body_refer_to_parameter"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, - ParseResumeLocation::FromString("@test_param_bool"))); - - // Add an invalid templated SQL function where the function body is empty. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_function_body_empty"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, ParseResumeLocation::FromString(""))); - - // Add an invalid templated SQL function that directly calls itself. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_recursive"}, FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, - ParseResumeLocation::FromString("udf_recursive()"))); - - // Add two invalid templated SQL functions that indirectly call themselves - // through one or more other templated SQL function calls. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_calls_self_indirectly_1"}, - FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, - ParseResumeLocation::FromString("udf_calls_self_indirectly_2()"))); - - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_calls_self_indirectly_2"}, - FunctionSignature(result_type, {}, context_id++), - /*argument_names=*/{}, - ParseResumeLocation::FromString("udf_calls_self_indirectly_1()"))); - - // Add a templated SQL function that calls a templated SQL TVF that calls the - // original templated SQL function again, to make sure cycle detection works. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_calls_tvf_calls_same_udf"}, - FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, - ParseResumeLocation::FromString( - "(select * from tvf_calls_udf_calls_same_tvf())"))); - - // Add a templated SQL function with duplicate argument names. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_duplicate_arg_names"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x", "x"}, - ParseResumeLocation::FromString("concat(x, 'abc')"))); - - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_like_any"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("x like any ('z')"))); - - // Add a templated SQL function that triggers a deprecation warning. - FunctionSignature deprecation_warning_signature(result_type, /*arguments=*/{}, - /*context_id=*/-1); - deprecation_warning_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/101)}); - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_calls_deprecation_warning"}, deprecation_warning_signature, - /*argument_names=*/{}, - ParseResumeLocation::FromString("deprecation_warning()"))); - - // Add a UDF with a simple valid templated SQL body that returns its one input - // argument, and has an expected result type of a 32-bit integer. We will use - // this to test comparing the result type against the expected type when - // testing templated SQL function calls. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_one_templated_arg_return_int32"}, - FunctionSignature(types::Int32Type(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a simple valid templated SQL body that returns its one input - // argument, and has an expected result type of a 64-bit integer. We will use - // this to test comparing the result type against the expected type when - // testing templated SQL function calls. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_one_templated_arg_return_int64"}, - FunctionSignature(types::Int64Type(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a simple valid templated SQL body that returns its one input - // argument, and has an expected result type of a date. We will use this to - // test comparing the result type against the expected type when testing - // templated SQL function calls. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_one_templated_arg_return_date"}, - FunctionSignature(types::DateType(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a simple valid templated SQL body that returns its one input - // argument, and has an expected result type of a struct. We will use - // this to test comparing the result type against the expected type when - // testing templated SQL function calls. - const StructType* return_type = nullptr; - std::vector fields; - fields.emplace_back("int_field", types::Int64Type()); - fields.emplace_back("string_field", types::StringType()); - ZETASQL_CHECK_OK(type_factory()->MakeStructType(fields, &return_type)); - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_one_templated_arg_return_struct_int64_string"}, - FunctionSignature(return_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); - - // Add a UDF with a simple valid templated SQL body that returns a constant - // integer, and has an expected result type of a string. We will use - // this to test comparing the result type against the expected type when - // testing templated SQL function calls. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_42_return_type_string"}, - FunctionSignature(types::StringType(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("42"))); - - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_42_return_type_int32"}, - FunctionSignature(types::Int32Type(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("42"))); - - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_return_999999999999999_return_type_int32"}, - FunctionSignature(types::Int32Type(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("999999999999999"))); - - catalog_->AddOwnedFunction(std::make_unique( - std::vector{"udf_any_and_string_args_return_string_arg"}, - FunctionSignature(FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(types::StringType(), - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/std::vector{"a", "x"}, - ParseResumeLocation::FromString("x"))); - - catalog_->AddOwnedFunction(std::make_unique( - std::vector{"udf_any_and_double_args_return_any"}, - FunctionSignature(FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(types::DoubleType(), - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/std::vector{"a", "x"}, - ParseResumeLocation::FromString("IF(x < 0, 'a', 'b')"))); - - const ArrayType* double_array_type = nullptr; - ZETASQL_CHECK_OK( - type_factory()->MakeArrayType(types::DoubleType(), &double_array_type)); - catalog_->AddOwnedFunction(std::make_unique( - std::vector{"udf_any_and_double_array_args_return_any"}, - FunctionSignature(FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(double_array_type, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/std::vector{"a", "x"}, - ParseResumeLocation::FromString("IF(x[SAFE_OFFSET(0)] < 0, 'a', 'b')"))); - - // Add a SQL UDA with a valid templated SQL body that refers to an aggregate - // argument only. - FunctionArgumentType int64_aggregate_arg_type(types::Int64Type()); - FunctionArgumentTypeOptions agg_options; - agg_options.set_is_not_aggregate(); - FunctionArgumentType int64_not_aggregate_arg_type(types::Int64Type(), - agg_options); - - FunctionOptions fn_opts_allow_null_handling; - fn_opts_allow_null_handling.set_supports_null_handling_modifier(true); - - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_valid_templated_return_sum_int64_arg"}, - FunctionSignature(result_type, {int64_aggregate_arg_type}, context_id++), - /*argument_names=*/{"x"}, ParseResumeLocation::FromString("sum(x)"), - Function::AGGREGATE, fn_opts_allow_null_handling)); - - // Add a SQL UDA with a valid templated SQL body that refers to a NOT - // AGGREGATE argument only. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_valid_templated_return_int64_not_aggregate_arg"}, - FunctionSignature(result_type, {int64_not_aggregate_arg_type}, - context_id++), - /*argument_names=*/{"y"}, ParseResumeLocation::FromString("y"), - Function::AGGREGATE)); - - // Add a SQL UDA with a valid templated SQL body that refers to an AGGREGATE - // argument and also a NOT AGGREGATE argument in the same script. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_valid_templated_return_int64_aggregate_and_not_aggregate_arg"}, - FunctionSignature( - result_type, {int64_aggregate_arg_type, int64_not_aggregate_arg_type}, - context_id++), - /*argument_names=*/{"x", "y"}, - ParseResumeLocation::FromString("sum(x) + y"), Function::AGGREGATE)); - - // Add a SQL UDA with an invalid templated SQL body that refers to an - // AGGREGATE argument and also a NOT AGGREGATE argument in the same script. - // The function attempts to refer to the AGGREGATE argument outside of an - // aggregate function, which is invalid. Note that NOT AGGREGATE arguments can - // still be aggregated. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_invalid_templated_return_int64_aggregate_and_not_aggregate_arg"}, - FunctionSignature( - result_type, {int64_aggregate_arg_type, int64_not_aggregate_arg_type}, - context_id++), - /*argument_names=*/{"x", "y"}, - ParseResumeLocation::FromString("sum(y) + x"), Function::AGGREGATE)); - - // Add a SQL UDA with an invalid templated SQL body, since there is an ORDER - // BY clause in an aggregate function nested inside. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_invalid_templated_return_sum_int64_arg_nested_order_by"}, - FunctionSignature(result_type, {int64_aggregate_arg_type}, context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("sum(x order by x)"), - Function::AGGREGATE)); - - FunctionArgumentTypeOptions required_non_agg_options = - FunctionArgumentTypeOptions(FunctionArgumentType::REQUIRED) - .set_is_not_aggregate(true); - catalog_->AddOwnedFunction(std::make_unique( - std::vector{"uda_any_and_string_args_return_string"}, - FunctionSignature( - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(types::StringType(), required_non_agg_options)}, - context_id++), - /*argument_names=*/std::vector{"a", "x"}, - ParseResumeLocation::FromString("IF(LOGICAL_OR(a), x, x || '_suffix')"), - Function::AGGREGATE)); - - catalog_->AddOwnedFunction(std::make_unique( - std::vector{"uda_any_and_double_args_return_any"}, - FunctionSignature( - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, required_non_agg_options), - FunctionArgumentType(types::DoubleType(), - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/std::vector{"a", "x"}, - ParseResumeLocation::FromString("STRING_AGG(IF(x < 0, 'a', 'b'))"), - Function::AGGREGATE)); - - ZETASQL_CHECK_OK( - type_factory()->MakeArrayType(types::DoubleType(), &double_array_type)); - catalog_->AddOwnedFunction(std::make_unique( - std::vector{"uda_any_and_double_array_args_return_any"}, - FunctionSignature( - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED), - FunctionArgumentType(double_array_type, required_non_agg_options)}, - context_id++), - /*argument_names=*/std::vector{"a", "x"}, - ParseResumeLocation::FromString( - "IF(x[SAFE_OFFSET(0)] < 0, MAX(a), MIN(a))"), - Function::AGGREGATE)); - - // This function template cannot be invoked because the UDA does not have - // type information for the `GROUP_ROWS()` TVF. We added it here to reproduce - // unhelpful error messages. - // See discussion in: b/285159284. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"WrappedGroupRows"}, - FunctionSignature(result_type, {ARG_TYPE_ARBITRARY}, context_id++), - /*argument_names=*/{"e"}, - ParseResumeLocation::FromString( - R"sql( - SUM(e) WITH GROUP ROWS(SELECT e - FROM GROUP_ROWS() - WHERE e IS NOT NULL))sql"), - Function::AGGREGATE)); - - // Add a SQL UDA with a valid templated SQL body with two NOT AGGREGATE - // arguments having default values. - FunctionArgumentType int64_default_not_aggregate_arg_type( - types::Int64Type(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_is_not_aggregate() - .set_default(values::Int64(0)) - .set_argument_name("delta", kPositionalOrNamed)); - FunctionArgumentType bool_default_not_aggregate_arg_type( - types::BoolType(), - FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) - .set_is_not_aggregate() - .set_default(values::Bool(false)) - .set_argument_name("allow_nulls", kPositionalOrNamed)); - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_templated_two_not_aggregate_args"}, - FunctionSignature(result_type, - {result_type, int64_default_not_aggregate_arg_type, - bool_default_not_aggregate_arg_type}, - context_id++), - /*argument_names=*/{"cval", "delta", "allow_nulls"}, - ParseResumeLocation::FromString(R"( - IF(COUNT(1) = COUNT(DISTINCT cval) + delta OR - (allow_nulls AND COUNTIF(cval IS NOT NULL) = COUNT(DISTINCT cval)), - NULL, "NOT NULL"))"), - Function::AGGREGATE)); - - // Add a templated (scalar) SQL function with definer rights - auto templated_scalar_definer_rights_function = - std::make_unique( - std::vector{"templated_scalar_definer_rights"}, - FunctionSignature( - result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/std::vector{"x"}, - ParseResumeLocation::FromString( - "x + (select count(*) from KeyValue)")); - templated_scalar_definer_rights_function->set_sql_security( - ResolvedCreateStatementEnums::SQL_SECURITY_DEFINER); - catalog_->AddOwnedFunction( - std::move(templated_scalar_definer_rights_function)); - - // Add a templated UDF whose function body returns collated value by calling - // COLLATE function. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_collate_function"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("collate(x, 'und:ci')"))); - - // Add a templated UDF whose function body returns collated array value by - // using COLLATE function result as an array element. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_collate_function_in_array"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("[collate(x, 'und:ci')]"))); - - // Add a templated UDF whose function body returns collated struct value by - // using COLLATE function result as a struct field. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_collate_function_in_struct"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("(collate(x, 'und:ci'), 1)"))); - - // Add a templated UDF whose function body calls COLLATE function but has an - // explicit STRING return type without collation. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"udf_templated_collate_function_return_string"}, - FunctionSignature(types::StringType(), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("collate(x, 'und:ci')"))); - - // Add a templated SQL UDA which calls MIN aggregate function with COLLATE - // function inside. - catalog_->AddOwnedFunction(new TemplatedSQLFunction( - {"uda_templated_aggregate_with_min_collate_function"}, - FunctionSignature(result_type, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*argument_names=*/{"x"}, - ParseResumeLocation::FromString("min(collate(x, 'und:ci'))"), - Function::AGGREGATE)); -} - -namespace { -const char kColumnNameBool[] = "column_bool"; -const char kColumnNameBytes[] = "column_bytes"; -const char kColumnNameDate[] = "column_date"; -const char kColumnNameDouble[] = "column_double"; -const char kColumnNameFloat[] = "column_float"; -const char kColumnNameInt32[] = "column_int32"; -const char kColumnNameInt64[] = "column_int64"; -const char kColumnNameString[] = "column_string"; -const char kColumnNameTime[] = "column_time"; -const char kColumnNameUInt32[] = "column_uint32"; -const char kColumnNameUInt64[] = "column_uint64"; -const char kColumnNameKey[] = "key"; -const char kColumnNameValue[] = "value"; -const char kColumnNameFilename[] = "filename"; - -const char kTypeBool[] = "bool"; -const char kTypeBytes[] = "bytes"; -const char kTypeDate[] = "date"; -const char kTypeDouble[] = "double"; -const char kTypeFloat[] = "float"; -const char kTypeInt32[] = "int32"; -const char kTypeInt64[] = "int64"; -const char kTypeString[] = "string"; -const char kTypeTime[] = "time"; -const char kTypeUInt32[] = "uint32"; -const char kTypeUInt64[] = "uint64"; -const char kTypeInterval[] = "interval"; - -struct OutputColumn { - std::string description; - std::string name; - const Type* type; -}; - -struct TVFArgument { - std::string description; - const Type* type; -}; - -} // namespace - -static std::vector GetOutputColumnsForAllTypes( - TypeFactory* types) { - return {{kTypeBool, kColumnNameBool, types->get_bool()}, - {kTypeBytes, kColumnNameBytes, types->get_bytes()}, - {kTypeDate, kColumnNameDate, types->get_date()}, - {kTypeDouble, kColumnNameDouble, types->get_double()}, - {kTypeFloat, kColumnNameFloat, types->get_float()}, - {kTypeInt32, kColumnNameInt32, types->get_int32()}, - {kTypeInt64, kColumnNameInt64, types->get_int64()}, - {kTypeString, kColumnNameString, types->get_string()}, - {kTypeTime, kColumnNameTime, types->get_time()}, - {kTypeUInt32, kColumnNameUInt32, types->get_uint32()}, - {kTypeUInt64, kColumnNameUInt64, types->get_uint64()}}; -} - -static std::vector GetTVFArgumentsForAllTypes(TypeFactory* types) { - return {{kTypeBool, types->get_bool()}, - {kTypeBytes, types->get_bytes()}, - {kTypeDate, types->get_date()}, - {kTypeDouble, types->get_double()}, - {kTypeFloat, types->get_float()}, - {kTypeInt32, types->get_int32()}, - {kTypeInt64, types->get_int64()}, - {kTypeString, types->get_string()}, - {kTypeTime, types->get_time()}, - {kTypeUInt32, types->get_uint32()}, - {kTypeUInt64, types->get_uint64()}, - {kTypeInterval, types->get_interval()}}; -} - -static TVFRelation GetOutputSchemaWithTwoTypes( - absl::Span output_columns_for_all_types) { - TVFRelation::ColumnList columns; - for (int i = 0; i < 2; ++i) { - columns.emplace_back(output_columns_for_all_types[i].name, - output_columns_for_all_types[i].type); - } - return TVFRelation(columns); -} - -void SampleCatalog::LoadTableValuedFunctions1() { - TVFRelation empty_output_schema({}); - - const std::vector kOutputColumnsAllTypes = - GetOutputColumnsForAllTypes(types_); - - const std::vector kArgumentsAllTypes = - GetTVFArgumentsForAllTypes(types_); - - TVFRelation output_schema_two_types = - GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); - - // Generate an output schema that returns an int64_t value table. - TVFRelation output_schema_int64_value_table = - TVFRelation::ValueTable(types_->get_int64()); - - // Generate an output schema that returns a proto value table. - TVFRelation output_schema_proto_value_table = TVFRelation::ValueTable( - GetProtoType(zetasql_test__::TestExtraPB::descriptor())); - - // Generate an output schema that returns every possible type. - TVFRelation::ColumnList columns; - columns.reserve(kOutputColumnsAllTypes.size()); - for (const auto& kv : kOutputColumnsAllTypes) { - columns.emplace_back(kv.name, kv.type); - } - TVFRelation output_schema_all_types(columns); - - // Add a TVF that takes no arguments. - int context_id = 0; - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - output_schema_two_types)); - - // Add a TVF that returns an empty output schema. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_empty_output_schema"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - empty_output_schema, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - empty_output_schema)); - - // Add a TVF that takes no arguments and returns all POD types. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args_return_all_pod_types"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_all_types, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - output_schema_all_types)); - - // Add a TVF for each POD type that accepts exactly one argument of that type. - for (const auto& kv : kArgumentsAllTypes) { - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {absl::StrCat("tvf_exactly_1_", kv.description, "_arg")}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(kv.type)}, context_id++), - output_schema_two_types)); - } - - // Add TVFs that accept between two and nine INT64 arguments. - for (int i = 2; i < 10; ++i) { - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {absl::StrCat("tvf_exactly_", i, "_int64_args")}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(i, types::Int64Type()), - context_id++), - output_schema_two_types)); - } - - // For each templated argument type, add a TVF that accepts exactly one - // argument of that type. - const std::vector> - kSignatureArgumentKinds = { - {"arg_type_any_1", ARG_TYPE_ANY_1}, - {"arg_type_any_2", ARG_TYPE_ANY_2}, - {"arg_array_type_any_1", ARG_ARRAY_TYPE_ANY_1}, - {"arg_array_type_any_2", ARG_ARRAY_TYPE_ANY_2}, - {"arg_proto_any", ARG_PROTO_ANY}, - {"arg_struct_any", ARG_STRUCT_ANY}, - {"arg_enum_any", ARG_ENUM_ANY}, - {"arg_type_relation", ARG_TYPE_RELATION}, - {"arg_type_arbitrary", ARG_TYPE_ARBITRARY}, - }; - for (const std::pair& kv : - kSignatureArgumentKinds) { - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {absl::StrCat("tvf_exactly_one_", kv.first)}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(kv.second)}, context_id++), - output_schema_two_types)); - } - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_exactly_one_proto"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(proto_KitchenSinkPB_)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a repeating final argument of type int64_t. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_repeating_int64_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(types::Int64Type(), - FunctionArgumentType::REPEATED)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a repeating final argument of ARG_TYPE_ANY_1. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_repeating_any_one_type_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ANY_1, - FunctionArgumentType::REPEATED)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a repeating final argument of ARG_TYPE_ARBITRARY. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_repeating_arbitrary_type_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REPEATED)}, - context_id++), - output_schema_two_types)); - - // Stop here to avoid hitting lint limits for function length. -} - -void SampleCatalog::LoadTableValuedFunctions2() { - TVFRelation empty_output_schema({}); - - const std::vector kOutputColumnsAllTypes = - GetOutputColumnsForAllTypes(types_); - - TVFRelation output_schema_two_types = - GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); - - // Generate an output schema that returns an int64_t value table. - TVFRelation output_schema_int64_value_table = - TVFRelation::ValueTable(types_->get_int64()); - - // Generate an output schema that returns a proto value table. - TVFRelation output_schema_proto_value_table = TVFRelation::ValueTable( - GetProtoType(zetasql_test__::TestExtraPB::descriptor())); - - TVFRelation output_schema_two_types_with_pseudo_columns( - {TVFSchemaColumn(kOutputColumnsAllTypes[0].name, - kOutputColumnsAllTypes[0].type), - TVFSchemaColumn(kOutputColumnsAllTypes[1].name, - kOutputColumnsAllTypes[1].type), - TVFSchemaColumn("RowId", types_->get_int64(), - /*is_pseudo_column_in=*/true), - TVFSchemaColumn("PartitionName", types_->get_string(), - /*is_pseudo_column_in=*/true)}); - - // Generate an output schema that returns an int64_t value table. - TVFRelation output_schema_value_table_with_pseudo_columns = - TVFRelation::ValueTable( - GetProtoType(zetasql_test__::TestExtraPB::descriptor()), - {TVFSchemaColumn("RowId", types_->get_int64(), - /*is_pseudo_column_in=*/true), - TVFSchemaColumn("PartitionName", types_->get_string(), - /*is_pseudo_column_in=*/true)}) - .value(); - - int64_t context_id = 0; - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_arg_returning_fixed_output_with_pseudo_columns"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types_with_pseudo_columns, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - output_schema_two_types_with_pseudo_columns)); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_arg_returning_value_table_with_pseudo_columns"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_value_table_with_pseudo_columns, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - output_schema_value_table_with_pseudo_columns)); - - // Add a TVF with exactly one relation argument. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_with_fixed_output"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation()}, context_id++), - output_schema_two_types)); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_models_with_fixed_output"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{"label", types::DoubleType()}}), - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyModel(), FunctionArgumentType::AnyModel()}, - context_id++), - TVFRelation({{"label", types::DoubleType()}}))); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_one_model_arg_with_fixed_output"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeDouble, types::DoubleType()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/false), - { - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}, - {kColumnNameValue, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentType::AnyModel(), - }, - context_id++), - TVFRelation({{kTypeDouble, types::DoubleType()}, - {kTypeString, types::StringType()}}))); - - // Add a TVF with exactly one relation argument. The output schema is set to - // be the same as the input schema. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_one_relation_arg_output_schema_is_input_schema"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation()}, context_id++))); - - // Add a TVF with exactly one optional relation argument. The output schema is - // set to be the same as the input schema. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_optional_relation_arg_return_int64_value_table"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_int64_value_table, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL)}, - context_id++), - output_schema_int64_value_table)); - - // Add a TVF with one relation argument and one integer argument. The output - // schema is set to be the same as the input schema of the relation argument. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_one_relation_arg_output_schema_is_input_schema_plus_int64_arg"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType(types::Int64Type())}, - context_id++))); - - // Add one TVF with two relation arguments that forwards the schema of the - // first relation argument to the output of the TVF. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_two_relation_args_output_schema_is_input_schema"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType::AnyRelation()}, - context_id++))); - - // Add one TVF with two relation arguments with the second one optional that - // forwards the schema of the first relation argument to the output of the - // TVF. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_two_relation_args_second_optional_output_schema_is_input_schema"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL)}, - context_id++))); - - // Add one TVF with three arguments: The first one is required model; The - // second is optional table; The third is optional struct. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_model_evaluation_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyModel(), - FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType(ARG_STRUCT_ANY, - FunctionArgumentType::OPTIONAL)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly two relation arguments. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_relation_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType::AnyRelation()}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly two relation arguments that returns an int64_t value - // table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_relation_args_return_int64_value_table"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_int64_value_table, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType::AnyRelation()}, - context_id++), - output_schema_int64_value_table)); - - // Add a TVF with exactly two relation arguments that returns a proto value - // table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_relation_args_return_proto_value_table"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_proto_value_table, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType::AnyRelation()}, - context_id++), - output_schema_proto_value_table)); - - // Add a TVF with exactly one argument of ARG_TYPE_RELATION and another - // argument of type int64_t. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_int64_arg_one_relation_arg"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(types::Int64Type()), - FunctionArgumentType::AnyRelation()}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one argument of ARG_TYPE_RELATION and another - // argument of type int64_t. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_one_int64_arg"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType(types::Int64Type())}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument and repeating int64_t arguments. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_repeating_int64_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType(types::Int64Type(), - FunctionArgumentType::REPEATED)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with one relation argument and also a repeating final argument of - // ARG_TYPE_ANY_1. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_repeating_any_one_type_args"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType(ARG_TYPE_ANY_1, - FunctionArgumentType::REPEATED)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column and one string column. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_int64_string_input_columns"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeInt64, types::Int64Type()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column and one string column, and no extra columns are allowed - // in the input relation. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_only_int64_string_input_columns"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeInt64, types::Int64Type()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/false)}, - context_id++), - output_schema_two_types)); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_only_int64_struct_int64_input_columns"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeInt64, types::Int64Type()}, - {"struct_int64", struct_with_one_field_type_}}), - /*extra_relation_input_columns_allowed=*/false)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with two relation arguments, one with a required input schema of - // one uint64_t column and one string column, and the other with a required - // input schema of one date column and one string column. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_relation_args_uint64_string_and_date_string_input_columns"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeUInt64, types::Uint64Type()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/true), - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeDate, types::DateType()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF one relation argument with a required input schema of many - // supported types. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_required_input_schema_many_types"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeBool, types_->get_bool()}, - {kTypeBytes, types_->get_bytes()}, - {kTypeDate, types_->get_date()}, - {kTypeDouble, types_->get_double()}, - {kTypeFloat, types_->get_float()}, - {kTypeInt32, types_->get_int32()}, - {kTypeInt64, types_->get_int64()}, - {kTypeString, types_->get_string()}, - {kTypeTime, types_->get_timestamp()}, - {kTypeUInt32, types_->get_uint32()}, - {kTypeUInt64, types_->get_uint64()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - const std::string kMyEnum = "myenum"; - const std::string kMyDate = "mydate"; - const std::string kInt64a = "int64a"; - const std::string kInt64b = "int64b"; - const std::string kInt64c = "int64c"; - - // Add a TVF with exactly one relation argument with a required input schema - // of one enum column. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_one_enum_input_column"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column( - kMyEnum, - GetEnumType(zetasql_test__::TestEnum_descriptor()))}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one date column. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_one_date_input_column"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kMyDate, types_->get_date())}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of three int64_t columns. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_three_int64_input_columns"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64()), - TVFRelation::Column(kInt64b, types_->get_int64()), - TVFRelation::Column(kInt64c, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one proto column value table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_input_proto_value_table"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation::ValueTable(GetProtoType( - zetasql_test__::TestExtraPB::descriptor())), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column value table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_int64_input_value_table"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation::ValueTable(types::Int64Type()), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column value table, and forwards the input schema to the - // output schema. - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_one_relation_arg_int64_input_value_table_forward_schema"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::RelationWithSchema( - TVFRelation::ValueTable(types::Int64Type()), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++))); - - // Add a TVF with exactly one relation argument with a fixed schema that - // returns a proto value table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_return_proto_value_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_proto_value_table, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation( - {TVFRelation::Column(kTypeString, types_->get_string()), - TVFRelation::Column(kTypeInt64, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_proto_value_table)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column, and extra input columns are allowed. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_key_input_column_extra_input_columns_allowed"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one string column, and extra input columns are allowed. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_filename_input_column_extra_input_columns_allowed"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameFilename, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column, and extra input columns are not allowed. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_key_input_column_extra_input_columns_banned"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/false)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one column whose name is "uint32" but whose type is actually uint64_t. - // Then it is possible to call this TVF with the SimpleTypes table and type - // coercion should coerce the provided column named "uint32" to type uint64_t. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_uint64_input_column_named_uint32"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeUInt32, types::Uint64Type()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with exactly one relation argument with a required input schema - // of one int64_t column and one string column. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_key_filename_input_columns"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}, - {kColumnNameFilename, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF that takes two scalar named arguments. - const auto named_required_format_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( - "format_string", kPositionalOrNamed)); - const auto named_required_date_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( - "date_string", kPositionalOrNamed)); - const auto named_required_any_relation_arg = FunctionArgumentType( - ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_argument_name( - "any_relation_arg", kPositionalOrNamed)); - const auto named_required_schema_relation_arg = FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false) - .set_argument_name("schema_relation_arg", kPositionalOrNamed)); - const auto named_required_value_table_relation_arg = FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions( - output_schema_proto_value_table, - /*extra_relation_input_columns_allowed=*/false) - .set_argument_name("value_table_relation_arg", kPositionalOrNamed)); - const auto named_optional_string_arg = FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("format_string", kPositionalOrNamed)); - const auto named_optional_date_arg = FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("date_string", kPositionalOrNamed)); - const auto named_optional_any_relation_arg = FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("any_relation_arg", kPositionalOrNamed)); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_required_scalar_args"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_required_format_arg, named_required_date_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with two named optional arguments. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_optional_scalar_args"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_optional_string_arg, named_optional_date_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with one optional named "any table" relation argument. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_optional_any_relation_arg"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_optional_any_relation_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with one optional named "any table" relation argument and an - // optional scalar argument. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_optional_any_relation_arg_optional_scalar_arg"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_optional_any_relation_arg, named_optional_string_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with one required named "any table" relation argument. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_required_any_relation_arg"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_required_any_relation_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with one named relation argument with a required schema. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_required_schema_relation_arg"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_required_schema_relation_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with one named relation argument with a value table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_required_value_table_relation_arg"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_required_value_table_relation_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - // Add a TVF with a combination of named scalar and relation arguments. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_scalar_and_relation_args"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_required_format_arg, named_required_schema_relation_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - { - // Add a TVF with relation arg + scalar arg + named lambda. - FunctionArgumentType relation_arg = FunctionArgumentType( - ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_argument_name( - "any_relation", kPositionalOrNamed)); - FunctionArgumentType scalar_arg = FunctionArgumentType( - ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_name( - "any_scalar", kPositionalOrNamed)); - FunctionArgumentType named_lambda_arg = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions().set_argument_name("named_lambda", - kNamedOnly)); - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_relation_arg_scalar_arg_named_lambda"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {relation_arg, scalar_arg, named_lambda_arg}, - /*context_id=*/-1}, - output_schema_two_types)); - - FunctionArgumentType scalar_arg_positional_must_be_not_null = - FunctionArgumentType( - types_->get_int64(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_be_non_null(true) - .set_min_value(3)); - FunctionArgumentType scalar_arg_positional_must_be_not_null_with_default = - FunctionArgumentType( - types_->get_double(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_be_non_null(true) - .set_default(values::Double(3.14))); - FunctionArgumentType scalar_arg_positional_must_be_constant = - FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_be_constant(true)); - FunctionArgumentType scalar_arg_positional_must_be_constant_expression = - FunctionArgumentType( - types_->get_double(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_be_constant_expression(true) - .set_max_value(100)); - FunctionArgumentType scalar_arg_positional_must_support_equality = - FunctionArgumentType( - ARG_STRUCT_ANY, FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_support_equality(true)); - FunctionArgumentType scalar_arg_positional_must_support_ordering = - FunctionArgumentType( - ARG_TYPE_ANY_1, FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_support_ordering(true)); - FunctionArgumentType scalar_arg_positional_must_support_grouping = - FunctionArgumentType( - ARG_TYPE_ANY_2, FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_must_support_grouping(true)); - FunctionArgumentType - scalar_arg_positional_array_element_must_support_equality = - FunctionArgumentType( - ARG_ARRAY_TYPE_ANY_3, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_array_element_must_support_equality(true)); - FunctionArgumentType - scalar_arg_positional_array_element_must_support_ordering = - FunctionArgumentType( - ARG_ARRAY_TYPE_ANY_4, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_array_element_must_support_ordering(true)); - FunctionArgumentType - scalar_arg_positional_array_element_must_support_grouping = - FunctionArgumentType( - ARG_ARRAY_TYPE_ANY_5, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_array_element_must_support_grouping(true)); - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_positional_scalar_args_with_constraints"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - { - scalar_arg_positional_must_be_not_null, - scalar_arg_positional_must_be_constant, - scalar_arg_positional_must_be_constant_expression, - scalar_arg_positional_must_support_equality, - scalar_arg_positional_must_support_ordering, - scalar_arg_positional_must_support_grouping, - scalar_arg_positional_array_element_must_support_equality, - scalar_arg_positional_array_element_must_support_ordering, - scalar_arg_positional_array_element_must_support_grouping, - scalar_arg_positional_must_be_not_null_with_default, - }, - /*context_id=*/-1}, - output_schema_two_types)); - - FunctionArgumentType scalar_arg_named_must_be_not_null = - FunctionArgumentType( - types_->get_int64(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_1", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_be_non_null(true) - .set_min_value(3)); - FunctionArgumentType scalar_arg_named_must_be_constant = - FunctionArgumentType( - types_->get_string(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_2", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_be_constant(true)); - FunctionArgumentType scalar_arg_named_must_be_constant_expression = - FunctionArgumentType( - types_->get_double(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_3", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_be_constant_expression(true) - .set_max_value(100)); - FunctionArgumentType scalar_arg_named_must_support_equality = - FunctionArgumentType( - ARG_STRUCT_ANY, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_4", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_support_equality(true)); - FunctionArgumentType scalar_arg_named_must_support_ordering = - FunctionArgumentType( - ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_5", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_support_ordering(true)); - FunctionArgumentType scalar_arg_named_must_support_grouping = - FunctionArgumentType( - ARG_TYPE_ANY_2, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_6", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_support_grouping(true)); - FunctionArgumentType scalar_arg_named_array_element_must_support_equality = - FunctionArgumentType( - ARG_ARRAY_TYPE_ANY_3, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_7", FunctionEnums::POSITIONAL_OR_NAMED) - .set_array_element_must_support_equality(true)); - FunctionArgumentType scalar_arg_named_array_element_must_support_ordering = - FunctionArgumentType( - ARG_ARRAY_TYPE_ANY_4, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_8", FunctionEnums::POSITIONAL_OR_NAMED) - .set_array_element_must_support_ordering(true)); - FunctionArgumentType scalar_arg_named_array_element_must_support_grouping = - FunctionArgumentType( - ARG_ARRAY_TYPE_ANY_5, - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_9", FunctionEnums::POSITIONAL_OR_NAMED) - .set_array_element_must_support_grouping(true)); - FunctionArgumentType scalar_arg_named_must_be_not_null_with_default = - FunctionArgumentType( - types_->get_double(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_argument_name("arg_10", FunctionEnums::POSITIONAL_OR_NAMED) - .set_must_be_non_null(true) - .set_default(values::Double(3.14))); - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_scalar_args_with_constraints"}, - {FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - { - scalar_arg_named_must_be_not_null, - scalar_arg_named_must_be_constant, - scalar_arg_named_must_be_constant_expression, - scalar_arg_named_must_support_equality, - scalar_arg_named_must_support_ordering, - scalar_arg_named_must_support_grouping, - scalar_arg_named_array_element_must_support_equality, - scalar_arg_named_array_element_must_support_ordering, - scalar_arg_named_array_element_must_support_grouping, - scalar_arg_named_must_be_not_null_with_default, - }, - /*context_id=*/-1}, - output_schema_two_types)); - } -} // NOLINT(readability/fn_size) - -// Tests handling of optional relation arguments. -// If the relation is present, it doubles the value of each row. -// If the input relation is absent, returns a single zero. -// -// It has one optional value table argument with a single int64_t column. -// The output schema is also an int64_t value table. -class TvfOptionalRelation : public FixedOutputSchemaTVF { - class TvfOptionalRelationIterator : public EvaluatorTableIterator { - public: - explicit TvfOptionalRelationIterator( - std::unique_ptr input) - : input_(std::move(input)) {} - - bool NextRow() override { - if (!input_) { - if (rows_returned_ > 0) { - return false; - } - value_ = values::Int64(0); - ++rows_returned_; - return true; - } - - if (!input_->NextRow()) { - return false; - } - - value_ = values::Int64(input_->GetValue(0).int64_value() * 2); - ++rows_returned_; - return true; - } - - int NumColumns() const override { return 1; } - std::string GetColumnName(int i) const override { - ABSL_DCHECK_EQ(i, 0); - return ""; - } - const Type* GetColumnType(int i) const override { - ABSL_DCHECK_EQ(i, 0); - return types::Int64Type(); - } - const Value& GetValue(int i) const override { - ABSL_DCHECK_EQ(i, 0); - return value_; - } - absl::Status Status() const override { - return input_ ? input_->Status() : absl::OkStatus(); - } - absl::Status Cancel() override { return input_->Cancel(); } - - private: - int64_t rows_returned_ = 0; - std::unique_ptr input_; - Value value_; - }; - - public: - explicit TvfOptionalRelation() - : FixedOutputSchemaTVF( - {R"(tvf_optional_relation)"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - TVFRelation::ValueTable(types::Int64Type()), - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions( - TVFRelation::ValueTable(types::Int64Type()), - /*extra_relation_input_columns_allowed=*/true) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - /*context_id=*/-1), - TVFRelation::ValueTable(types::Int64Type())) {} - - absl::StatusOr> CreateEvaluator( - std::vector input_arguments, - const std::vector& output_columns, - const FunctionSignature* function_call_signature) const override { - ZETASQL_RET_CHECK_LE(input_arguments.size(), 1); - - std::unique_ptr input; - if (input_arguments.size() == 1) { - ZETASQL_RET_CHECK(input_arguments[0].relation); - input = std::move(input_arguments[0].relation); - ZETASQL_RET_CHECK_EQ(input->NumColumns(), 1); - ZETASQL_RET_CHECK_EQ(input->GetColumnType(0), types::Int64Type()); - } - - ZETASQL_RET_CHECK_EQ(output_columns.size(), 1); - return std::make_unique(std::move(input)); - } -}; - -// Tests handling of optional scalar and named arguments. -// -// Calculates and emits the value of y=xa+b, `steps` numbers of times -// incrementing x by `dx` each time. -class TvfOptionalArguments : public FixedOutputSchemaTVF { - class Evaluator : public EvaluatorTableIterator { - public: - Evaluator(double x, int64_t a, int64_t b, double dx, int64_t steps) - : x_(x), a_(a), b_(b), dx_(dx), steps_(steps) {} - - bool NextRow() override { - if (current_step_ >= steps_) { - return false; - } - - value_ = values::Double(x_ * a_ + b_); - - ++current_step_; - x_ += dx_; - return true; - } - int NumColumns() const override { return 1; } - std::string GetColumnName(int i) const override { - ABSL_DCHECK_EQ(i, 0); - return "y"; - } - const Type* GetColumnType(int i) const override { - ABSL_DCHECK_EQ(i, 0); - return types::DoubleType(); - } - const Value& GetValue(int i) const override { - ABSL_DCHECK_EQ(i, 0); - return value_; - } - absl::Status Status() const override { return absl::OkStatus(); } - absl::Status Cancel() override { return absl::OkStatus(); } - - private: - double x_; - int64_t a_; - int64_t b_; - double dx_; - int64_t steps_; - int64_t current_step_ = 0; - Value value_; - }; - - public: - explicit TvfOptionalArguments() - : FixedOutputSchemaTVF( - {R"(tvf_optional_arguments)"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{"value", types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/false), - { - // Starting x value. - FunctionArgumentType(types::DoubleType(), - FunctionArgumentType::OPTIONAL), - // A constant. - FunctionArgumentType( - types::Int64Type(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)), - // B constant. - FunctionArgumentType( - types::Int64Type(), - FunctionArgumentTypeOptions().set_cardinality( - FunctionArgumentType::OPTIONAL)), - // X increment. - FunctionArgumentType( - types::DoubleType(), - FunctionArgumentTypeOptions() - .set_argument_name( - "dx", FunctionEnums::POSITIONAL_OR_NAMED) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - // Number of steps. - FunctionArgumentType( - types::Int64Type(), - FunctionArgumentTypeOptions() - .set_argument_name( - "steps", FunctionEnums::POSITIONAL_OR_NAMED) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - }, - /*context_id=*/-1), - TVFRelation(TVFRelation({{"y", types::DoubleType()}}))) {} - - absl::StatusOr> CreateEvaluator( - std::vector input_arguments, - const std::vector& output_columns, - const FunctionSignature* function_call_signature) const override { - ZETASQL_RET_CHECK_LE(input_arguments.size(), 5); - - double x = 1; - if (!input_arguments.empty()) { - ZETASQL_RET_CHECK(input_arguments[0].value); - ZETASQL_RET_CHECK(input_arguments[0].value->type()->IsDouble()); - if (!input_arguments[0].value->is_null()) { - x = input_arguments[0].value->double_value(); - } - } - - int64_t a = 2; - if (input_arguments.size() >= 2) { - ZETASQL_RET_CHECK(input_arguments[1].value); - ZETASQL_RET_CHECK(input_arguments[1].value->type()->IsInt64()); - if (!input_arguments[1].value->is_null()) { - a = input_arguments[1].value->int64_value(); - } - } - - int64_t b = 3; - if (input_arguments.size() >= 3) { - ZETASQL_RET_CHECK(input_arguments[2].value); - ZETASQL_RET_CHECK(input_arguments[2].value->type()->IsInt64()); - if (!input_arguments[2].value->is_null()) { - b = input_arguments[2].value->int64_value(); - } - } - - double dx = 1; - if (input_arguments.size() >= 4) { - ZETASQL_RET_CHECK(input_arguments[3].value); - ZETASQL_RET_CHECK(input_arguments[3].value->type()->IsDouble()); - if (!input_arguments[3].value->is_null()) { - dx = input_arguments[3].value->double_value(); - } - } - - int64_t steps = 1; - if (input_arguments.size() >= 5) { - ZETASQL_RET_CHECK(input_arguments[4].value); - ZETASQL_RET_CHECK(input_arguments[4].value->type()->IsInt64()); - if (!input_arguments[4].value->is_null()) { - steps = input_arguments[4].value->int64_value(); - } - } - - return std::make_unique(x, a, b, dx, - steps); - } -}; - -// Tests handling of repeated arguments. -// Takes pairs of string and int arguments and produces a table with a row for -// each pair. -class TvfRepeatedArguments : public FixedOutputSchemaTVF { - class TvfRepeatedArgumentsIterator : public EvaluatorTableIterator { - public: - explicit TvfRepeatedArgumentsIterator(std::vector args) - : args_(std::move(args)) {} - - bool NextRow() override { - if (current_ + 1 >= args_.size()) { - return false; - } - - if (!args_[current_].value || - !args_[current_].value->type()->IsString()) { - status_ = absl::InternalError("Bad key"); - return false; - } - - if (!args_[current_ + 1].value || - !args_[current_ + 1].value->type()->IsInt64()) { - status_ = absl::InternalError("Bad value"); - return false; - } - - key_ = *args_[current_].value; - value_ = *args_[current_ + 1].value; - current_ += 2; - return true; - } - - int NumColumns() const override { return 2; } - std::string GetColumnName(int i) const override { - ABSL_DCHECK_GE(i, 0); - ABSL_DCHECK_LT(i, NumColumns()); - return i == 0 ? "key" : "value"; - } - const Type* GetColumnType(int i) const override { - ABSL_DCHECK_GE(i, 0); - ABSL_DCHECK_LT(i, NumColumns()); - return i == 0 ? types::StringType() : types::Int64Type(); - } - const Value& GetValue(int i) const override { - ABSL_DCHECK_GE(i, 0); - ABSL_DCHECK_LT(i, NumColumns()); - return i == 0 ? key_ : value_; - } - absl::Status Status() const override { return status_; } - absl::Status Cancel() override { return absl::OkStatus(); } - - private: - std::vector args_; - int64_t current_ = 0; - Value key_; - Value value_; - absl::Status status_; - }; - - public: - explicit TvfRepeatedArguments() - : FixedOutputSchemaTVF( - {R"(tvf_repeated_arguments)"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{"key", types::StringType()}, - {"value", types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/false), - { - FunctionArgumentType(types::StringType(), - FunctionArgumentType::REPEATED), - FunctionArgumentType(types::Int64Type(), - FunctionArgumentType::REPEATED), - }, - /*context_id=*/-1), - TVFRelation({{"key", types::StringType()}, - {"value", types::Int64Type()}})) {} - - absl::StatusOr> CreateEvaluator( - std::vector input_arguments, - const std::vector& output_columns, - const FunctionSignature* function_call_signature) const override { - ZETASQL_RET_CHECK(input_arguments.size() % 2 == 0); - return std::make_unique( - std::move(input_arguments)); - } -}; - -// Tests forwarding input schema in a TVF. -// -// This function will pass through values from input columns to matching output -// columns. For INT64 columns it will additionally add the value of the integer -// argument. -// -// It has one relation argument and one integer argument. The output -// schema is set to be the same as the input schema of the relation argument. -class TvfIncrementBy : public ForwardInputSchemaToOutputSchemaTVF { - class TvfIncrementByIterator : public EvaluatorTableIterator { - public: - explicit TvfIncrementByIterator( - std::unique_ptr input, int64_t value_arg, - std::vector output_columns) - : input_(std::move(input)), - value_arg_(value_arg), - output_columns_(std::move(output_columns)), - values_(output_columns_.size()) {} - - bool NextRow() override { - if (!input_->NextRow()) { - status_ = input_->Status(); - return false; - } - - for (int o = 0; o < output_columns_.size(); ++o) { - std::string output_column_name = GetColumnName(o); - const Value* value = nullptr; - for (int i = 0; i < input_->NumColumns(); ++i) { - if (input_->GetColumnName(i) == output_column_name) { - value = &input_->GetValue(i); - break; - } - } - - if (value == nullptr) { - status_ = ::zetasql_base::InternalErrorBuilder() - << "Could not find input column for " << output_column_name; - return false; - } - - values_[o] = value->type()->IsInt64() - ? values::Int64(value->ToInt64() + value_arg_) - : *value; - } - - return true; - } - - int NumColumns() const override { - return static_cast(output_columns_.size()); - } - std::string GetColumnName(int i) const override { - ABSL_DCHECK_LT(i, output_columns_.size()); - return output_columns_[i].name; - } - const Type* GetColumnType(int i) const override { - ABSL_DCHECK_LT(i, output_columns_.size()); - return output_columns_[i].type; - } - const Value& GetValue(int i) const override { - ABSL_DCHECK_LT(i, values_.size()); - return values_[i]; - } - absl::Status Status() const override { return status_; } - absl::Status Cancel() override { return input_->Cancel(); } - - private: - std::unique_ptr input_; - int64_t value_arg_; - absl::Status status_; - std::vector output_columns_; - std::vector values_; - }; - - public: - explicit TvfIncrementBy() - : ForwardInputSchemaToOutputSchemaTVF( - {R"(tvf_increment_by)"}, - FunctionSignature( - ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType( - types::Int64Type(), - FunctionArgumentTypeOptions() - .set_cardinality(FunctionArgumentType::OPTIONAL) - .set_default(values::Int64(1)))}, - /*context_id=*/-1)) {} - - absl::StatusOr> CreateEvaluator( - std::vector input_arguments, - const std::vector& output_columns, - const FunctionSignature* function_call_signature) const override { - ZETASQL_RET_CHECK_EQ(input_arguments.size(), 2); - ZETASQL_RET_CHECK(input_arguments[0].relation); - ZETASQL_RET_CHECK(input_arguments[1].value); - ZETASQL_RET_CHECK_EQ(input_arguments[1].value->type_kind(), TypeKind::TYPE_INT64); - return std::make_unique( - std::move(input_arguments[0].relation), - input_arguments[1].value->ToInt64(), output_columns); - } -}; - -// This function takes two integer values and provides both sum and difference. -// -// Has a fixed input and output schema. -class TvfSumAndDiff : public FixedOutputSchemaTVF { - class TvfSumAndDiffIterator : public EvaluatorTableIterator { - public: - explicit TvfSumAndDiffIterator( - std::unique_ptr input) - : input_(std::move(input)) { - output_columns_["sum"] = values::Int64(0); - output_columns_["diff"] = values::Int64(0); - } - - bool NextRow() override { - if (!input_->NextRow()) { - return false; - } - int64_t a = input_->GetValue(0).int64_value(); - int64_t b = input_->GetValue(1).int64_value(); - output_columns_["sum"] = values::Int64(a + b); - output_columns_["diff"] = values::Int64(a - b); - return true; - } - - int NumColumns() const override { - return static_cast(output_columns_.size()); - } - std::string GetColumnName(int i) const override { - ABSL_DCHECK_LT(i, output_columns_.size()); - auto iter = output_columns_.cbegin(); - std::advance(iter, i); - return iter->first; - } - const Type* GetColumnType(int i) const override { - return GetValue(i).type(); - } - const Value& GetValue(int i) const override { - ABSL_DCHECK_LT(i, output_columns_.size()); - auto iter = output_columns_.cbegin(); - std::advance(iter, i); - return iter->second; - } - absl::Status Status() const override { return input_->Status(); } - absl::Status Cancel() override { return input_->Cancel(); } - - private: - std::unique_ptr input_; - absl::btree_map output_columns_; - }; - - public: - TvfSumAndDiff() - : FixedOutputSchemaTVF( - {R"(tvf_sum_diff)"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{"sum", types::Int64Type()}, - {"diff", types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation( - {{"a", types::Int64Type()}, {"b", types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/false)}, - /*context_id=*/-1), - TVFRelation( - {{"sum", types::Int64Type()}, {"diff", types::Int64Type()}})) {} - - absl::StatusOr> CreateEvaluator( - std::vector input_arguments, - const std::vector& output_columns, - const FunctionSignature* function_call_signature) const override { - ZETASQL_RET_CHECK_EQ(input_arguments.size(), 1); - ZETASQL_RET_CHECK(input_arguments[0].relation); - return std::make_unique( - std::move(input_arguments[0].relation)); - } -}; - -void SampleCatalog::LoadTableValuedFunctionsWithEvaluators() { - catalog_->AddOwnedTableValuedFunction(new TvfOptionalRelation()); - catalog_->AddOwnedTableValuedFunction(new TvfOptionalArguments()); - catalog_->AddOwnedTableValuedFunction(new TvfRepeatedArguments()); - catalog_->AddOwnedTableValuedFunction(new TvfIncrementBy()); - catalog_->AddOwnedTableValuedFunction(new TvfSumAndDiff()); -} - -void SampleCatalog::LoadFunctionsWithStructArgs() { - const std::vector kOutputColumnsAllTypes = - GetOutputColumnsForAllTypes(types_); - TVFRelation output_schema_two_types = - GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); - - const Type* array_string_type = nullptr; - ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_string(), &array_string_type)); - - const Type* struct_type1 = nullptr; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"field1", array_string_type}, {"field2", array_string_type}}, - &struct_type1)); - const Type* struct_type2 = nullptr; - ZETASQL_CHECK_OK(types_->MakeStructType({{"field1", array_string_type}, - {"field2", array_string_type}, - {"field3", array_string_type}}, - &struct_type2)); - - const auto named_struct_arg1 = FunctionArgumentType( - struct_type1, FunctionArgumentTypeOptions().set_argument_name( - "struct_arg1", kPositionalOrNamed)); - const auto named_struct_arg2 = FunctionArgumentType( - struct_type2, FunctionArgumentTypeOptions().set_argument_name( - "struct_arg2", kPositionalOrNamed)); - - // A TVF with struct args. - auto tvf = std::make_unique( - std::vector{"tvf_named_struct_args"}, - FunctionSignature{FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {named_struct_arg1, named_struct_arg2}, - /*context_id=*/-1}, - output_schema_two_types); - catalog_->AddOwnedTableValuedFunction(tvf.release()); - - auto function = std::make_unique( - "fn_named_struct_args", "sample_functions", Function::SCALAR); - function->AddSignature({FunctionArgumentType(array_string_type), - {named_struct_arg1, named_struct_arg2}, - /*context_id=*/-1}); - catalog_->AddOwnedFunction(std::move(function)); -} - -void SampleCatalog::LoadTVFWithExtraColumns() { - int64_t context_id = 0; - - // Add a TVF with appended columns of valid ZetaSQL types. - catalog_->AddOwnedTableValuedFunction( - new ForwardInputSchemaToOutputSchemaWithAppendedColumnTVF( - {"tvf_append_columns"}, - FunctionSignature(ARG_TYPE_RELATION, {ARG_TYPE_RELATION}, - context_id++), - {TVFSchemaColumn("append_col_int64", types::Int64Type()), - TVFSchemaColumn("append_col_int32", types::Int32Type()), - TVFSchemaColumn("append_col_uint32", types::Uint32Type()), - TVFSchemaColumn("append_col_uint64", types::Uint64Type()), - TVFSchemaColumn("append_col_bytes", types::BytesType()), - TVFSchemaColumn("append_col_bool", types::BoolType()), - TVFSchemaColumn("append_col_float", types::FloatType()), - TVFSchemaColumn("append_col_double", types::DoubleType()), - TVFSchemaColumn("append_col_date", types::DateType()), - TVFSchemaColumn("append_col_timestamp", types::TimestampType()), - TVFSchemaColumn("append_col_numeric", types::NumericType()), - TVFSchemaColumn("append_col_bignumeric", types::BigNumericType()), - TVFSchemaColumn("append_col_json", types::JsonType()), - TVFSchemaColumn("append_col_string", types::StringType()), - TVFSchemaColumn("append_col_uuid", types::UuidType())})); - - // Add a TVF with an appended column that has empty name. - catalog_->AddOwnedTableValuedFunction( - new ForwardInputSchemaToOutputSchemaWithAppendedColumnTVF( - {"tvf_append_no_column"}, - FunctionSignature(ARG_TYPE_RELATION, {ARG_TYPE_RELATION}, - context_id++), - {})); - - const auto named_required_any_relation_arg = FunctionArgumentType( - ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_argument_name( - "any_relation_arg", kPositionalOrNamed)); - - // Add a TVF with one required named "any table" relation argument. - catalog_->AddOwnedTableValuedFunction( - new ForwardInputSchemaToOutputSchemaWithAppendedColumnTVF( - {"tvf_append_columns_any_relation_arg"}, - FunctionSignature(ARG_TYPE_RELATION, - {named_required_any_relation_arg}, - /*context_id=*/context_id++), - {TVFSchemaColumn("append_col_int32", types::Int32Type())})); -} - -void SampleCatalog::LoadDescriptorTableValuedFunctions() { - int64_t context_id = 0; - const std::vector kOutputColumnsAllTypes = - GetOutputColumnsForAllTypes(types_); - - TVFRelation output_schema_two_types = - GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); - - const std::string kInt64a = "int64a"; - const std::string kInt64b = "int64b"; - - // Add a TVF with a table parameter and a descriptor with -1 table offset. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_one_descriptor"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true), - FunctionArgumentType::AnyDescriptor()}, - context_id++), - output_schema_two_types)); - - // Add a TVF with two table parameters, one descriptor with 0 table offset - // and one descriptor with 1 table offset. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_relations_arg_two_descriptors_resolved_names"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true), - FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64b, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true), - FunctionArgumentType::AnyDescriptor(0), - FunctionArgumentType::AnyDescriptor(1)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a table parameter and a descriptor with 0 table offset. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_one_descriptor_resolved_names"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true), - FunctionArgumentType::AnyDescriptor(0)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a descriptor with 1 table offset and a table parameter. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_descriptor_resolved_names_one_relation_arg"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyDescriptor(1), - FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64()), - TVFRelation::Column(kInt64b, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a descriptor with 1 table offset and a table parameter with - // ambiguous column naming problem. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_descriptor_resolved_names_one_relation_arg_ambiguous_naming"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyDescriptor(1), - FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64()), - TVFRelation::Column(kInt64b, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a descriptor with 1 table offset, a table parameter and a - // descriptor with -1 table offset. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_descriptor_resolved_names_one_relation_arg_one_descriptor_arg"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyDescriptor(1), - FunctionArgumentType::RelationWithSchema( - TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), - /*extra_relation_input_columns_allowed=*/true), - FunctionArgumentType::AnyDescriptor()}, - context_id++), - output_schema_two_types)); -} - -void SampleCatalog::LoadConnectionTableValuedFunctions() { - int64_t context_id = 0; - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_connection_arg_with_fixed_output"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyConnection()}, context_id++), - TVFRelation({{kTypeString, types::StringType()}}))); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_connection_one_string_arg_with_fixed_output"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeInt64, types::Int64Type()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyConnection(), - FunctionArgumentType(types::StringType())}, - context_id++), - TVFRelation({{kTypeInt64, types::Int64Type()}, - {kTypeString, types::StringType()}}))); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_connections_with_fixed_output"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - TVFRelation({{kTypeDouble, types::DoubleType()}, - {kTypeString, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyConnection(), - FunctionArgumentType::AnyConnection()}, - context_id++), - TVFRelation({{kTypeDouble, types::DoubleType()}, - {kTypeString, types::StringType()}}))); -} - -void SampleCatalog::LoadTableValuedFunctionsWithDeprecationWarnings() { - // Generate an empty output schema. - TVFRelation empty_output_schema({}); - - const std::vector kOutputColumnsAllTypes = - GetOutputColumnsForAllTypes(types_); - - TVFRelation output_schema_two_types = - GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); - - int context_id = 0; - - // Add a TVF that triggers a deprecation warning. - FunctionSignature deprecation_warning_signature( - FunctionArgumentType::RelationWithSchema( - empty_output_schema, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++); - deprecation_warning_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/11)}); - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_deprecation_warning"}, deprecation_warning_signature, - output_schema_two_types)); - - // Add a TVF that triggers two deprecation warnings with the same kind. - FunctionSignature two_deprecation_warnings_same_kind_signature( - FunctionArgumentType::RelationWithSchema( - empty_output_schema, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++); - two_deprecation_warnings_same_kind_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/12), - CreateDeprecationWarning(/*id=*/13)}); - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_deprecation_warnings_same_kind"}, - two_deprecation_warnings_same_kind_signature, output_schema_two_types)); - - // Add a TVF that triggers two deprecation warnings with different kinds. - FunctionSignature two_deprecation_warnings_signature( - FunctionArgumentType::RelationWithSchema( - empty_output_schema, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++); - two_deprecation_warnings_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/14), - CreateDeprecationWarning( - /*id=*/15, DeprecationWarning::DEPRECATED_FUNCTION_SIGNATURE)}); - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_deprecation_warnings"}, two_deprecation_warnings_signature, - output_schema_two_types)); - - // Add a TVF with exactly one relation argument. The output schema is set to - // be the same as the input schema. The TVF also triggers a deprecation - // warning. - FunctionSignature forward_deprecation_signature( - ARG_TYPE_RELATION, {FunctionArgumentType::AnyRelation()}, context_id++); - forward_deprecation_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/16)}); - catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( - {"tvf_one_relation_arg_output_schema_is_input_schema_deprecation"}, - forward_deprecation_signature)); - - // Add a TVF with three arguments: The first one is required model; The - // second is optional table; The third is named optional struct. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_model_optional_table_named_struct"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyModel(), - FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_STRUCT_ANY, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with two arguments: The first is named optional struct. The - // second is a named optional table; - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_struct_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType( - ARG_STRUCT_ANY, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("barfoo", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with two arguments: The first is named optional struct. The - // second is a named optional table; - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_named_proto_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType( - ARG_PROTO_ANY, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("barfoo", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required model; The - // second is optional scalar; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_model_optional_scalar_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyModel(), - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first two are optional scalars; The - // third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_optional_scalars_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required table; The - // second is optional scalar; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_table_optional_scalar_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is optional table; The - // second is optional scalar; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_optional_table_optional_scalar_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required scalar; The - // second is optional model; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_scalar_optional_model_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ARBITRARY), - FunctionArgumentType(ARG_TYPE_MODEL, FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required scalar; The - // second is optional model; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_optional_scalar_optional_model_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType(ARG_TYPE_MODEL, FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is optional scalar; The - // second is named optional table; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_optional_scalar_named_tables"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("barfoo", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is optional scalar; The - // second is named optional model; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_optional_scalar_named_model_named_table"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - ARG_TYPE_MODEL, - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("barfoo", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with a required table, a named optional table, a mandatory - // named table and a required mandatory named table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_required_named_optional_required_tables"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::REQUIRED), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("table2", kNamedOnly) - .set_cardinality(FunctionArgumentType::REQUIRED)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("table3", kPositionalOrNamed) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("table4", kNamedOnly) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required model; The - // second is optional string; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_model_optional_string_optional_table"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyModel(), - FunctionArgumentType(types::StringType(), - FunctionArgumentType::OPTIONAL), - FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL)}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required model; The - // second is optional table; The third is named optional string with default. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_model_optional_table_named_string_default"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType::AnyModel(), - FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - types::StringType(), - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kPositionalOrNamed) - .set_default(values::String("default")) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with three arguments: The first one is required model; The - // second is a string with default; The third is named optional table. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_optional_table_default_mandatory_string"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType(ARG_TYPE_RELATION, - FunctionArgumentType::OPTIONAL), - FunctionArgumentType( - types::StringType(), - FunctionArgumentTypeOptions() - .set_argument_name("foobar", kNamedOnly) - .set_default(values::String("default")) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); - - // Add a TVF with two table arguments which are both named-only. - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_two_named_only_tables"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_two_types, - /*extra_relation_input_columns_allowed=*/false), - {FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("table1", kNamedOnly) - .set_cardinality(FunctionArgumentType::OPTIONAL)), - FunctionArgumentType( - ARG_TYPE_RELATION, - FunctionArgumentTypeOptions() - .set_argument_name("table2", kNamedOnly) - .set_cardinality(FunctionArgumentType::OPTIONAL))}, - context_id++), - output_schema_two_types)); -} - -// Add a SQL table function to catalog starting from a full create table -// function statement. -void SampleCatalog::AddSqlDefinedTableFunctionFromCreate( - absl::string_view create_table_function, - const LanguageOptions& language_options, absl::string_view user_id_column) { - // Ensure the language options used allow CREATE FUNCTION - LanguageOptions language = language_options; - language.AddSupportedStatementKind(RESOLVED_CREATE_TABLE_FUNCTION_STMT); - language.EnableLanguageFeature(FEATURE_CREATE_TABLE_FUNCTION); - language.EnableLanguageFeature(FEATURE_TABLE_VALUED_FUNCTIONS); - language.EnableLanguageFeature(FEATURE_TEMPLATE_FUNCTIONS); - language.EnableLanguageFeature(FEATURE_V_1_1_WITH_ON_SUBQUERY); - language.EnableLanguageFeature(FEATURE_V_1_3_INLINE_LAMBDA_ARGUMENT); - AnalyzerOptions analyzer_options; - analyzer_options.set_language(language); - std::unique_ptr analyzer_output; - ZETASQL_CHECK_OK(AnalyzeStatement(create_table_function, analyzer_options, - catalog_.get(), catalog_->type_factory(), - &analyzer_output)) - << "[" << create_table_function << "]"; - const ResolvedStatement* resolved = analyzer_output->resolved_statement(); - ABSL_CHECK(resolved->Is()); - const auto* resolved_create = - resolved->GetAs(); - - std::unique_ptr function; - if (resolved_create->query() != nullptr) { - std::unique_ptr sql_tvf; - ZETASQL_CHECK_OK(SQLTableValuedFunction::Create(resolved_create, &sql_tvf)); - function = std::move(sql_tvf); - } else { - function = std::make_unique( - resolved_create->name_path(), resolved_create->signature(), - resolved_create->argument_name_list(), - ParseResumeLocation::FromString(resolved_create->code())); - } - - if (!user_id_column.empty()) { - ZETASQL_CHECK_OK(function->SetUserIdColumnNamePath({std::string(user_id_column)})); - } - catalog_->AddOwnedTableValuedFunction(std::move(function)); - sql_object_artifacts_.emplace_back(std::move(analyzer_output)); -} - -void SampleCatalog::LoadNonTemplatedSqlTableValuedFunctions( - const LanguageOptions& language_options) { - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelectWithUserId() - AS SELECT 1 AS a, 2 AS b;)", - language_options, "a"); - - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelect() - AS SELECT 1 AS a, 2 AS b;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelectUnion() - AS SELECT 1 AS a, 2 AS b - UNION ALL - SELECT 1, 4;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelectCTE() - AS WITH t AS (SELECT 1 AS a, 2 AS b) SELECT * FROM t;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelectFromTvf() - AS SELECT * FROM NullarySelect();)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelectCallingScalarUDF() - AS SELECT NullaryPi() AS pi;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION NullarySelectCallingLambdaArgFunction() - AS SELECT ARRAY_FILTER([1, 2, 3], e -> e > 1) as arr;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION UnaryScalarArg(arg0 INT64) - AS SELECT arg0;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION UnaryScalarArgMultipleReferences(arg0 INT64) - AS SELECT arg0 + arg0 AS ret0, arg0 AS ret1;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION BinaryScalarArg(arg0 INT64, arg1 INT64) - AS SELECT arg0, arg1, arg0 + arg1 AS ret2;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION UnaryScalarArgSubqueryReference(arg0 INT64) - AS SELECT (SELECT arg0) AS ret0;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION UnaryScalarArgSubqueryWithReference(arg0 INT64) - AS SELECT (WITH t AS (SELECT arg0) SELECT t.arg0 FROM t) AS ret0;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION UnaryTableArg(arg0 TABLE) - AS SELECT * FROM arg0;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION ScalarParamUsedAsTVFArgument(arg0 INT64) - AS (SELECT * FROM UnaryScalarArg(arg0)) - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION UnaryAbTableArg(arg0 TABLE) - AS SELECT * FROM arg0; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION UnaryAbTableArgSelfJoin( - arg0 TABLE) - AS SELECT * FROM arg0 CROSS JOIN arg0 AS t1; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION UnaryAbTableArgScannedInCTE( - arg0 TABLE) - AS WITH t AS (SELECT * FROM arg0) SELECT * FROM t; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION BinaryTableArg( - arg0 TABLE, arg1 TABLE) - AS SELECT * FROM arg0 CROSS JOIN arg1; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION BinaryAbTableArg( - arg0 TABLE, arg1 TABLE) - AS SELECT * FROM arg0 CROSS JOIN arg1; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION UnaryAbTableArgWithScalarArgs( - x INT64, arg0 TABLE, y STRING) - AS SELECT * FROM arg0 WHERE arg0.a = x AND arg0.b = y; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION UnaryAbTableArgWithScalarArgsTempl( - x ANY TYPE, arg0 ANY TABLE, y ANY TYPE) - AS SELECT * FROM arg0 WHERE arg0.a = x AND arg0.b = y; - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION CallsUnaryAbTableArgWithScalarArgsTempl( - ignored_param ANY TYPE) - AS SELECT * FROM UnaryAbTableArgWithScalarArgsTempl( - 1, (SELECT 1 a, "2" b, DATE '2020-08-22' AS c), "b"); - )sql", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION JoinsTableArgToScannedTable( - arg_table TABLE, arg_scalar STRING - ) - AS SELECT arg_scalar AS c, * - FROM TwoIntegers JOIN arg_table USING (key, value); - )sql", - language_options); - - // Functions for definer-rights inlining - AddSqlDefinedTableFunctionFromCreate( - R"sql( - CREATE TABLE FUNCTION DefinerRightsTvf(a INT64) SQL SECURITY DEFINER - AS SELECT * FROM KeyValue WHERE KeyValue.Key = a; )sql", - language_options); - - if (language_options.LanguageFeatureEnabled( - FEATURE_V_1_3_COLLATION_SUPPORT) && - language_options.LanguageFeatureEnabled( - FEATURE_V_1_3_ANNOTATION_FRAMEWORK)) { - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION ScalarArgWithCollatedOutputCols(arg0 STRING) - AS SELECT - COLLATE(arg0, 'und:ci') AS col_ci, - [COLLATE(arg0, 'und:ci')] AS col_array_ci, - ([COLLATE(arg0, 'und:ci')], 1) AS col_struct_ci;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION TableArgWithCollatedOutputCols( - arg0 TABLE) - AS SELECT - COLLATE(a, 'und:ci') AS col_ci, - [COLLATE(a, 'und:ci')] AS col_array_ci, - ([COLLATE(a, 'und:ci')], 1) AS col_struct_ci - FROM arg0;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION ValueTableArgWithCollatedOutputCols( - arg0 TABLE>) - AS SELECT - COLLATE(str_field, 'und:ci') AS col_ci, - [COLLATE(str_field, 'und:ci')] AS col_array_ci, - ([COLLATE(str_field, 'und:ci')], 1) AS col_struct_ci - FROM arg0;)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION ScalarArgWithCollatedOutputValueTableCol( - arg0 STRING) - AS SELECT AS VALUE COLLATE(arg0, 'und:ci');)", - language_options); - AddSqlDefinedTableFunctionFromCreate( - R"(CREATE TABLE FUNCTION ScalarArgsOfCollatableTypes( - arg0 STRING, arg1 ARRAY, arg2 STRUCT) - AS SELECT arg0, arg1, arg2;)", - language_options); - } -} - -void SampleCatalog::LoadTemplatedSQLTableValuedFunctions() { - const std::string kColumnNameKey = "key"; - const std::string kColumnNameDate = "date"; - int context_id = 0; - - // Add a TVF with a simple valid templated SQL body. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_one"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, ParseResumeLocation::FromString("select 1 as x"))); - - // Add a templated SQL TVF that calls another templated SQL TVF. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_call_tvf_templated_select_one"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select * from tvf_templated_select_one()"))); - - // Add a templated SQL TVF that calls another templated SQL TVF twice. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_call_tvf_templated_select_one_twice"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select * from tvf_templated_select_one() union all " - "select * from tvf_templated_select_one()"))); - - // Add a TVF with a valid templated SQL body that refers to a scalar argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_int64_arg"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(types::Int64Type())}, - context_id++), - /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); - - // Add a TVF with a valid templated SQL body that refers to a scalar argument - // with a name that is potentially ambiguous with an in scope column name. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_int64_arg_with_name_ambiguity"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(types::Int64Type())}, - context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("SELECT x FROM (SELECT -99 AS x)"))); - - // Add a TVF with a valid templated SQL body that refers to a scalar argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_any_scalar_arg"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++), - /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); - - // Add a TVF with a valid templated SQL body that performs addition on a - // scalar argument. The function signature accepts a single argument of any - // scalar type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_scalar_arg_plus_integer"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select x + 42"))); - - // Add a TVF with a valid templated SQL body that accepts an input argument - // where the name contains '$'. The function signature accepts a single - // argument of any scalar type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_scalar_arg_plus_integer_accept_dollars_col_name"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*arg_name_list=*/{"$col1"}, - ParseResumeLocation::FromString("select `$col1` as x"))); - - // Add a TVF with a valid templated SQL body that returns an output column - // where the name contains '$'. The function signature accepts a single - // argument of any scalar type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_scalar_arg_plus_integer_return_dollars_col_name"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select x as `$col1`"))); - - // Add a TVF with a valid templated SQL body that performs concatenation on a - // scalar argument. The function signature accepts a single argument of any - // scalar type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_scalar_arg_concat_string"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select concat(x, 'abc') as y"))); - - // Add a TVF with a valid templated SQL body that performs a proto field - // access on a scalar argument. The function signature accepts a single - // argument of any scalar type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_scalar_arg_proto_field_access"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY, - FunctionArgumentType::REQUIRED)}, - context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select x.int32_field as y"))); - - // Add a TVF with a valid templated SQL body that refers to a relation - // argument using specific column names. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_relation_arg_using_column_names"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation()}, context_id++), - /*arg_name_list=*/{"t"}, - ParseResumeLocation::FromString("select key, value from t"))); - - // Add a TVF with a simple valid templated SQL body that selects a name from - // a templated input table argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_a"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation()}, context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select a from x"))); - - // Add a TVF with a valid templated SQL body that refers to a relation - // argument using "select *". - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_relation_arg_using_select_star"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation()}, context_id++), - /*arg_name_list=*/{"t"}, - ParseResumeLocation::FromString("select * from t"))); - - // Add a TVF with a templated SQL body that refers to a relation argument - // using "select 1". The TVF is missing an output column name. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_relation_arg_using_select_one"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation()}, context_id++), - /*arg_name_list=*/{"t"}, - ParseResumeLocation::FromString("(select 1 from t limit 1)"))); - - // Add a TVF with a valid templated SQL body that refers to a relation - // argument and also a table in the catalog. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_relation_arg_and_catalog_table"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation()}, context_id++), - /*arg_name_list=*/{"t"}, - ParseResumeLocation::FromString( - "(select * from t) union all (select * from keyvalue)"))); - - // Add a TVF with a valid templated SQL body that refers to two relation - // arguments and uses a SQL WITH clause. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_two_relation_args"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType::AnyRelation(), - FunctionArgumentType::AnyRelation()}, - context_id++), - /*arg_name_list=*/{"s", "t"}, - ParseResumeLocation::FromString( - "with w1 as (select * from s),\n" - " w2 as (select * from t)\n" - "select * from w1 inner join w2 using (key) order by key limit 1"))); - - // Add a TVF with a valid templated SQL body that refers to both a scalar - // argument and a relation argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_scalar_and_relation_args"}, - FunctionSignature( - ARG_TYPE_RELATION, - {FunctionArgumentType(types::Int64Type()), - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - /*arg_name_list=*/{"x", "t"}, - ParseResumeLocation::FromString("select key from t where key < x"))); - - // This is the same TVF as above, but without a schema specified for the - // relation argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_scalar_and_relation_args_no_schema"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(types::Int64Type()), - FunctionArgumentType::AnyRelation()}, - context_id++), - /*arg_name_list=*/{"x", "t"}, - ParseResumeLocation::FromString("select key from t where key < x"))); - - // This is the same TVF as above, but without a schema specified for the - // relation argument. - const StructType* arg_type = nullptr; - std::vector fields{{"y", types::Int64Type()}, - {"z", types::StringType()}}; - ZETASQL_CHECK_OK(type_factory()->MakeStructType(fields, &arg_type)); - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_with_struct_param"}, - FunctionSignature( - ARG_TYPE_RELATION, - {FunctionArgumentType(arg_type), FunctionArgumentType::AnyRelation()}, - context_id++), - /*arg_name_list=*/{"x", "t"}, - ParseResumeLocation::FromString("select x.y from t as x"))); - - // Add a TVF with a valid templated SQL body that refers to both a scalar - // date argument and a relation argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_scalar_date_and_relation_args"}, - FunctionSignature( - ARG_TYPE_RELATION, - {FunctionArgumentType(types::DateType()), - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameDate, types::DateType()}}), - /*extra_relation_input_columns_allowed=*/true)}, - context_id++), - /*arg_name_list=*/{"d", "t"}, - ParseResumeLocation::FromString( - "select `date` from t where `date` < d"))); - - // Add a TVF with a valid templated SQL body that refers to both a scalar - // argument of any type and a relation argument of any table. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_any_scalar_and_relation_args"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY), - FunctionArgumentType::AnyRelation()}, - context_id++), - /*arg_name_list=*/{"s", "t"}, - ParseResumeLocation::FromString("select *, s from t"))); - - // Add an invalid TVF with a simple templated SQL body missing an output - // column name. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_one_missing_col_name"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, ParseResumeLocation::FromString("select 1"))); - - // Add an invalid templated SQL TVF with a parse error in the function body. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_parse_error"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, ParseResumeLocation::FromString("a b c d e"))); - - // Add an invalid templated SQL TVF with an analysis error in the function - // body. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_analysis_error"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString("select * from invalidtable"))); - - // Add an invalid templated SQL TVF where the function body is not a query. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_function_body_not_query"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "insert keyvalue (key, value) values (1, 'one')"))); - - // Add an invalid templated SQL TVF that attempts to refer to a query - // parameter. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_function_body_refer_to_parameter"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select @test_param_bool from keyvalue"))); - - // Add an invalid templated SQL TVF where the function body is empty. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_function_body_empty"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, ParseResumeLocation::FromString(""))); - - // Add an invalid templated SQL TVF that directly calls itself. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_recursive"}, FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString("select * from tvf_recursive()"))); - - // Add two invalid templated SQL TVFs that indirectly call themselves. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_calls_self_indirectly_1"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select * from tvf_calls_self_indirectly_2()"))); - - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_calls_self_indirectly_2"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select * from tvf_calls_self_indirectly_1()"))); - - // Add a templated SQL TVF that calls a templated SQL function that calls the - // original templated SQL TVF again, to make sure cycle detection works. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_calls_udf_calls_same_tvf"}, - FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select udf_calls_tvf_calls_same_udf()"))); - - // Add a templated SQL TVF that calls a TVF that triggers a deprecation - // warning. - FunctionSignature deprecation_warning_signature( - ARG_TYPE_RELATION, /*arguments=*/{}, context_id++); - deprecation_warning_signature.SetAdditionalDeprecationWarnings( - {CreateDeprecationWarning(/*id=*/1001)}); - catalog_->AddOwnedTableValuedFunction( - new TemplatedSQLTVF({"tvf_templated_calls_tvf_deprecation_warning"}, - deprecation_warning_signature, - /*arg_name_list=*/{}, - ParseResumeLocation::FromString( - "select * from tvf_deprecation_warning()"))); - - FunctionSignature signature_return_key_int64_col( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}}), - /*extra_relation_input_columns_allowed=*/true), - /*arguments=*/{FunctionArgumentType(ARG_TYPE_ARBITRARY)}, context_id++); - FunctionSignature signature_return_key_int64_and_value_string_cols( - FunctionArgumentType::RelationWithSchema( - TVFRelation({{kColumnNameKey, types::Int64Type()}, - {kColumnNameValue, types::StringType()}}), - /*extra_relation_input_columns_allowed=*/true), - /*arguments=*/ - {FunctionArgumentType(ARG_TYPE_ARBITRARY), - FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++); - FunctionSignature signature_return_value_table_string_col( - FunctionArgumentType::RelationWithSchema( - TVFRelation::ValueTable(types::StringType()), - /*extra_relation_input_columns_allowed=*/true), - /*arguments=*/{FunctionArgumentType(ARG_TYPE_ARBITRARY)}, context_id++); - - // Add a templated TVF with a required signature of a single INT64 column - // named "key". - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_x_with_required_output_schema"}, - signature_return_key_int64_col, - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select x as key"))); - - // Add an invalid templated TVF that returns a duplicate column name. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_duplicate_output_column_with_required_output_schema"}, - signature_return_key_int64_col, - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select x as key, 42 as key"))); - - // Add a templated TVF with a required non-value-table output schema. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_missing_output_column_with_required_output_schema"}, - signature_return_key_int64_col, - /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); - - // Add a templated TVF with a required value-table output schema that returns - // a value table. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_as_value_x_with_required_" - "value_table_output_schema"}, - signature_return_value_table_string_col, - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select as value x"))); - - // Add a templated TVF with a required value-table output schema that returns - // a non-value table of the same type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_x_with_required_value_table_output_schema"}, - signature_return_value_table_string_col, - /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); - - // Add a templated TVF with a required value-table output schema that returns - // NULL. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_null_with_required_value_table_output_schema"}, - signature_return_value_table_string_col, - /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select null"))); - - // Add a templated TVF with a required value-table output schema that returns - // NULL casted to string type. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_null_str_with_required_value_table_output_schema"}, - signature_return_value_table_string_col, - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select cast(null as string)"))); - - // Add a templated TVF with a required output schema with two columns. The - // function body returns the two columns in opposite order. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_return_swapped_cols_required_output_schema"}, - signature_return_key_int64_and_value_string_cols, - /*arg_name_list=*/{"key", "value"}, - ParseResumeLocation::FromString("select value, key"))); - - // Add a templated TVF with a required output schema with two columns. The - // function body returns the two columns in opposite order, plus an extra - // column. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_return_swapped_cols_plus_extra_required_output_schema"}, - signature_return_key_int64_and_value_string_cols, - /*arg_name_list=*/{"key", "value"}, - ParseResumeLocation::FromString("select value, key, 42 as x"))); - - // Add a templated TVF which returns three columns of collated string type, - // collated array type and struct type with collated field by calling COLLATE - // function over input scalar argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_collated_output_columns_with_collate_function"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY), - FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++), - /*arg_name_list=*/{"x", "y"}, - ParseResumeLocation::FromString( - "select COLLATE(x, 'und:ci') as col_ci, [COLLATE(x, 'und:ci')] as " - "col_array_ci, (COLLATE(x, 'und:ci'), y) as col_struct_ci"))); - - // Add a templated TVF which returns three columns of collated string type, - // collated array type and struct type with collated field by referencing - // collated table column. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_collated_output_columns_with_collated_column_ref"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++), - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString( - "select CONCAT(x, string_ci) as concat_ci, array_with_string_ci, " - "struct_with_string_ci from CollatedTable"))); - - // Add a templated TVF which returns columns of collated types with input - // relation argument. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_collated_output_columns_with_relation_arg"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_RELATION), - FunctionArgumentType(types::Int64Type())}, - context_id++), - /*arg_name_list=*/{"x", "y"}, - ParseResumeLocation::FromString( - "select COLLATE(col_str, 'und:ci') as col_ci, [COLLATE(col_str, " - "'und:ci')] as col_array_ci, (COLLATE(col_str, 'und:ci'), y) as " - "col_struct_ci from x"))); - - // Add a templated TVF which returns a value table with collated output - // column. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_collated_output_column_as_value_table"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY), - FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++), - /*arg_name_list=*/{"x", "y"}, - ParseResumeLocation::FromString( - "select as value CONCAT(COLLATE(x, 'und:ci'), y)"))); - - // Add a templated TVF which returns a collated output column and has an - // explicit result schema. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_collated_output_column_returns_int64_string_col"}, - signature_return_key_int64_and_value_string_cols, - /*arg_name_list=*/{"x", "y"}, - ParseResumeLocation::FromString( - "select x as key, COLLATE(y, 'und:ci') as value"))); - - // Add a templated TVF which returns a collated output column and has an - // explicit value table result schema. - catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( - {"tvf_templated_select_collated_output_column_returns_value_table_string_" - "col"}, - signature_return_value_table_string_col, - /*arg_name_list=*/{"x"}, - ParseResumeLocation::FromString("select as value COLLATE(x, 'und:ci')"))); - - // Add a templated definer-rights TVF - auto templated_definer_rights_tvf = std::make_unique( - std::vector{"definer_rights_templated_tvf"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, - context_id++), - /*arg_name_list=*/std::vector{"x"}, - ParseResumeLocation::FromString("select * from KeyValue WHERE key = x")); - templated_definer_rights_tvf->set_sql_security( - ResolvedCreateStatementEnums::SQL_SECURITY_DEFINER); - catalog_->AddOwnedTableValuedFunction( - std::move(templated_definer_rights_tvf)); - - // b/259000660: Add a templated SQL TVF whose code has a braced proto - // constructor. - auto templated_proto_braced_ctor_tvf = std::make_unique( - std::vector{"templated_proto_braced_ctor_tvf"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_RELATION)}, - context_id++), - /*arg_name_list=*/std::vector{"T"}, - ParseResumeLocation::FromString(R"sql( - SELECT NEW zetasql_test__.TestExtraPB {int32_val1 : v} AS dice_roll - FROM T)sql")); - catalog_->AddOwnedTableValuedFunction( - std::move(templated_proto_braced_ctor_tvf)); - auto templated_struct_braced_ctor_tvf = std::make_unique( - std::vector{"templated_struct_braced_ctor_tvf"}, - FunctionSignature(ARG_TYPE_RELATION, - {FunctionArgumentType(ARG_TYPE_RELATION)}, - context_id++), - /*arg_name_list=*/std::vector{"T"}, - ParseResumeLocation::FromString(R"sql( - SELECT STRUCT {int32_val1 : v} AS dice_roll - FROM T)sql")); - catalog_->AddOwnedTableValuedFunction( - std::move(templated_struct_braced_ctor_tvf)); -} - -void SampleCatalog::LoadTableValuedFunctionsWithAnonymizationUid() { - // Generate an output schema that returns every possible type. - const std::vector output_columns = - GetOutputColumnsForAllTypes(types_); - TVFRelation::ColumnList columns; - columns.reserve(output_columns.size()); - for (const auto& kv : output_columns) { - columns.emplace_back(kv.name, kv.type); - } - TVFRelation output_schema_all_types(columns); - - int context_id = 0; - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args_with_anonymization_uid"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_all_types, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - AnonymizationInfo::Create({"column_int64"}).value_or(nullptr), - output_schema_all_types)); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_relation_arg_with_anonymization_uid"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_all_types, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList({FunctionArgumentType(ARG_TYPE_RELATION)}), - context_id++), - AnonymizationInfo::Create({"column_int64"}).value_or(nullptr), - output_schema_all_types)); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_one_templated_arg_with_anonymization_uid"}, - FunctionSignature( - FunctionArgumentType::RelationWithSchema( - output_schema_all_types, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList({FunctionArgumentType(ARG_TYPE_ARBITRARY)}), - context_id++), - AnonymizationInfo::Create({"column_int64"}).value_or(nullptr), - output_schema_all_types)); - - TVFRelation output_schema_proto( - {{"user_info", GetProtoType(zetasql_test__::TestExtraPB::descriptor())}}); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args_with_nested_anonymization_uid"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_proto, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - AnonymizationInfo::Create({"user_info", "int32_val1"}).value_or(nullptr), - output_schema_proto)); - - TVFRelation output_schema_proto_value_table = TVFRelation::ValueTable( - GetProtoType(zetasql_test__::TestExtraPB::descriptor())); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args_value_table_with_anonymization_uid"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_proto_value_table, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - AnonymizationInfo::Create({"int32_val1"}).value_or(nullptr), - output_schema_proto_value_table)); - - TVFRelation output_schema_proto_value_table_with_nested_int = - TVFRelation::ValueTable( - GetProtoType(zetasql_test__::KitchenSinkPB::descriptor())); - - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args_value_table_with_nested_anonymization_uid"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_proto_value_table_with_nested_int, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - AnonymizationInfo::Create({"nested_value", "nested_int64"}) - .value_or(nullptr), - output_schema_proto_value_table_with_nested_int)); - - // Repro for unvalidated AnonymizationInfo::UserIdColumnNamePath() seen in - // b/254939522 - catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( - {"tvf_no_args_value_table_with_invalid_nested_anonymization_uid"}, - FunctionSignature(FunctionArgumentType::RelationWithSchema( - output_schema_proto_value_table_with_nested_int, - /*extra_relation_input_columns_allowed=*/false), - FunctionArgumentTypeList(), context_id++), - AnonymizationInfo::Create({"nested_value.nested_int64"}) - .value_or(nullptr), - output_schema_proto_value_table_with_nested_int)); -} - -void SampleCatalog::AddProcedureWithArgumentType(std::string type_name, - const Type* arg_type) { - auto procedure = absl::WrapUnique( - new Procedure({absl::StrCat("proc_on_", type_name)}, - {types_->get_bool(), {arg_type}, /*context_id=*/-1})); - catalog_->AddOwnedProcedure(std::move(procedure)); -} - -void SampleCatalog::LoadProcedures() { - Procedure* procedure = nullptr; - - // Procedure with no arguments. - procedure = new Procedure({"proc_no_args"}, - {types_->get_bool(), {}, /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // Add a procedure that takes a specific enum as an argument. - const EnumType* enum_TestEnum = - GetEnumType(zetasql_test__::TestEnum_descriptor()); - procedure = - new Procedure({"proc_on_TestEnum"}, - {types_->get_bool(), {enum_TestEnum}, /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // Add a procedure to illustrate how repeated/optional arguments are resolved. - procedure = - new Procedure({"proc_on_req_opt_rep"}, - {types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::REQUIRED}, - {types_->get_int64(), FunctionArgumentType::REPEATED}, - {types_->get_int64(), FunctionArgumentType::REPEATED}, - {types_->get_int64(), FunctionArgumentType::REQUIRED}, - {types_->get_int64(), FunctionArgumentType::OPTIONAL}}, - /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // Add a procedure with templated arguments. - procedure = - new Procedure({"proc_on_any_any"}, {types_->get_int64(), - {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1}, - /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // Add a procedure with templated arguments of arbitrary type. - procedure = new Procedure({"proc_on_arbitrary_arbitrary"}, - {types_->get_int64(), - {ARG_TYPE_ARBITRARY, ARG_TYPE_ARBITRARY}, - /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // Add a procedure with one repeated argument. - procedure = new Procedure( - {"proc_on_rep"}, {types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::REPEATED}}, - /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // Add a procedure with one optional argument. - procedure = new Procedure( - {"proc_on_opt"}, {types_->get_int64(), - {{types_->get_int64(), FunctionArgumentType::OPTIONAL}}, - /*context_id=*/-1}); - catalog_->AddOwnedProcedure(procedure); - - // These sample procedures are named 'proc_on_' with one argument of - // type that returns a bool. - AddProcedureWithArgumentType("bool", types_->get_bool()); - AddProcedureWithArgumentType("int32", types_->get_int32()); - AddProcedureWithArgumentType("int64", types_->get_int64()); - AddProcedureWithArgumentType("uint32", types_->get_uint32()); - AddProcedureWithArgumentType("uint64", types_->get_uint64()); - AddProcedureWithArgumentType("float", types_->get_float()); - AddProcedureWithArgumentType("double", types_->get_double()); - AddProcedureWithArgumentType("date", types_->get_date()); - AddProcedureWithArgumentType("timestamp", types_->get_timestamp()); - AddProcedureWithArgumentType("string", types_->get_string()); - - // Add a procedure with named_lambda. - { - FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, - FunctionArgumentTypeOptions().set_argument_name("named_lambda", - kNamedOnly)); - auto procedure = std::make_unique( - std::vector{"proc_on_named_lambda"}, - FunctionSignature{types_->get_int64(), - {ARG_TYPE_ANY_1, named_lambda}, - /*context_id=*/-1}); - catalog_->AddOwnedProcedure(std::move(procedure)); - } -} - -void SampleCatalog::LoadConstants() { - // Load constants that are owned by 'catalog_'. - std::unique_ptr int64_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"TestConstantInt64"}, - Value::Int64(1L), &int64_constant)); - catalog_->AddOwnedConstant(int64_constant.release()); - std::unique_ptr string_constant; - ZETASQL_CHECK_OK( - SimpleConstant::Create(std::vector{"TestConstantString"}, - Value::String("foo"), &string_constant)); - catalog_->AddOwnedConstant(string_constant.release()); - - std::unique_ptr string_constant_nonstandard_name; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"Test Constant-String"}, - Value::String("foo bar"), &string_constant_nonstandard_name)); - catalog_->AddOwnedConstant(string_constant_nonstandard_name.release()); - - // Load a constant that is not owned by 'catalog_'. - const ProtoType* const proto_type = - GetProtoType(zetasql_test__::KitchenSinkPB::descriptor()); - absl::Cord text_proto = absl::Cord("int64_key_1: 1, int64_key_2: -999"); - - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"TestConstantProto"}, - Value::Proto(proto_type, text_proto), - &owned_constant_)); - catalog_->AddConstant(owned_constant_.get()); - - // Load a constant that conflicts with a table. - const StructType* table_struct_type; - ZETASQL_CHECK_OK(types_->MakeStructType({{"key", types_->get_int32()}}, - &table_struct_type)); - std::unique_ptr table_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"NameConflictTable"}, - Value::Struct(table_struct_type, {Value::Int32(-3456)}), - &table_constant)); - catalog_->AddOwnedConstant(table_constant.release()); - - // Load a constant that conflicts with a value table. - std::unique_ptr value_table_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"Int32ValueTable"}, - Value::Int32(3), &value_table_constant)); - catalog_->AddOwnedConstant(value_table_constant.release()); - - // Load a constant that conflicts with a type. - std::unique_ptr type_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"NameConflictType"}, - Value::Bool(false), &type_constant)); - catalog_->AddOwnedConstant(type_constant.release()); - - // Load a constant that conflicts with zero-argument functions. - std::unique_ptr zero_argument_function_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"sort_count"}, - Value::Int64(4), - &zero_argument_function_constant)); - catalog_->AddOwnedConstant(zero_argument_function_constant.release()); - - std::unique_ptr - zero_argument_function_constant_with_optional_parentheses; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"CURRENT_DATE"}, Value::Int64(4), - &zero_argument_function_constant_with_optional_parentheses)); - catalog_->AddOwnedConstant( - zero_argument_function_constant_with_optional_parentheses.release()); - - // Load a constant that conflicts with a multi-argument function. - std::unique_ptr multi_argument_function_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"concat"}, - Value::Int64(5), - &multi_argument_function_constant)); - catalog_->AddOwnedConstant(multi_argument_function_constant.release()); - - // Load a constant that conflicts with a zero-argument TVF. - std::unique_ptr zero_argument_tvf_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"tvf_no_args"}, - Value::Int64(6), - &zero_argument_tvf_constant)); - catalog_->AddOwnedConstant(zero_argument_tvf_constant.release()); - - // Load a constant that conflicts with a multi-argument TVF. - std::unique_ptr multi_argument_tvf_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"tvf_exactly_1_int64_arg"}, Value::Int64(7), - &multi_argument_tvf_constant)); - catalog_->AddOwnedConstant(multi_argument_tvf_constant.release()); - - // Load a constant that conflicts with a zero-argument procedure. - // The multi-argument case is handled in the nested catalog. - std::unique_ptr constant; - ZETASQL_CHECK_OK( - SimpleConstant::Create({"proc_no_args"}, Value::Bool(true), &constant)); - catalog_->AddOwnedConstant(constant.release()); - - // Load a constant that conflicts with a catalog. - const StructType* nested_struct_type; - ZETASQL_CHECK_OK(types_->MakeStructType( - {{"a", types_->get_int32()}, {"b", types_->get_int64()}}, - &nested_struct_type)); - const StructType* catalog_struct_type; - ZETASQL_CHECK_OK( - types_->MakeStructType({{"a", types_->get_int32()}, - {"nested_catalog_catalog", nested_struct_type}, - {"TestConstantBool", types_->get_bool()}}, - &catalog_struct_type)); - std::unique_ptr catalog_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"nested_catalog_with_catalog"}, - Value::Struct(catalog_struct_type, - {Value::Int32(-3456), - Value::Struct(nested_struct_type, - {Value::Int32(-3434), Value::Int64(4333)}), - Value::Bool(false)}), - &catalog_constant)); - catalog_->AddOwnedConstant(catalog_constant.release()); - - // Load a constant that conflicts with an expression column in standalone - // expression resolution. - std::unique_ptr standalone_expression_constant; - ZETASQL_CHECK_OK( - SimpleConstant::Create(std::vector{"column_KitchenSink"}, - Value::Int64(8), &standalone_expression_constant)); - catalog_->AddOwnedConstant(standalone_expression_constant.release()); - - // Load a constant with a name that resembles a system variable. - std::unique_ptr sysvar1_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"@@sysvar1"}, - Value::Int64(8), &sysvar1_constant)); - catalog_->AddOwnedConstant(sysvar1_constant.release()); - - std::unique_ptr sysvar2_constant; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"@@sysvar2"}, - Value::Int64(8), &sysvar2_constant)); - catalog_->AddOwnedConstant(sysvar2_constant.release()); - - // Script variables are managed by the ScriptExecutor. Eventually, they get - // put into the catalog as constants. For testing, we'll add some "variables" - // here. - std::unique_ptr string_variable_foo; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"string_variable_foo"}, - Value::String("string_variable_foo_value"), &string_variable_foo)); - catalog_->AddOwnedConstant(std::move(string_variable_foo)); - - std::unique_ptr string_variable_bar; - ZETASQL_CHECK_OK(SimpleConstant::Create( - std::vector{"string_variable_bar"}, - Value::String("string_variable_bar_value"), &string_variable_bar)); - catalog_->AddOwnedConstant(std::move(string_variable_bar)); - - std::unique_ptr int_variable_foo; - ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"int_variable_foo"}, - Value::Int32(4), &int_variable_foo)); - catalog_->AddOwnedConstant(std::move(int_variable_foo)); -} - -void SampleCatalog::LoadConnections() { - auto connection1 = std::make_unique("connection1"); - auto connection2 = std::make_unique("connection2"); - auto connection3 = std::make_unique("connection3"); - auto nested_connection = - std::make_unique("NestedConnection"); - owned_connections_[connection1->Name()] = std::move(connection1); - owned_connections_[connection2->Name()] = std::move(connection2); - owned_connections_[connection3->Name()] = std::move(connection3); - // This connection properly should be ONLY added to the nested catalog (e.g. - // skipped in the loop below). But, due to how lookup works with nested - // catalogs in simple_catalog currently, it will fail if it's not in the top- - // level catalog as well. - owned_connections_[nested_connection->Name()] = std::move(nested_connection); - for (auto it = owned_connections_.begin(); it != owned_connections_.end(); - ++it) { - catalog_->AddConnection(it->second.get()); - } -} - -void SampleCatalog::LoadSequences() { - auto sequence1 = std::make_unique("sequence1"); - auto sequence2 = std::make_unique("sequence2"); - owned_sequences_[sequence1->Name()] = std::move(sequence1); - owned_sequences_[sequence2->Name()] = std::move(sequence2); - for (auto it = owned_sequences_.begin(); it != owned_sequences_.end(); ++it) { - catalog_->AddSequence(it->second.get()); - } -} - -void SampleCatalog::AddOwnedTable(SimpleTable* table) { - catalog_->AddOwnedTable(absl::WrapUnique(table)); - zetasql_base::InsertOrDie(&tables_, table->Name(), table); -} - -void SampleCatalog::LoadWellKnownLambdaArgFunctions() { - const Type* int64_type = types_->get_int64(); - const Type* bool_type = types_->get_bool(); - - // Models ARRAY_FILTER - auto function = std::make_unique( - "fn_array_filter", "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_ARRAY_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, bool_type)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, FUNCTION<->BOOL>) -> >", - function->GetSignature(0)->DebugString()); - function->AddSignature( - {ARG_ARRAY_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1, int64_type}, bool_type)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, FUNCTION<(, INT64)->BOOL>) -> >", - function->GetSignature(1)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Models ARRAY_TRANSFORM - function = std::make_unique("fn_array_transform", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_ARRAY_TYPE_ANY_2, - {ARG_ARRAY_TYPE_ANY_1, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, FUNCTION<->>) -> >", - function->GetSignature(0)->DebugString()); - function->AddSignature({ARG_ARRAY_TYPE_ANY_2, - {ARG_ARRAY_TYPE_ANY_1, - FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1, int64_type}, ARG_TYPE_ANY_2)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, FUNCTION<(, INT64)->>) -> >", - function->GetSignature(1)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - function = std::make_unique("fn_fp_array_sort", "sample_functions", - Function::SCALAR); - function->AddSignature({ARG_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, - FunctionArgumentType::Lambda( - {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1}, int64_type)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, FUNCTION<(, )->INT64>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Models REDUCE function, which takes an input array, an initial state and a - // function to run over each element with the current state to produce the - // final state. - function = std::make_unique("fn_fp_array_reduce", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_2, - {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_2, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_2, ARG_TYPE_ANY_1}, - ARG_TYPE_ANY_2)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, , FUNCTION<(, )->>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); -} - -void SampleCatalog::LoadContrivedLambdaArgFunctions() { - const Type* int64_type = types_->get_int64(); - const Type* string_type = types_->get_string(); - const Type* bool_type = types_->get_bool(); - - // Demonstrate having to get common super type for two different concrete type - // for a single template type. - auto function = std::make_unique( - "fn_fp_T_T_LAMBDA", "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_1, - {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, bool_type)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(, , FUNCTION<->BOOL>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // fn_fp_ArrayT_T is provided here to show current behavior to make it easier - // for reader to understand fn_fp_ArrayT_T_LAMBDA. - function = std::make_unique("fn_fp_ArrayT_T", "sample_functions", - Function::SCALAR); - function->AddSignature({ARG_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_1}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, ) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Demostrate case where we don't have common super type for T1, due to - // ARRAY. - function = std::make_unique("fn_fp_ArrayT_T_LAMBDA", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_1, - {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_1, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, bool_type)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(>, , FUNCTION<->BOOL>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Demonstrate that lambda argument type inference conflict with final - // concrete type of templated type influenced by lambda body type. - function = std::make_unique("fn_fp_T_LAMBDA_RET_T", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_1, - {ARG_TYPE_ANY_1, - FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(, FUNCTION<->>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - const auto named_required_format_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( - "format_string", kPositionalOrNamed)); - // Signature with lambda and named argument before lambda. - function = std::make_unique("fn_fp_named_then_lambda", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_1, - {named_required_format_arg, - FunctionArgumentType::Lambda({int64_type}, ARG_TYPE_ANY_1)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(STRING format_string, FUNCTION>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Signature with lambda and named argument after lambda. - function = std::make_unique("fn_fp_lambda_then_named", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_1, - {FunctionArgumentType::Lambda({int64_type}, ARG_TYPE_ANY_1), - named_required_format_arg}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(FUNCTION>, STRING format_string) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Signature with lambda and repeated arguments after lambda. - const auto repeated_arg = - FunctionArgumentType(types_->get_int64(), FunctionArgumentType::REPEATED); - function = std::make_unique("fn_fp_lambda_then_repeated", - "sample_functions", Function::SCALAR); - function->AddSignature( - {ARG_TYPE_ANY_1, - {FunctionArgumentType::Lambda({int64_type}, ARG_TYPE_ANY_1), - repeated_arg}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(FUNCTION>, repeated INT64) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - // Signature with lambda and repeated arguments before lambda. - function = std::make_unique("fn_fp_repeated_arg_then_lambda", - "sample_functions", Function::SCALAR); - function->AddSignature({ARG_TYPE_ANY_1, - {repeated_arg, FunctionArgumentType::Lambda( - {int64_type}, ARG_TYPE_ANY_1)}, - /*context_id=*/-1}); - ABSL_CHECK_EQ("(repeated INT64, FUNCTION>) -> ", - function->GetSignature(0)->DebugString()); - catalog_->AddOwnedFunction(function.release()); - - AddFunction( - "fn_fp_repeated_arg_then_lambda_string", Function::SCALAR, - {SignatureBuilder() - .AddArg(ArgBuilder().Repeated().String()) - .AddArg(FunctionArgumentType::Lambda({string_type}, ARG_TYPE_ANY_1)) - .Returns(ArgBuilder().T1()) - .Build()}); - /* - // Signature with lambda and repeated arguments before lambda. - function = std::make_unique("fn_fp_repeated_arg_then_lambda_string", - "sample_functions", Function::SCALAR); - const auto repeated_string_arg = FunctionArgumentType( - types_->get_string(), FunctionArgumentType::REPEATED); - - function->AddSignature( - {ARG_TYPE_ANY_1, - {repeated_string_arg, - FunctionArgumentType::Lambda({string_type}, ARG_TYPE_ANY_1)}}); - catalog_->AddOwnedFunction(function.release()); - */ -} - -void SampleCatalog::AddSqlDefinedFunction( - absl::string_view name, FunctionSignature signature, - const std::vector& argument_names, - absl::string_view function_body_sql, - const LanguageOptions& language_options) { - AnalyzerOptions analyzer_options; - analyzer_options.set_language(language_options); - ABSL_CHECK_EQ(argument_names.size(), signature.arguments().size()); - for (int i = 0; i < argument_names.size(); ++i) { - ABSL_CHECK_NE(signature.argument(i).type(), nullptr); - ZETASQL_CHECK_OK(analyzer_options.AddExpressionColumn( - argument_names[i], signature.argument(i).type())); - } - std::unique_ptr analyzer_output; - ZETASQL_CHECK_OK(AnalyzeExpressionForAssignmentToType( - function_body_sql, analyzer_options, catalog_.get(), - catalog_->type_factory(), signature.result_type().type(), - &analyzer_output)); - std::unique_ptr function; - ZETASQL_CHECK_OK(SQLFunction::Create(name, FunctionEnums::SCALAR, {signature}, - /*function_options=*/{}, - analyzer_output->resolved_expr(), argument_names, - /*aggregate_expression_list=*/{}, - /*parse_resume_location=*/{}, &function)); - catalog_->AddOwnedFunction(function.release()); - sql_object_artifacts_.emplace_back(std::move(analyzer_output)); -} - -// Add a SQL function to catalog starting from a full create_function -// statement. -void SampleCatalog::AddSqlDefinedFunctionFromCreate( - absl::string_view create_function, const LanguageOptions& language_options, - bool inline_sql_functions, - std::optional function_options) { - // Ensure the language options used allow CREATE FUNCTION - LanguageOptions language = language_options; - language.AddSupportedStatementKind(RESOLVED_CREATE_FUNCTION_STMT); - language.EnableLanguageFeature(FEATURE_V_1_3_INLINE_LAMBDA_ARGUMENT); - language.EnableLanguageFeature(FEATURE_V_1_1_WITH_ON_SUBQUERY); - language.EnableLanguageFeature(FEATURE_TEMPLATE_FUNCTIONS); - language.EnableLanguageFeature(FEATURE_CREATE_AGGREGATE_FUNCTION); - language.EnableLanguageFeature(FEATURE_V_1_1_HAVING_IN_AGGREGATE); - language.EnableLanguageFeature( - FEATURE_V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE); - AnalyzerOptions analyzer_options; - analyzer_options.set_language(language); - analyzer_options.set_enabled_rewrites(/*rewrites=*/{}); - analyzer_options.enable_rewrite(REWRITE_INLINE_SQL_FUNCTIONS, - inline_sql_functions); - analyzer_options.enable_rewrite(REWRITE_INLINE_SQL_UDAS, - inline_sql_functions); - sql_object_artifacts_.emplace_back(); - ZETASQL_CHECK_OK(AddFunctionFromCreateFunction( - create_function, analyzer_options, /*allow_persistent_function=*/true, - function_options, sql_object_artifacts_.back(), *catalog_)); -} - -void SampleCatalog::LoadSqlFunctions(const LanguageOptions& language_options) { - LoadScalarSqlFunctions(language_options); - LoadScalarSqlFunctionsFromStandardModule(language_options); - LoadDeepScalarSqlFunctions(language_options); - LoadScalarSqlFunctionTemplates(language_options); - LoadAggregateSqlFunctions(language_options); -} - -void SampleCatalog::LoadScalarSqlFunctions( - const LanguageOptions& language_options) { - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION NullaryPi() RETURNS FLOAT64 AS (3.141597); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION NullaryWithSubquery() - AS (EXISTS (SELECT 1 FROM UNNEST([1,2,3]) AS e WHERE e = 2)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION NullaryWithCommonTableExpression() - AS (EXISTS (WITH t AS (SELECT [1,2,3] AS arr) - SELECT 1 FROM t, t.arr as e WHERE e = 2)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION NullaryWithLambda() - AS (ARRAY_INCLUDES([1, 2, 3], e -> e = 2)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION NullaryWithSqlFunctionCallPreInlined() - AS (NullaryPi()); )", - language_options, /*inline_sql_functions=*/true); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION NullaryWithSqlFunctionCallNotPreInlined() - AS (NullaryPi()); )", - language_options, /*inline_sql_functions=*/false); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION UnaryIncrement(a INT64) AS ( a + 1 ); )", - language_options); - - // Creating the function using AnalyzeExpressionForAssignmentToType results in - // a function body expression with ResolvedExpressionColumn where arguments - // are referenced instead of a ResolvedArgumentRef. That appears to be done - // in several catalogs, so we want to test that case too. - const Type* int64_type = types_->get_int64(); - FunctionSignature int_int = {int64_type, - {int64_type}, - /*context_id=*/static_cast(0)}; - AddSqlDefinedFunction("UnaryIncrementRefArg", int_int, {"a"}, "a + 1", - language_options); -} - -void SampleCatalog::LoadScalarSqlFunctionsFromStandardModule( - const LanguageOptions& language_options) { - // Function from sql/modules/math_utils/math_utils.sqlm - // References each of its arguments more than once. - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION FloorDiv(n INT64, d INT64) RETURNS INT64 - AS ( DIV(n, d) - IF(n < 0 AND MOD(n, d) != 0, 1, 0) ); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION IgnoresArg(a INT64) AS (1); )", language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION ReferencesArgsInsideSubquery(a INT64, b INT64) - AS ((SELECT a + b)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION ReferencesArgInsideCte(a INT64) - AS ((WITH t AS (SELECT a AS c) SELECT c FROM t)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION ReferencesArgOutsideCte(a INT64) - AS ((WITH t AS (SELECT 1 AS c) SELECT c + a FROM t)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION ReferencesArgInsideLambda(a INT64) - AS (ARRAY_TRANSFORM([1, 2, 3], e->e = a)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION scalar_function_definer_rights() SQL SECURITY DEFINER - AS ((SELECT COUNT(*) FROM KeyValue)); )", - language_options, - /*inline_sql_functions=*/true); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION stable_array_sort(arr ANY TYPE) - AS ((SELECT ARRAY_AGG(e ORDER BY e, off) - FROM UNNEST(arr) AS e WITH OFFSET off)); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE FUNCTION template_with_typed_arg( - arg_any ANY TYPE, arg_string STRING) - AS (arg_any IS NULL OR arg_string IS NULL); - )sql", - language_options); -} - -void SampleCatalog::LoadDeepScalarSqlFunctions( - const LanguageOptions& language_options) { - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION CallsPi0() AS (NullaryPi());)", language_options, - /*inline_sql_functions=*/false); - - for (int i = 0; i < 25; ++i) { - AddSqlDefinedFunctionFromCreate( - absl::StrCat("CREATE FUNCTION CallsPi", i + 1, "() AS (CallsPi", i, - "());"), - language_options, /*inline_sql_functions=*/false); - } -} - -void SampleCatalog::LoadScalarSqlFunctionTemplates( - const LanguageOptions& language_options) { - // This function is logically equivalent to ARRAY_REVERSE - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION REVERSE_ARRAY(input_arr ANY TYPE) - AS (IF (input_arr IS NULL, - NULL, - ARRAY(SELECT e - FROM UNNEST(input_arr) AS e WITH OFFSET - ORDER BY OFFSET desc))); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION TimesTwo(arg ANY TYPE) AS (arg + arg); )", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"( CREATE FUNCTION SUM_DOUBLE_ARRAY(input_arr ANY TYPE) - AS ((SELECT SUM(e) - FROM UNNEST(ARRAY_TRANSFORM(input_arr, e->TimesTwo(e))) e));)", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql(CREATE TEMP FUNCTION b290673529(thing_id ANY TYPE) RETURNS BOOL - AS ( - thing_id IN (SELECT thing_id FROM (SELECT 1 AS thing_id)) - ); - )sql", - language_options, /*inline_sql_functions=*/false); -} - -void SampleCatalog::LoadAggregateSqlFunctions( - const LanguageOptions& language_options) { - AddSqlDefinedFunctionFromCreate( - R"sql( CREATE AGGREGATE FUNCTION NotAggregate() AS (1 + 1);)sql", - language_options); - - // This function provides some open-box testing of the inliner in that it - // enables a test to ensure columns internal to the function body are properly - // re-mapped and don't result in column id collisions in the rewritten - // query if the function is called twice. - AddSqlDefinedFunctionFromCreate( - R"sql( CREATE AGGREGATE FUNCTION NotAggregateInternalColumn() AS ( - (SELECT a + a FROM (SELECT 1 AS a)) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( CREATE AGGREGATE FUNCTION CountStar() AS (COUNT(*));)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( CREATE AGGREGATE FUNCTION CallsCountStar() AS (CountStar());)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION NotAggregateArgs( - a INT64 NOT AGGREGATE - ) AS ( - a + a - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION SumOfNotAggregateArg( - not_agg_arg INT64 NOT AGGREGATE - ) AS ( - SUM(not_agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION ExpressionOutsideSumOfNotAggregate( - not_agg_arg INT64 NOT AGGREGATE - ) AS ( - not_agg_arg + SUM(not_agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION ExpressionInsideSumOfNotAggregate( - not_agg_arg INT64 NOT AGGREGATE - ) AS ( - SUM(not_agg_arg + not_agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION SumOfAggregateArgs( - agg_arg INT64 - ) AS ( - SUM(agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION SumExpressionOfAggregateArgs( - agg_arg INT64 - ) AS ( - SUM(agg_arg + agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION ExprOutsideSumExpressionOfAggregateArgs( - agg_arg INT64 - ) AS ( - 1 + SUM(agg_arg + agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION ExprOutsideAndInsideSum( - agg_arg INT64, - not_agg_arg INT64 NOT AGGREGATE - ) AS ( - not_agg_arg + SUM(not_agg_arg + agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaWithHavingMax( - agg_arg STRING, - another_agg_arg INT64 - ) AS ( - ARRAY_AGG(agg_arg HAVING MAX another_agg_arg) - );)sql", - language_options); - - // TODO: Add example with ARRAY_AGG( ... ORDER BY ... ) - // after that is enabled in UDA bodies. - - // TODO: Add example with WITH GROUP ROWS( ... ) after that - // is enabled in UDA bodies. - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaTemplateWithHavingMax( - agg_arg ANY TYPE - ) AS ( - ARRAY_AGG(agg_arg.a HAVING MAX agg_arg.b) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaTemplateWithIgnoreNulls( - agg_arg ANY TYPE - ) AS ( - STRUCT ( - ARRAY_AGG(agg_arg IGNORE NULLS) AS ignore_nulls, - ARRAY_AGG(agg_arg RESPECT NULLS) AS respect_nulls, - ARRAY_AGG(agg_arg) AS whatever_nulls - ) - );)sql", - language_options); - - // A token UDA with LIMIT in it. We can't do ORDER BY, so the use of LIMIT - // is ... questionable. - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaTemplateWithLimitZero( - agg_arg ANY TYPE - ) AS ( - STRUCT ( - ARRAY_AGG(agg_arg LIMIT 0) AS ignore_nulls - ) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaCallingAnotherUda( - agg_arg ANY TYPE - ) AS ( - CountStar() - COUNT(agg_arg.a) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaCallingAnotherUdaWithNonAggregateArgs( - agg_arg INT64, - non_agg_arg ANY TYPE NOT AGGREGATE - ) AS ( - ExprOutsideAndInsideSum(agg_arg + 1, non_agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaSafeCallingAnotherUdaWithNonAggregateArgs( - agg_arg INT64, - non_agg_arg ANY TYPE NOT AGGREGATE - ) AS ( - SAFE.ExprOutsideAndInsideSum(agg_arg + 1, non_agg_arg) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaInvokingUdf( - agg_arg ANY TYPE - ) AS ( - ExprOutsideAndInsideSum(TimesTwo(agg_arg), 3) - );)sql", - language_options); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaInvokingSafeUdf( - agg_arg ANY TYPE - ) AS ( - ExprOutsideAndInsideSum(SAFE.TimesTwo(agg_arg), 3) - );)sql", - language_options); - - FunctionOptions aggregate_calling_clauses_enabled; - aggregate_calling_clauses_enabled - // No need to test clamped between modifier since it is hard-coded to only - // work on functions with specific names. - .set_supports_distinct_modifier(true) - .set_supports_having_modifier(true) - .set_supports_null_handling_modifier(true) - .set_supports_limit(true) - .set_supports_order_by(true) - .set_uses_upper_case_sql_name(false); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaConcreteWithOptionalCallingClausesEnabled( - agg_arg INT64 - ) AS ( NULL ); - )sql", - language_options, /*inline_sql_functions=*/false, - aggregate_calling_clauses_enabled); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaTemplateWithOptionalCallingClausesEnabled( - agg_arg ANY TYPE - ) AS ( NULL ); - )sql", - language_options, /*inline_sql_functions=*/false, - aggregate_calling_clauses_enabled); - - AddSqlDefinedFunctionFromCreate( - R"sql( - CREATE AGGREGATE FUNCTION UdaInlinedOnCreate(x INT64) AS ( - SumExpressionOfAggregateArgs(x) - );)sql", - language_options, /*inline_sql_functions=*/true); -} - -void SampleCatalog::ForceLinkProtoTypes() { - google::protobuf::LinkMessageReflection(); + const BuiltinFunctionOptions& builtin_function_options, + TypeFactory* type_factory) + : SampleCatalogImpl(type_factory) { + ZETASQL_CHECK_OK(SampleCatalogImpl::LoadCatalogImpl(builtin_function_options)); } } // namespace zetasql diff --git a/zetasql/testdata/sample_catalog.h b/zetasql/testdata/sample_catalog.h index 92761e602..728e437b7 100644 --- a/zetasql/testdata/sample_catalog.h +++ b/zetasql/testdata/sample_catalog.h @@ -17,36 +17,26 @@ #ifndef ZETASQL_TESTDATA_SAMPLE_CATALOG_H_ #define ZETASQL_TESTDATA_SAMPLE_CATALOG_H_ -#include -#include -#include -#include -#include - -#include "zetasql/public/analyzer_output.h" #include "zetasql/public/builtin_function_options.h" #include "zetasql/public/function.h" #include "zetasql/public/function_signature.h" #include "zetasql/public/language_options.h" -#include "zetasql/public/simple_catalog.h" #include "zetasql/public/type.h" -#include "zetasql/resolved_ast/resolved_ast.h" -#include "absl/container/node_hash_map.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "google/protobuf/descriptor.h" -#include "google/protobuf/descriptor_database.h" +#include "zetasql/testdata/sample_catalog_impl.h" +#include "absl/status/status.h" namespace zetasql { // SampleCatalog provides a SimpleCatalog loaded with a shared sample schema, -// used by several tests. Look at the .cc file to see what's in the Catalog. +// used by several tests. Look at sample_catalog_impl.cc to see what's in the +// Catalog. // All proto types compiled into this binary will be available in the catalog. -class SampleCatalog { +class SampleCatalog : public SampleCatalogImpl { public: // Default constructor using default LanguageOptions and a locally owned // TypeFactory. SampleCatalog(); + // Constructor given 'language_options' and an optional 'type_factory'. // If 'type_factory' is specified then it must outlive this SampleCatalog // and this SampleCatalog does not take ownership of it. If 'type_factory' @@ -65,219 +55,11 @@ class SampleCatalog { SampleCatalog(const SampleCatalog&) = delete; SampleCatalog& operator=(const SampleCatalog&) = delete; - ~SampleCatalog(); - - SimpleCatalog* catalog() { return catalog_.get(); } - - TypeFactory* type_factory() { return types_; } - - // Useful for configuring EvaluatorTableIterators for tables in the catalog. - SimpleTable* GetTableOrDie(absl::string_view name); - absl::StatusOr GetTable(absl::string_view name); + ~SampleCatalog() override = default; private: - std::unique_ptr alt_descriptor_database_; - std::unique_ptr alt_descriptor_pool_; - std::unique_ptr ambiguous_has_descriptor_pool_; - std::unique_ptr internal_type_factory_; - std::unique_ptr catalog_; - TypeFactory* types_; // Not owned. - - void LoadCatalog(const LanguageOptions& language_options); - // Returns a copy of `options` that supplies default types for function - // signatures that expect them. This can override map entries in - // the original `options`'s `supplied_argument_types`. - ZetaSQLBuiltinFunctionOptions LoadDefaultSuppliedTypes( - const ZetaSQLBuiltinFunctionOptions& options); - void LoadCatalogBuiltins(const LanguageOptions& language_options); - void LoadCatalogBuiltins( - const ZetaSQLBuiltinFunctionOptions& builtin_function_options); - void LoadCatalogImpl(const LanguageOptions& language_options); - void LoadTypes(); - void LoadTables(); - void LoadProtoTables(); - void LoadViews(const LanguageOptions& language_options); - void LoadNestedCatalogs(); - void AddFunctionWithArgumentType(std::string type_name, const Type* arg_type); - - // Creates and adds the Function to the catalog. - // This performs some basic validation. - // The group used is 'sample_functions'. - const Function* AddFunction( - absl::string_view name, Function::Mode mode, - std::vector function_signatures, - FunctionOptions function_options = {}); - - void LoadFunctionsWithStructArgs(); - void LoadFunctions(); - // Similar to `LoadFunctions`, loads test functions into the sample catalog. - // We have to split the original `LoadFunctions` function into two otherwise - // the stack size limit is reached. - void LoadFunctions2(); - void LoadExtendedSubscriptFunctions(); - void LoadFunctionsWithDefaultArguments(); - void LoadTemplatedSQLUDFs(); - - // Loads several table-valued functions into the sample catalog. For a full - // list of the signatures added, please see the beginning of the method - // definition. LoadTableValuedFunctions() has gotten so large that we have to - // split it up in order to avoid lint warnings. - void LoadTableValuedFunctions1(); - void LoadTableValuedFunctions2(); - void LoadTableValuedFunctionsWithEvaluators(); - void LoadTVFWithExtraColumns(); - void LoadConnectionTableValuedFunctions(); - void LoadDescriptorTableValuedFunctions(); - void LoadTableValuedFunctionsWithDeprecationWarnings(); - - // Add a SQL table function to catalog starting from a full create table - // function statement. - void AddSqlDefinedTableFunctionFromCreate( - absl::string_view create_table_function, - const LanguageOptions& language_options, - absl::string_view user_id_column = ""); - void LoadNonTemplatedSqlTableValuedFunctions( - const LanguageOptions& language_options); - void LoadTemplatedSQLTableValuedFunctions(); - void LoadTableValuedFunctionsWithAnonymizationUid(); - - void AddProcedureWithArgumentType(std::string type_name, - const Type* arg_type); - void LoadProcedures(); - void LoadConstants(); - void LoadConnections(); - void LoadSequences(); - // Load signatures for well known functional programming functions for example - // FILTER, TRANSFORM, REDUCE. - void LoadWellKnownLambdaArgFunctions(); - // Contrived signatures are loaded in order to demonstrate the behavior of - // lambda signature matching and resolving for unusual cases. - // This include: - // * Using lambda with repeated arguments. - // * Using lambda with named arguments. - // * Possible signatures that could result in type inference failure for - // various combinations of templated lambda arguments and other arguments. - void LoadContrivedLambdaArgFunctions(); - - void AddOwnedTable(SimpleTable* table); - void AddGeneratedColumnToTable(std::string column_name, - std::vector expression_columns, - std::string generated_expr, - SimpleTable* table); - - // Add a SQLFunction to catalog_ with a SQL expression as the function body. - void AddSqlDefinedFunction(absl::string_view name, - FunctionSignature signature, - const std::vector& argument_names, - absl::string_view function_body_sql, - const LanguageOptions& language_options); - // Add a SQL function to catalog starting from a full create_function - // statement. - void AddSqlDefinedFunctionFromCreate( - absl::string_view create_function, - const LanguageOptions& language_options, bool inline_sql_functions = true, - std::optional function_options = std::nullopt); - - void LoadSqlFunctions(const LanguageOptions& language_options); - - // Helpers for LoadSqlFunctions so that its both logically broken up and - // so that its less troublesome for dbg build stacks. - void LoadScalarSqlFunctions(const LanguageOptions& language_options); - void LoadScalarSqlFunctionsFromStandardModule( - const LanguageOptions& language_options); - void LoadDeepScalarSqlFunctions(const LanguageOptions& language_options); - void LoadScalarSqlFunctionTemplates(const LanguageOptions& language_options); - void LoadAggregateSqlFunctions(const LanguageOptions& language_options); - - // This can be used force linking of a proto for the generated_pool. - // This may be required if a proto is referenced in file-based tests - // (such as analyzer test), but not otherwise directly linked. - // We don't force linking the entire test_schema since we may need - // to test this partial-linkage in other contexts (and it's expensive). - // Note, this function isn't actually called. But it _does_ need to be - // defined in the class to ensure it can't be pruned. - // This is a all weird linker magic. - void ForceLinkProtoTypes(); - - const ProtoType* GetProtoType(const google::protobuf::Descriptor* descriptor); - const EnumType* GetEnumType(const google::protobuf::EnumDescriptor* descriptor); - - const ArrayType* int32array_type_; - const ArrayType* int64array_type_; - const ArrayType* uint32array_type_; - const ArrayType* uint64array_type_; - const ArrayType* bytes_array_type_; - const ArrayType* bool_array_type_; - const ArrayType* float_array_type_; - const ArrayType* double_array_type_; - const ArrayType* date_array_type_; - const ArrayType* string_array_type_; - const ArrayType* timestamp_array_type_; - const ArrayType* proto_array_type_; - const ArrayType* struct_array_type_; - const ArrayType* json_array_type_; - const ArrayType* numeric_array_type_; - const ArrayType* bignumeric_array_type_; - const ArrayType* interval_array_type_; - - const EnumType* enum_TestEnum_; - const EnumType* enum_AnotherTestEnum_; - const EnumType* enum_TestEnumWithAnnotations_; - const ProtoType* proto_KitchenSinkPB_; - const ProtoType* proto_MessageWithKitchenSinkPB_; - const ProtoType* proto_CivilTimeTypesSinkPB_; - const ProtoType* proto_TestExtraPB_; - const ProtoType* proto_abPB_; - const ProtoType* proto_bcPB_; - - const ProtoType* proto_EmptyMessage_; - const ProtoType* proto3_KitchenSinkPB_; - const ProtoType* proto3_MessageWithInvalidMap_; - const ProtoType* proto_ambiguous_has_; - const ProtoType* proto_field_formats_proto_; - const ProtoType* proto_MessageWithMapField_; - const ProtoType* proto_approx_distance_function_options_; - - // STRUCT - const StructType* struct_type_; - // STRUCT> - const StructType* nested_struct_type_; - // STRUCT>> - const StructType* doubly_nested_struct_type_; - // STRUCT, - // z ARRAY>> - const StructType* struct_with_array_field_type_; - // STRUCT - const StructType* struct_with_one_field_type_; - // STRUCT> - const StructType* struct_with_kitchen_sink_type_; - // STRUCT>> - const StructType* struct_of_array_of_struct_with_kitchen_sink_type_; - - const Type* int32map_type_; - const Type* int64map_type_; - const Type* bytesmap_type_; - - const SimpleTable* key_value_table_; - - // A constant to load. Owned by this catalog to get coverage for - // SimpleCatalog::AddConstant(). - std::unique_ptr owned_constant_; - - // Pointers are owned by 'catalog_'. - absl::node_hash_map tables_; - - // Connections owned by this catalog. - std::unordered_map> - owned_connections_; - - // Sequences owned by this catalog. - std::unordered_map> - owned_sequences_; - - // Manages the lifetime of ResolvedAST objects for SQL defined statements like - // views, SQL functions, column expressions, or SQL TVFs. - std::vector> sql_object_artifacts_; + absl::Status LoadCatalogImpl( + const ZetaSQLBuiltinFunctionOptions& builtin_function_options) = delete; }; } // namespace zetasql diff --git a/zetasql/testdata/sample_catalog_impl.cc b/zetasql/testdata/sample_catalog_impl.cc new file mode 100644 index 000000000..ac4793885 --- /dev/null +++ b/zetasql/testdata/sample_catalog_impl.cc @@ -0,0 +1,8747 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/testdata/sample_catalog_impl.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zetasql/base/logging.h" +#include "zetasql/common/errors.h" +#include "zetasql/public/analyzer.h" +#include "zetasql/public/analyzer_output.h" +#include "zetasql/public/annotation/collation.h" +#include "zetasql/public/anon_function.h" +#include "zetasql/public/builtin_function.pb.h" +#include "zetasql/public/builtin_function_options.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/cycle_detector.h" +#include "zetasql/public/deprecation_warning.pb.h" +#include "zetasql/public/error_location.pb.h" +#include "zetasql/public/evaluator_table_iterator.h" +#include "zetasql/public/function.h" +#include "zetasql/public/function.pb.h" +#include "zetasql/public/function_signature.h" +#include "zetasql/public/options.pb.h" +#include "zetasql/public/parse_resume_location.h" +#include "zetasql/public/procedure.h" +#include "zetasql/public/simple_catalog.h" +#include "zetasql/public/simple_catalog_util.h" +#include "zetasql/public/sql_function.h" +#include "zetasql/public/sql_tvf.h" +#include "zetasql/public/strings.h" +#include "zetasql/public/table_valued_function.h" +#include "zetasql/public/templated_sql_function.h" +#include "zetasql/public/templated_sql_tvf.h" +#include "zetasql/public/type.pb.h" +#include "zetasql/public/types/annotation.h" +#include "zetasql/public/types/simple_value.h" +#include "zetasql/public/types/type.h" +#include "zetasql/public/types/type_factory.h" +#include "zetasql/public/value.h" +#include "zetasql/resolved_ast/resolved_ast.h" +#include "zetasql/resolved_ast/resolved_ast_enums.pb.h" +#include "zetasql/resolved_ast/resolved_node_kind.pb.h" +#include "zetasql/testdata/ambiguous_has.pb.h" +#include "zetasql/testdata/referenced_schema.pb.h" +#include "zetasql/testdata/sample_annotation.h" +#include "zetasql/testdata/test_proto3.pb.h" +#include "absl/base/attributes.h" +#include "absl/base/const_init.h" +#include "absl/base/no_destructor.h" +#include "absl/container/btree_map.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/functional/function_ref.h" +#include "zetasql/base/check.h" +#include "absl/memory/memory.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/ascii.h" +#include "absl/strings/cord.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" +#include "absl/time/time.h" +#include "zetasql/base/source_location.h" +#include "absl/types/span.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor_database.h" +#include "google/protobuf/message.h" +#include "zetasql/base/map_util.h" +#include "zetasql/base/ret_check.h" +#include "zetasql/base/status_builder.h" +#include "zetasql/base/status_macros.h" + +namespace zetasql { + +namespace { + +// A fluent builder for building FunctionArgumentType instances +class ArgBuilder { + public: + explicit ArgBuilder() = default; + ArgBuilder&& Type(const Type* type) && { + kind_ = ARG_TYPE_FIXED; + type_ = type; + return std::move(*this); + } + // Convenience wrappers for some common Type choices. + // This should _not_ be exhaustive, prefer Type(...) over adding multitudes + // of these wrappers + ArgBuilder&& String() && { + return std::move(*this).Type(types::StringType()); + } + ArgBuilder&& Int64() && { return std::move(*this).Type(types::Int64Type()); } + ArgBuilder&& Bool() && { return std::move(*this).Type(types::BoolType()); } + ArgBuilder&& T1() && { + kind_ = ARG_TYPE_ANY_1; + return std::move(*this); + } + ArgBuilder&& T2() && { + kind_ = ARG_TYPE_ANY_2; + return std::move(*this); + } + ArgBuilder&& Any() && { + kind_ = ARG_TYPE_ARBITRARY; + return std::move(*this); + } + ArgBuilder&& Repeated() && { + options_.set_cardinality(FunctionEnums::REPEATED); + return std::move(*this); + } + ArgBuilder&& Optional() && { + options_.set_cardinality(FunctionEnums::OPTIONAL); + return std::move(*this); + } + // Implies optional. + ArgBuilder&& Default(Value value) && { + options_.set_default(value); + options_.set_cardinality(FunctionEnums::OPTIONAL); + return std::move(*this); + } + ArgBuilder&& Name(absl::string_view name) && { + options_.set_argument_name(name, zetasql::kPositionalOrNamed); + return std::move(*this); + } + ArgBuilder&& NameOnly(absl::string_view name) && { + options_.set_argument_name(name, zetasql::kNamedOnly); + return std::move(*this); + } + + FunctionArgumentType Build() && { + ABSL_CHECK(kind_.has_value()); + if (type_ != nullptr) { + ABSL_CHECK(kind_ = ARG_TYPE_FIXED); + return FunctionArgumentType(type_, options_); + } else { + return FunctionArgumentType(*kind_, options_); + } + } + + private: + FunctionArgumentTypeOptions options_; + std::optional kind_; + const ::zetasql::Type* type_ = nullptr; +}; + +// A fluent class for constructing FuntionSignature objects. +class SignatureBuilder { + public: + explicit SignatureBuilder( + zetasql_base::SourceLocation loc = zetasql_base::SourceLocation::current()) + : loc_(loc) {} + + SignatureBuilder&& AddArg(FunctionArgumentType t) && { + args_.push_back(std::move(t)); + return std::move(*this); + } + SignatureBuilder&& AddArg(ArgBuilder&& t) && { + return std::move(*this).AddArg(std::move(t).Build()); + } + + // By default, result type is string (many tests don't care about the return + // type). + SignatureBuilder&& Returns(FunctionArgumentType t) && { + ret_type_ = std::move(t); + return std::move(*this); + } + SignatureBuilder&& Returns(ArgBuilder&& t) && { + return std::move(*this).Returns(std::move(t).Build()); + } + + // Sets context_id to line number, which can speed up debugging. + FunctionSignature Build() && { + return FunctionSignature(ret_type_, std::move(args_), + /*context_id=*/loc_.line()); + } + + private: + FunctionArgumentTypeList args_; + // Default to return string. + FunctionArgumentType ret_type_ = {types::StringType()}; + zetasql_base::SourceLocation loc_; +}; + +} // namespace + +SampleCatalogImpl::SampleCatalogImpl(TypeFactory* type_factory) { + if (type_factory == nullptr) { + internal_type_factory_ = std::make_unique(); + types_ = internal_type_factory_.get(); + } else { + types_ = type_factory; + } + catalog_ = std::make_unique("sample_catalog", types_); +} + +SampleCatalogImpl::~SampleCatalogImpl() = default; + +SimpleTable* SampleCatalogImpl::GetTableOrDie(absl::string_view name) { + return zetasql_base::FindOrDie(tables_, name); +} + +absl::StatusOr SampleCatalogImpl::GetTable( + absl::string_view name) { + SimpleTable** table = zetasql_base::FindOrNull(tables_, name); + if (table != nullptr) { + return *table; + } else { + return zetasql_base::NotFoundErrorBuilder() + << "SampleCatalog: Table " << name << " not found"; + } +} + +const ProtoType* SampleCatalogImpl::GetProtoType( + const google::protobuf::Descriptor* descriptor) { + const Type* type; + ZETASQL_CHECK_OK(catalog_->FindType({descriptor->full_name()}, &type)); + ABSL_CHECK(type != nullptr); + ABSL_CHECK(type->IsProto()); + return type->AsProto(); +} + +const EnumType* SampleCatalogImpl::GetEnumType( + const google::protobuf::EnumDescriptor* descriptor) { + const Type* type; + ZETASQL_CHECK_OK(catalog_->FindType({descriptor->full_name()}, &type)); + ABSL_CHECK(type != nullptr); + ABSL_CHECK(type->IsEnum()); + return type->AsEnum(); +} + +static absl::StatusOr ComputeResultTypeCallbackForNullOfType( + Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, + const FunctionSignature& signature, + absl::Span arguments, + const AnalyzerOptions& analyzer_options) { + ZETASQL_RET_CHECK_EQ(arguments.size(), 1); + ZETASQL_RET_CHECK_EQ(signature.NumConcreteArguments(), arguments.size()); + const LanguageOptions& language_options = analyzer_options.language(); + if (!arguments[0].is_literal() || arguments[0].is_literal_null()) { + return MakeSqlError() + << "Argument to NULL_OF_TYPE must be a literal string"; + } + ZETASQL_RET_CHECK(arguments[0].type()->IsString()); + const Value& value = *arguments[0].literal_value(); + ZETASQL_RET_CHECK(!value.is_null()); + const absl::string_view type_name = value.string_value(); + const TypeKind type_kind = + Type::ResolveBuiltinTypeNameToKindIfSimple(type_name, language_options); + if (type_kind == TYPE_UNKNOWN) { + return MakeSqlError() << "Type not implemented for NULL_OF_TYPE: " + << ToStringLiteral(absl::AsciiStrToUpper(type_name)); + } + // We could parse complex type names here too by calling the type analyzer. + return type_factory->MakeSimpleType(type_kind); +} + +// A ComputeResultTypeCallback that looks for the 'type_name' argument from the +// input list and uses its value to generate the result type. +static absl::StatusOr ComputeResultTypeFromStringArgumentValue( + Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, + const FunctionSignature& signature, + absl::Span arguments, + const AnalyzerOptions& analyzer_options) { + ZETASQL_RET_CHECK_EQ(signature.NumConcreteArguments(), arguments.size()); + const LanguageOptions& language_options = analyzer_options.language(); + std::string type_name; + for (int i = 0; i < arguments.size(); ++i) { + if (!signature.ConcreteArgument(i).has_argument_name() || + signature.ConcreteArgument(i).argument_name() != "type_name") { + continue; + } + + const InputArgumentType& arg = arguments[i]; + ZETASQL_RET_CHECK(arg.type()->IsString()); + ZETASQL_RET_CHECK(arg.is_literal()); + if (arg.is_literal_null()) { + return MakeSqlError() << "Argument 'type_name' cannot be NULL"; + } + + const Value& value = *arg.literal_value(); + ZETASQL_RET_CHECK(!value.is_null()); + type_name = value.string_value(); + const TypeKind type_kind = + Type::ResolveBuiltinTypeNameToKindIfSimple(type_name, language_options); + if (type_kind != TYPE_UNKNOWN) { + return type_factory->MakeSimpleType(type_kind); + } + // Try to find the type in catalog. + const Type* type = nullptr; + std::vector path; + ZETASQL_RETURN_IF_ERROR(ParseIdentifierPath(type_name, LanguageOptions(), &path)); + if (catalog->FindType(path, &type).ok()) { + return type; + } + break; + } + if (!type_name.empty()) { + return MakeSqlError() << "Invalid type name provided: " << type_name; + } + // Use INT64 as return type if not overridden. + return type_factory->get_int64(); +} + +static absl::StatusOr ComputeResultTypeCallbackToStruct( + Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, + const FunctionSignature& signature, + absl::Span arguments, + const AnalyzerOptions& analyzer_options) { + const StructType* struct_type; + std::vector struct_fields; + struct_fields.reserve(arguments.size()); + for (int i = 0; i < arguments.size(); ++i) { + struct_fields.push_back({absl::StrCat("field", i), arguments[i].type()}); + } + ZETASQL_CHECK_OK( + type_factory->MakeStructType(std::move(struct_fields), &struct_type)); + return struct_type; +} + +// Similar to `ComputeResultTypeCallbackToStruct`, but use the argument aliases +// in `arguments`, if specified, as the struct field names. +static absl::StatusOr +ComputeResultTypeCallbackToStructUseArgumentAliases( + Catalog* catalog, TypeFactory* type_factory, CycleDetector* cycle_detector, + const FunctionSignature& signature, + absl::Span arguments, + const AnalyzerOptions& analyzer_options) { + const StructType* struct_type; + std::vector struct_fields; + struct_fields.reserve(arguments.size()); + for (int i = 0; i < arguments.size(); ++i) { + std::string field_name; + if (arguments[i].argument_alias().has_value()) { + field_name = arguments[i].argument_alias()->ToString(); + } else { + field_name = absl::StrCat("no_alias_", i); + } + struct_fields.push_back({field_name, arguments[i].type()}); + } + ZETASQL_CHECK_OK(type_factory->MakeStructType(struct_fields, &struct_type)); + return struct_type; +} + +// f(collation_1, ..., collation_n) -> STRUCT. +// If the return value is nullptr it means no collations. +static absl::StatusOr +ComputeResultAnnotationsCallbackToStruct(const AnnotationCallbackArgs& args, + TypeFactory& type_factory) { + const Type* result_type = args.result_type; + const std::vector& arguments = + args.argument_annotations; + // Can only handle struct type, where each argument becomes a field of the + // struct. + ZETASQL_RET_CHECK(result_type->IsStruct()); + ZETASQL_RET_CHECK_EQ(result_type->AsStruct()->num_fields(), arguments.size()); + + std::unique_ptr annotation_map = + AnnotationMap::Create(result_type); + ZETASQL_RET_CHECK(annotation_map->IsStructMap()); + StructAnnotationMap* struct_annotation_map = annotation_map->AsStructMap(); + ZETASQL_RET_CHECK_EQ(struct_annotation_map->num_fields(), arguments.size()); + + // We only check collation because currently the only supported annotation for + // functions is collation. + const int collation_annotation_id = CollationAnnotation::GetId(); + + // The annotation of each input argument is added to the corresponding + // struct field. + for (int i = 0; i < arguments.size(); ++i) { + if (arguments[i] == nullptr || + arguments[i]->GetAnnotation(collation_annotation_id) == nullptr) { + continue; + } + struct_annotation_map->mutable_field(i)->SetAnnotation( + collation_annotation_id, + *arguments[i]->GetAnnotation(collation_annotation_id)); + } + if (annotation_map->Empty()) { + // Use nullptr rather than an empty annotation map when there are no + // annotations. + return nullptr; + } + return type_factory.TakeOwnership(std::move(annotation_map)); +} + +// f(ARRAY, ..., ARRAY) -> ARRAY>. +// If the return value is nullptr it means no collations. +static absl::StatusOr +ComputeResultAnnotationsCallbackArraysToArrayOfStruct( + const AnnotationCallbackArgs& args, TypeFactory& type_factory) { + const Type* result_type = args.result_type; + const std::vector& arguments = + args.argument_annotations; + + // The return type must be ARRAY. + ZETASQL_RET_CHECK(result_type->IsArray()); + ZETASQL_RET_CHECK(result_type->AsArray()->element_type()->IsStruct()); + ZETASQL_RET_CHECK_EQ(result_type->AsArray()->element_type()->AsStruct()->num_fields(), + arguments.size()); + + // We only check collation because currently the only supported annotation for + // functions is collation. + const int collation_annotation_id = CollationAnnotation::GetId(); + + // Validations on the annotations of the input arguments. + for (const AnnotationMap* argument : arguments) { + if (argument == nullptr) { + continue; + } + ZETASQL_RET_CHECK(argument->IsArrayMap()); + // Only the array elements may have collations, not the array themselves. + ZETASQL_RET_CHECK_EQ(argument->AsArrayMap()->GetAnnotation(collation_annotation_id), + nullptr); + } + + std::unique_ptr annotation_map = + AnnotationMap::Create(result_type); + ZETASQL_RET_CHECK(annotation_map->IsArrayMap()); + ZETASQL_RET_CHECK(annotation_map->AsArrayMap()->element()->IsStructMap()); + + ArrayAnnotationMap* array_annotation_map = annotation_map->AsArrayMap(); + StructAnnotationMap* element_struct_annotation_map = + array_annotation_map->mutable_element()->AsStructMap(); + ZETASQL_RET_CHECK_EQ(element_struct_annotation_map->num_fields(), arguments.size()); + + // Propagate the array element collations to the elements of the result array. + for (int i = 0; i < arguments.size(); ++i) { + if (arguments[i] == nullptr) { + continue; + } + const AnnotationMap* argument_element_annotation_map = + arguments[i]->AsArrayMap()->element(); + if (argument_element_annotation_map == nullptr || + argument_element_annotation_map->GetAnnotation( + collation_annotation_id) == nullptr) { + continue; + } + element_struct_annotation_map->mutable_field(i)->SetAnnotation( + collation_annotation_id, + *argument_element_annotation_map->GetAnnotation( + collation_annotation_id)); + } + if (annotation_map->Empty()) { + // Use nullptr rather than an empty annotation map when there are no + // annotations. + return nullptr; + } + return type_factory.TakeOwnership(std::move(annotation_map)); +} + +// f(annotation_1, ..., annotation_n) -> annotation_n. +static absl::StatusOr +ComputeResultAnnotationsCallbackUseTheFinalAnnotation( + const AnnotationCallbackArgs& args, TypeFactory& type_factory) { + const Type* result_type = args.result_type; + ZETASQL_RET_CHECK_EQ(result_type, type_factory.get_string()); + const std::vector& arguments = + args.argument_annotations; + std::unique_ptr annotation_map = + AnnotationMap::Create(result_type); + // We only check collation because currently the only supported annotation for + // functions is collation. + const int collation_annotation_id = CollationAnnotation::GetId(); + for (int i = 0; i < arguments.size(); ++i) { + if (arguments[i] == nullptr) { + annotation_map->UnsetAnnotation(collation_annotation_id); + } else { + annotation_map->SetAnnotation( + collation_annotation_id, + *arguments[i]->GetAnnotation(collation_annotation_id)); + } + } + if (annotation_map->Empty()) { + return nullptr; + } + return type_factory.TakeOwnership(std::move(annotation_map)); +} + +// f(collation_1, ..., collation_n) -> SQL error. +// This is to verify that the thrown error is used as why a function call is +// invalid. +static absl::StatusOr +ComputeResultAnnotationsCallbackSqlError(const AnnotationCallbackArgs& args, + TypeFactory& type_factory) { + for (const AnnotationMap* argument : args.argument_annotations) { + if (argument != nullptr && !argument->Empty()) { + return MakeSqlError() << "Arguments should not have annotations"; + } + } + return nullptr; +} + +ZetaSQLBuiltinFunctionOptions SampleCatalogImpl::LoadDefaultSuppliedTypes( + const ZetaSQLBuiltinFunctionOptions& options) { + ZetaSQLBuiltinFunctionOptions options_copy = options; + const ProtoType* approx_distance_function_options_proto_type; + ZETASQL_CHECK_OK(types_->MakeProtoType( + zetasql_test__::TestApproxDistanceFunctionOptionsProto::GetDescriptor(), + &approx_distance_function_options_proto_type)); + absl::flat_hash_set approx_distance_function_ids = { + FN_APPROX_COSINE_DISTANCE_FLOAT_WITH_PROTO_OPTIONS, + FN_APPROX_COSINE_DISTANCE_DOUBLE_WITH_PROTO_OPTIONS, + FN_APPROX_EUCLIDEAN_DISTANCE_FLOAT_WITH_PROTO_OPTIONS, + FN_APPROX_EUCLIDEAN_DISTANCE_DOUBLE_WITH_PROTO_OPTIONS, + FN_APPROX_DOT_PRODUCT_INT64_WITH_PROTO_OPTIONS, + FN_APPROX_DOT_PRODUCT_FLOAT_WITH_PROTO_OPTIONS, + FN_APPROX_DOT_PRODUCT_DOUBLE_WITH_PROTO_OPTIONS}; + // Note that only argument index `2` is specified because only the third + // argument in approximate distance functions supports supplied argument + // types. + for (const auto& id : approx_distance_function_ids) { + std::pair id_idx_pair = {id, 2}; + options_copy.argument_types[id_idx_pair] = + approx_distance_function_options_proto_type; + } + return options_copy; +} + +void SampleCatalogImpl::LoadCatalogBuiltins( + const ZetaSQLBuiltinFunctionOptions& builtin_function_options) { + // Populate the sample catalog with the ZetaSQL functions using the + // specified ZetaSQLBuiltinFunctionOptions. + ZETASQL_CHECK_OK(catalog_->AddBuiltinFunctionsAndTypes( + LoadDefaultSuppliedTypes(builtin_function_options))); +} + +absl::Status SampleCatalogImpl::LoadCatalogImpl( + const ZetaSQLBuiltinFunctionOptions& builtin_function_options) { + const LanguageOptions& language_options = + builtin_function_options.language_options; + + LoadCatalogBuiltins(builtin_function_options); + + // Make all proto Descriptors linked into this binary available. + catalog_->SetDescriptorPool(google::protobuf::DescriptorPool::generated_pool()); + + // Create a Catalog called alt_descriptor_pool which has a duplicate copy + // of all protos in the main catalog, but in a different DescriptorPool. + alt_descriptor_database_ = std::make_unique( + *google::protobuf::DescriptorPool::generated_pool()); + alt_descriptor_pool_ = + std::make_unique(alt_descriptor_database_.get()); + + SimpleCatalog* alt_descriptor_pool_catalog = + catalog_->MakeOwnedSimpleCatalog("alt_descriptor_pool"); + alt_descriptor_pool_catalog->SetDescriptorPool(alt_descriptor_pool_.get()); + + // Create a Catalog called ambiguous_has_descriptor_pool which has the + // (modified) AmbiguousHasPB proto, obtained by changing the + // "confusing_name_to_be_rewritten" field's name changed to "confusing_name". + // This makes it such that the modified AmbiguousHasPB has a field called + // "has_confusing_name" and "confusing_name". Such proto descriptors can occur + // in practice, but not when C++ code is generated, hence this hack. + google::protobuf::FileDescriptorProto modified_descriptor_proto; + zetasql_test__::AmbiguousHasPB::descriptor()->file()->CopyTo( + &modified_descriptor_proto); + bool found_message = false; + for (google::protobuf::DescriptorProto& message_descriptor_proto : + *modified_descriptor_proto.mutable_message_type()) { + if (message_descriptor_proto.name() == "AmbiguousHasPB") { + found_message = true; + bool found_field = false; + for (google::protobuf::FieldDescriptorProto& field_descriptor_proto : + *message_descriptor_proto.mutable_field()) { + if (field_descriptor_proto.name() == "confusing_name_to_be_rewritten") { + found_field = true; + field_descriptor_proto.set_name("confusing_name"); + break; + } + } + ABSL_CHECK(found_field) << message_descriptor_proto; + } + } + ABSL_CHECK(found_message) << modified_descriptor_proto; + ambiguous_has_descriptor_pool_ = std::make_unique(); + ambiguous_has_descriptor_pool_->BuildFile(modified_descriptor_proto); + + auto ambiguous_has_descriptor_pool_catalog = + std::make_unique("ambiguous_has_descriptor_pool"); + ambiguous_has_descriptor_pool_catalog->SetDescriptorPool( + ambiguous_has_descriptor_pool_.get()); + catalog_->AddOwnedCatalog(ambiguous_has_descriptor_pool_catalog.release()); + + // Add various kinds of objects to the catalog(s). + ZETASQL_RETURN_IF_ERROR(LoadTypes()); + ZETASQL_RETURN_IF_ERROR(LoadTables()); + LoadConnections(); + LoadSequences(); + LoadProtoTables(); + LoadViews(language_options); + LoadNestedCatalogs(); + LoadFunctions(); + LoadFunctions2(); + LoadAllRegisteredCatalogChanges(); + LoadExtendedSubscriptFunctions(); + LoadFunctionsWithDefaultArguments(); + LoadFunctionsWithStructArgs(); + LoadTemplatedSQLUDFs(); + LoadTableValuedFunctions1(); + LoadTableValuedFunctions2(); + LoadTableValuedFunctionsWithEvaluators(); + LoadTVFWithExtraColumns(); + LoadDescriptorTableValuedFunctions(); + LoadConnectionTableValuedFunctions(); + LoadTableValuedFunctionsWithDeprecationWarnings(); + LoadTemplatedSQLTableValuedFunctions(); + LoadTableValuedFunctionsWithAnonymizationUid(); + LoadProcedures(); + LoadConstants(); + LoadWellKnownLambdaArgFunctions(); + LoadContrivedLambdaArgFunctions(); + LoadSqlFunctions(language_options); + LoadNonTemplatedSqlTableValuedFunctions(language_options); + return absl::OkStatus(); +} + +absl::Status SampleCatalogImpl::LoadTypes() { + enum_TestEnum_ = GetEnumType(zetasql_test__::TestEnum_descriptor()); + enum_AnotherTestEnum_ = + GetEnumType(zetasql_test__::AnotherTestEnum_descriptor()); + enum_TestEnumWithAnnotations_ = + GetEnumType(zetasql_test__::TestEnumWithAnnotations_descriptor()); + proto_KitchenSinkPB_ = + GetProtoType(zetasql_test__::KitchenSinkPB::descriptor()); + proto_MessageWithKitchenSinkPB_ = + GetProtoType(zetasql_test__::MessageWithKitchenSinkPB::descriptor()); + proto_CivilTimeTypesSinkPB_ = + GetProtoType(zetasql_test__::CivilTimeTypesSinkPB::descriptor()); + proto_TestExtraPB_ = GetProtoType(zetasql_test__::TestExtraPB::descriptor()); + proto_abPB_ = GetProtoType(zetasql_test__::TestAbPB::descriptor()); + proto_bcPB_ = GetProtoType(zetasql_test__::TestBcPB::descriptor()); + proto_EmptyMessage_ = + GetProtoType(zetasql_test__::EmptyMessage::descriptor()); + proto3_KitchenSinkPB_ = + GetProtoType(zetasql_test__::Proto3KitchenSink::descriptor()); + proto3_MessageWithInvalidMap_ = + GetProtoType(zetasql_test__::MessageWithInvalidMap::descriptor()); + proto_field_formats_proto_ = + GetProtoType(zetasql_test__::FieldFormatsProto::descriptor()); + proto_MessageWithMapField_ = + GetProtoType(zetasql_test__::MessageWithMapField::descriptor()); + proto_approx_distance_function_options_ = GetProtoType( + zetasql_test__::TestApproxDistanceFunctionOptionsProto::descriptor()); + + // We want to pull AmbiguousHasPB from the descriptor pool where it was + // modified, not the generated pool. + const google::protobuf::Descriptor* ambiguous_has_descriptor = + ambiguous_has_descriptor_pool_->FindMessageTypeByName( + "zetasql_test__.AmbiguousHasPB"); + ABSL_CHECK(ambiguous_has_descriptor); + ZETASQL_CHECK_OK( + types_->MakeProtoType(ambiguous_has_descriptor, &proto_ambiguous_has_)); + + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_int32()}, {"b", types_->get_string()}}, + &struct_type_)); + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"c", types_->get_int32()}, {"d", struct_type_}}, &nested_struct_type_)); + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"e", types_->get_int32()}, {"f", nested_struct_type_}}, + &doubly_nested_struct_type_)); + + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_int32(), &int32array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_int64(), &int64array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_uint32(), &uint32array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_uint64(), &uint64array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_bytes(), &bytes_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_bool(), &bool_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_float(), &float_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_double(), &double_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_date(), &date_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_string(), &string_array_type_)); + ZETASQL_CHECK_OK( + types_->MakeArrayType(types_->get_timestamp(), ×tamp_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(proto_TestExtraPB_, &proto_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(struct_type_, &struct_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_json(), &json_array_type_)); + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_numeric(), &numeric_array_type_)); + ZETASQL_CHECK_OK( + types_->MakeArrayType(types_->get_bignumeric(), &bignumeric_array_type_)); + ZETASQL_CHECK_OK( + types_->MakeArrayType(types_->get_interval(), &interval_array_type_)); + + ZETASQL_ASSIGN_OR_RETURN(int32map_type_, types_->MakeMapType(types_->get_int32(), + types_->get_int32())); + ZETASQL_ASSIGN_OR_RETURN(int64map_type_, types_->MakeMapType(types_->get_int64(), + types_->get_int64())); + ZETASQL_ASSIGN_OR_RETURN(bytesmap_type_, types_->MakeMapType(types_->get_bytes(), + types_->get_bytes())); + + ZETASQL_CHECK_OK(types_->MakeStructType({{"x", types_->get_int64()}, + {"y", struct_type_}, + {"z", struct_array_type_}}, + &struct_with_array_field_type_)); + + ZETASQL_CHECK_OK(types_->MakeStructType({{"x", types_->get_int64()}}, + &struct_with_one_field_type_)); + + const StructType* struct_with_just_kitchen_sink_type; + ZETASQL_CHECK_OK(types_->MakeStructType({{"kitchen_sink", proto_KitchenSinkPB_}}, + &struct_with_just_kitchen_sink_type)); + ZETASQL_CHECK_OK(types_->MakeStructType({{"kitchen_sink", proto_KitchenSinkPB_}, + {"s", struct_with_just_kitchen_sink_type}}, + &struct_with_kitchen_sink_type_)); + const ArrayType* array_of_struct_with_kitchen_sink_type; + ZETASQL_CHECK_OK(types_->MakeArrayType(struct_with_just_kitchen_sink_type, + &array_of_struct_with_kitchen_sink_type)); + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_int64()}, + {"b", array_of_struct_with_kitchen_sink_type}}, + &struct_of_array_of_struct_with_kitchen_sink_type_)); + + // Add a named struct type for testing name collisions. + const StructType* name_conflict_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}, + &name_conflict_type)); + catalog_->AddType("NameConflictType", name_conflict_type); + + // Add a simple type for testing alias type from engine catalog + catalog_->AddType("INT64AliasType", types_->get_int64()); + + return absl::OkStatus(); +} + +namespace { + +// Implementation of EvaluatorTableIterator which ignores SetReadTime(), but +// delegates all other methods to an underlying iterator passed to the +// constructor. +class IgnoreReadTimeIterator : public EvaluatorTableIterator { + public: + explicit IgnoreReadTimeIterator( + std::unique_ptr iterator) + : iterator_(std::move(iterator)) {} + + int NumColumns() const override { return iterator_->NumColumns(); } + std::string GetColumnName(int i) const override { + return iterator_->GetColumnName(i); + } + const Type* GetColumnType(int i) const override { + return iterator_->GetColumnType(i); + } + absl::Status SetColumnFilterMap( + absl::flat_hash_map> filter_map) + override { + return iterator_->SetColumnFilterMap(std::move(filter_map)); + } + absl::Status SetReadTime(absl::Time read_time) override { + return absl::OkStatus(); + } + bool NextRow() override { return iterator_->NextRow(); } + const Value& GetValue(int i) const override { return iterator_->GetValue(i); } + absl::Status Status() const override { return iterator_->Status(); } + absl::Status Cancel() override { return iterator_->Cancel(); } + void SetDeadline(absl::Time deadline) override { + iterator_->SetDeadline(deadline); + } + + private: + std::unique_ptr iterator_; +}; + +// Minimal table implementation to support testing of FOR SYSTEM TIME AS OF. +// This is just a modified version of SimpleTable which ignores the read time. +class SimpleTableWithReadTimeIgnored : public SimpleTable { + public: + SimpleTableWithReadTimeIgnored(absl::string_view name, + absl::Span columns, + const int64_t id = 0) + : SimpleTable(name, columns, id) {} + + absl::StatusOr> + CreateEvaluatorTableIterator( + absl::Span column_idxs) const override { + std::unique_ptr iterator; + ZETASQL_ASSIGN_OR_RETURN(iterator, + SimpleTable::CreateEvaluatorTableIterator(column_idxs)); + return std::make_unique(std::move(iterator)); + } +}; + +} // namespace + +absl::Status SampleCatalogImpl::AddGeneratedColumnToTable( + std::string column_name, std::vector expression_columns, + std::string generated_expr, SimpleTable* table) { + AnalyzerOptions options; + options.mutable_language()->SetSupportsAllStatementKinds(); + std::unique_ptr output; + for (const std::string& expression_column : expression_columns) { + ZETASQL_RET_CHECK_OK( + options.AddExpressionColumn(expression_column, types::Int64Type())); + } + ZETASQL_RET_CHECK_OK(AnalyzeExpression(generated_expr, options, catalog_.get(), + catalog_->type_factory(), &output)); + SimpleColumn::ExpressionAttributes expr_attributes( + SimpleColumn::ExpressionAttributes::ExpressionKind::GENERATED, + generated_expr, output->resolved_expr()); + ZETASQL_RET_CHECK_OK(table->AddColumn( + new SimpleColumn(table->Name(), column_name, types::Int64Type(), + {.column_expression = expr_attributes}), + /*is_owned=*/true)); + sql_object_artifacts_.push_back(std::move(output)); + return absl::OkStatus(); +} + +absl::Status SampleCatalogImpl::LoadTables() { + SimpleTable* value_table = new SimpleTable( + "Value", {{"Value", types_->get_int64()}, + // to test while() loop in SQLBuilder::GetScanAlias + {"Value_1", types_->get_int64()}}); + AddOwnedTable(value_table); + + SimpleTable* key_value_table = new SimpleTable( + "KeyValue", + {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}); + key_value_table->SetContents({{Value::Int64(1), Value::String("a")}, + {Value::Int64(2), Value::String("b")}}); + + AddOwnedTable(key_value_table); + key_value_table_ = key_value_table; + + SimpleTable* multiple_columns_table = + new SimpleTable("MultipleColumns", {{"int_a", types_->get_int64()}, + {"string_a", types_->get_string()}, + {"int_b", types_->get_int64()}, + {"string_b", types_->get_string()}, + {"int_c", types_->get_int64()}, + {"int_d", types_->get_int64()}}); + AddOwnedTable(multiple_columns_table); + + SimpleTable* update_to_default_table = new SimpleTable( + "UpdateToDefaultTable", {{"writable", types_->get_int64()}}); + ZETASQL_CHECK_OK(update_to_default_table->AddColumn( + new SimpleColumn(update_to_default_table->Name(), "readonly", + types_->get_int64(), {.is_writable_column = false}), + /*is_owned=*/true)); + ZETASQL_CHECK_OK(update_to_default_table->AddColumn( + new SimpleColumn(update_to_default_table->Name(), + "readonly_settable_to_default", types_->get_int64(), + {.is_writable_column = false, + .can_update_unwritable_to_default = true}), + /*is_owned=*/true)); + AddOwnedTable(update_to_default_table); + + SimpleTable* ab_table = new SimpleTable( + "abTable", {{"a", types_->get_int64()}, {"b", types_->get_string()}}); + AddOwnedTable(ab_table); + SimpleTable* bc_table = new SimpleTable( + "bcTable", {{"b", types_->get_int64()}, {"c", types_->get_string()}}); + AddOwnedTable(bc_table); + + SimpleTable* key_value_table_read_time_ignored = + new SimpleTableWithReadTimeIgnored( + "KeyValueReadTimeIgnored", + {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}); + AddOwnedTable(key_value_table_read_time_ignored); + + SimpleTable* another_key_value = new SimpleTable( + "AnotherKeyValue", + {{"Key", types_->get_int64()}, {"value", types_->get_string()}}); + AddOwnedTable(another_key_value); + + const SimpleModel* one_double_model = + new SimpleModel("OneDoubleModel", {{"a", types_->get_double()}}, + {{"label", types_->get_double()}}, /*id=*/0); + const SimpleModel* one_double_one_string_model = new SimpleModel( + "OneDoubleOneStringModel", + {{"a", types_->get_double()}, {"b", types_->get_string()}}, + {{"label", types_->get_double()}}, /*id=*/1); + const SimpleModel* one_double_two_output_model = new SimpleModel( + "OneDoubleTwoOutputModel", {{"a", types_->get_double()}}, + {{"label1", types_->get_double()}, {"label2", types_->get_double()}}, + /*id=*/2); + catalog_->AddOwnedModel(one_double_model); + catalog_->AddOwnedModel(one_double_one_string_model); + catalog_->AddOwnedModel(one_double_two_output_model); + + SimpleTable* key_value2_table = new SimpleTable( + "KeyValue2", + {{"Key", types_->get_int64()}, {"Value2", types_->get_string()}}); + AddOwnedTable(key_value2_table); + + SimpleTable* space_value_table = new SimpleTable( + " Value", + {{" Key", types_->get_int64()}, {" Value", types_->get_string()}}); + AddOwnedTable(space_value_table); + + SimpleTable* table_with_default_column = new SimpleTable( + "TableWithDefaultColumn", + {{"id", types_->get_int64()}, {"a", types_->get_int64()}}); + + const std::string default_expr = "10"; + AnalyzerOptions analyzer_options; + std::unique_ptr output; + ZETASQL_CHECK_OK(AnalyzeExpression(default_expr, analyzer_options, catalog_.get(), + catalog_->type_factory(), &output)); + + SimpleColumn::ExpressionAttributes expr_attributes( + SimpleColumn::ExpressionAttributes::ExpressionKind::DEFAULT, default_expr, + output->resolved_expr()); + ZETASQL_CHECK_OK(table_with_default_column->AddColumn( + new SimpleColumn(table_with_default_column->Name(), "default_col", + types_->get_int64(), + {.column_expression = expr_attributes}), + /*is_owned=*/true)); + + sql_object_artifacts_.emplace_back(std::move(output)); + AddOwnedTable(table_with_default_column); + + SimpleTable* table_with_generated_column = new SimpleTable( + "TableWithGeneratedColumn", std::vector{}); + // Adding column A AS (B+C) to table. + ZETASQL_RETURN_IF_ERROR(AddGeneratedColumnToTable("A", {"B", "C"}, "B+C", + table_with_generated_column)); + // Adding column B AS (C+1) to table. + ZETASQL_RETURN_IF_ERROR(AddGeneratedColumnToTable("B", {"C"}, "C+1", + table_with_generated_column)); + // Adding column C int64_t to table. + ZETASQL_RET_CHECK_OK(table_with_generated_column->AddColumn( + new SimpleColumn(table_with_generated_column->Name(), "C", + types::Int64Type()), + /*is_owned=*/true)); + // Adding column D AS (A+B+C) to table. + ZETASQL_RETURN_IF_ERROR(AddGeneratedColumnToTable("D", {"A", "B", "C"}, "A+B+C", + table_with_generated_column)); + AddOwnedTable(table_with_generated_column); + + // Create two tables with the following schema. + // GeoStructTable1: geo STRUCT + // GeoStructTable2: geo STRUCT + const StructType* struct_with_geo_type1; + ZETASQL_CHECK_OK(types_->MakeStructType({{"a", types_->get_geography()}}, + &struct_with_geo_type1)); + const StructType* struct_with_geo_type2; + ZETASQL_CHECK_OK(types_->MakeStructType({{"b", types_->get_geography()}}, + &struct_with_geo_type2)); + + SimpleTable* geo_struct_table1 = + new SimpleTable("GeoStructTable1", {{"geo", struct_with_geo_type1}}); + AddOwnedTable(geo_struct_table1); + + SimpleTable* geo_struct_table2 = + new SimpleTable("GeoStructTable2", {{"geo", struct_with_geo_type2}}); + AddOwnedTable(geo_struct_table2); + + auto collatedTable = new SimpleTable("CollatedTable"); + const AnnotationMap* annotation_map_string_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(types_->get_string()); + annotation_map->SetAnnotation( + SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_string_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* annotation_map_string_binary; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(types_->get_string()); + annotation_map->SetAnnotation( + SimpleValue::String("binary")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_string_binary, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* annotation_map_struct_with_string_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(struct_type_); + annotation_map->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_struct_with_string_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* annotation_map_array_with_string_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(string_array_type_); + annotation_map->AsArrayMap() + ->mutable_element() + ->SetAnnotation(SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_array_with_string_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + auto string_ci = new SimpleColumn( + collatedTable->Name(), "string_ci", + AnnotatedType(types_->get_string(), annotation_map_string_ci)); + + auto string_cs = new SimpleColumn( + collatedTable->Name(), "string_binary", + AnnotatedType(types_->get_string(), annotation_map_string_binary)); + + auto struct_ci = new SimpleColumn( + collatedTable->Name(), "struct_with_string_ci", + AnnotatedType(struct_type_, annotation_map_struct_with_string_ci)); + + auto array_ci = new SimpleColumn( + collatedTable->Name(), "array_with_string_ci", + AnnotatedType(string_array_type_, annotation_map_array_with_string_ci)); + + ZETASQL_CHECK_OK(collatedTable->AddColumn(string_ci, /*is_owned=*/true)); + ZETASQL_CHECK_OK(collatedTable->AddColumn(string_cs, /*is_owned=*/true)); + ZETASQL_CHECK_OK(collatedTable->AddColumn(struct_ci, /*is_owned=*/true)); + ZETASQL_CHECK_OK(collatedTable->AddColumn(array_ci, /*is_owned=*/true)); + AddOwnedTable(collatedTable); + + auto complex_collated_table = new SimpleTable("ComplexCollatedTable"); + + const StructType* struct_with_bool_string_type; + // We let the first field have bool (rather than int32_t) type to avoid + // implicitly coercing ARRAY> to + // ARRAY> in the testing, which is currently not + // supported. + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_bool()}, {"b", types_->get_string()}}, + &struct_with_bool_string_type)); + const ArrayType* array_of_struct_type; + ZETASQL_CHECK_OK(types_->MakeArrayType(struct_with_bool_string_type, + &array_of_struct_type)); + const StructType* struct_with_array_of_struct_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_int32()}, {"b", array_of_struct_type}}, + &struct_with_array_of_struct_type)); + + const StructType* struct_of_multiple_string_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_string()}, {"b", types_->get_string()}}, + &struct_of_multiple_string_type)); + + const StructType* struct_of_double_and_string_and_array_type; + ZETASQL_CHECK_OK(types_->MakeStructType({{"a", types_->get_double()}, + {"b", types_->get_string()}, + {"c", string_array_type_}}, + &struct_of_double_and_string_and_array_type)); + + const AnnotationMap* annotation_map_array_of_struct_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(array_of_struct_type); + annotation_map->AsArrayMap() + ->mutable_element() + ->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_array_of_struct_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* annotation_map_struct_with_array_of_struct_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(struct_with_array_of_struct_type); + annotation_map->AsStructMap() + ->mutable_field(1) + ->AsArrayMap() + ->mutable_element() + ->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_struct_with_array_of_struct_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* annotation_map_struct_of_struct_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(nested_struct_type_); + annotation_map->AsStructMap() + ->mutable_field(1) + ->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_struct_of_struct_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* annotation_map_struct_with_string_ci_binary; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(struct_of_multiple_string_type); + annotation_map->AsStructMap() + ->mutable_field(0) + ->SetAnnotation(SimpleValue::String("und:ci")); + annotation_map->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::String("binary")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_struct_with_string_ci_binary, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* + annotation_map_struct_of_double_and_string_ci_and_array_ci; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(struct_of_double_and_string_and_array_type); + // Set collation for the second field of STRING type. + annotation_map->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::String("und:ci")); + // Set collation for the third field of ARRAY type. + annotation_map->AsStructMap() + ->mutable_field(2) + ->AsArrayMap() + ->mutable_element() + ->SetAnnotation(SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_struct_of_double_and_string_ci_and_array_ci, + types_->TakeOwnership(std::move(annotation_map))); + } + + auto string_no_collation = new SimpleColumn( + complex_collated_table->Name(), "string_no_collation", + AnnotatedType(types_->get_string(), /*annotation_map=*/nullptr)); + + auto array_of_struct_ci = new SimpleColumn( + complex_collated_table->Name(), "array_of_struct_ci", + AnnotatedType(array_of_struct_type, annotation_map_array_of_struct_ci)); + + auto struct_with_array_of_struct_ci = new SimpleColumn( + complex_collated_table->Name(), "struct_with_array_of_struct_ci", + AnnotatedType(struct_with_array_of_struct_type, + annotation_map_struct_with_array_of_struct_ci)); + + auto struct_of_struct_ci = new SimpleColumn( + complex_collated_table->Name(), "struct_of_struct_ci", + AnnotatedType(nested_struct_type_, annotation_map_struct_of_struct_ci)); + + auto struct_with_string_ci_binary = new SimpleColumn( + complex_collated_table->Name(), "struct_with_string_ci_binary", + AnnotatedType(struct_of_multiple_string_type, + annotation_map_struct_with_string_ci_binary)); + + auto struct_of_double_and_string_ci_and_array_ci = new SimpleColumn( + complex_collated_table->Name(), + "struct_of_double_and_string_ci_and_array_ci", + AnnotatedType( + struct_of_double_and_string_and_array_type, + annotation_map_struct_of_double_and_string_ci_and_array_ci)); + + ZETASQL_CHECK_OK(complex_collated_table->AddColumn(string_no_collation, + /*is_owned=*/true)); + ZETASQL_CHECK_OK(complex_collated_table->AddColumn(array_of_struct_ci, + /*is_owned=*/true)); + ZETASQL_CHECK_OK(complex_collated_table->AddColumn(struct_with_array_of_struct_ci, + /*is_owned=*/true)); + ZETASQL_CHECK_OK(complex_collated_table->AddColumn(struct_of_struct_ci, + /*is_owned=*/true)); + ZETASQL_CHECK_OK(complex_collated_table->AddColumn(struct_with_string_ci_binary, + /*is_owned=*/true)); + ZETASQL_CHECK_OK(complex_collated_table->AddColumn( + struct_of_double_and_string_ci_and_array_ci, + /*is_owned=*/true)); + AddOwnedTable(complex_collated_table); + + auto collated_table_with_proto = new SimpleTable("CollatedTableWithProto"); + const AnnotationMap* annotation_map_proto; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(proto_KitchenSinkPB_); + annotation_map->SetAnnotation( + SimpleValue::String("und:ci")); + ZETASQL_ASSIGN_OR_RETURN(annotation_map_proto, + types_->TakeOwnership(std::move(annotation_map))); + } + + auto proto_with_collation = new SimpleColumn( + collated_table_with_proto->Name(), "proto_with_collation", + AnnotatedType(proto_KitchenSinkPB_, annotation_map_proto)); + + ZETASQL_CHECK_OK(collated_table_with_proto->AddColumn(proto_with_collation, + /*is_owned=*/true)); + AddOwnedTable(collated_table_with_proto); + + auto generic_annotation_test_table = new SimpleTable("AnnotatedTable"); + + const AnnotationMap* generic_test_annotation_map_for_string_field; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(types_->get_string()); + annotation_map->SetAnnotation(SimpleValue::Int64(0)); + ZETASQL_ASSIGN_OR_RETURN(generic_test_annotation_map_for_string_field, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* generic_test_annotation_map_for_int_field; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(types_->get_int64()); + annotation_map->SetAnnotation(SimpleValue::Int64(1)); + ZETASQL_ASSIGN_OR_RETURN(generic_test_annotation_map_for_int_field, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* generic_annotation_map_for_struct_field; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(struct_type_); + annotation_map->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::Int64(2)); + ZETASQL_ASSIGN_OR_RETURN(generic_annotation_map_for_struct_field, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* generic_test_annotation_map_for_string_array_field; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(string_array_type_); + annotation_map->SetAnnotation(SimpleValue::Int64(3)); + ZETASQL_ASSIGN_OR_RETURN(generic_test_annotation_map_for_string_array_field, + types_->TakeOwnership(std::move(annotation_map))); + } + + const AnnotationMap* generic_annotation_map_for_nested_struct_field; + { + std::unique_ptr annotation_map = + AnnotationMap::Create(nested_struct_type_); + annotation_map->AsStructMap() + ->mutable_field(0) + ->SetAnnotation(SimpleValue::Int64(4)); + annotation_map->AsStructMap() + ->mutable_field(1) + ->AsStructMap() + ->mutable_field(1) + ->SetAnnotation(SimpleValue::Int64(5)); + ZETASQL_ASSIGN_OR_RETURN(generic_annotation_map_for_nested_struct_field, + types_->TakeOwnership(std::move(annotation_map))); + } + + auto generic_annotation_test_string_column = new SimpleColumn( + generic_annotation_test_table->Name(), "string", + AnnotatedType(types_->get_string(), + generic_test_annotation_map_for_string_field)); + auto generic_annotation_test_int_column = new SimpleColumn( + generic_annotation_test_table->Name(), "int_a", + AnnotatedType(types_->get_int64(), + generic_test_annotation_map_for_int_field)); + auto generic_annotation_test_int_column_unannotated = new SimpleColumn( + generic_annotation_test_table->Name(), "int_b", types_->get_int64()); + auto generic_annotation_test_struct_column = new SimpleColumn( + generic_annotation_test_table->Name(), "struct_a", + AnnotatedType(struct_type_, generic_annotation_map_for_struct_field)); + auto generic_annotation_test_string_array_column = new SimpleColumn( + generic_annotation_test_table->Name(), "string_array", + AnnotatedType(string_array_type_, + generic_test_annotation_map_for_string_array_field)); + auto generic_annotation_test_nested_struct_column = new SimpleColumn( + generic_annotation_test_table->Name(), "struct_b", + AnnotatedType(nested_struct_type_, + generic_annotation_map_for_nested_struct_field)); + + ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( + generic_annotation_test_string_column, /*is_owned=*/true)); + ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( + generic_annotation_test_int_column, /*is_owned=*/true)); + ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( + generic_annotation_test_int_column_unannotated, /*is_owned=*/true)); + ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( + generic_annotation_test_struct_column, /*is_owned=*/true)); + ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( + generic_annotation_test_string_array_column, /*is_owned=*/true)); + ZETASQL_CHECK_OK(generic_annotation_test_table->AddColumn( + generic_annotation_test_nested_struct_column, /*is_owned=*/true)); + AddOwnedTable(generic_annotation_test_table); + + AddOwnedTable(new SimpleTable( + "SimpleTypes", + {{"int32", types_->get_int32()}, + {"int64", types_->get_int64()}, + {"uint32", types_->get_uint32()}, + {"uint64", types_->get_uint64()}, + {"string", types_->get_string()}, + {"bytes", types_->get_bytes()}, + {"bool", types_->get_bool()}, + {"float", types_->get_float()}, + {"double", types_->get_double()}, + {"date", types_->get_date()}, + // These types were removed, but we keep the fields in the sample + // table in order not to disturb analyzer test results too much (all + // the variable ids would change if we were to remove them). + // TODO: Remove them when all other changes settled down. + {"timestamp_seconds", types_->get_timestamp()}, + {"timestamp_millis", types_->get_timestamp()}, + {"timestamp_micros", types_->get_timestamp()}, + {"timestamp_nanos", types_->get_timestamp()}, + // Real types resume here. + {"timestamp", types_->get_timestamp()}, + {"numeric", types_->get_numeric()}, + {"bignumeric", types_->get_bignumeric()}, + {"json", types_->get_json()}, + {"uuid", types_->get_uuid()}})); + + { + auto simple_table_with_uid = + std::make_unique("SimpleTypesWithAnonymizationUid", + std::vector{ + {"int32", types_->get_int32()}, + {"int64", types_->get_int64()}, + {"uint32", types_->get_uint32()}, + {"uint64", types_->get_uint64()}, + {"string", types_->get_string()}, + {"bytes", types_->get_bytes()}, + {"bool", types_->get_bool()}, + {"float", types_->get_float()}, + {"double", types_->get_double()}, + {"date", types_->get_date()}, + {"uid", types_->get_int64()}, + {"numeric", types_->get_numeric()}}); + ZETASQL_CHECK_OK(simple_table_with_uid->SetAnonymizationInfo("uid")); + AddOwnedTable(simple_table_with_uid.release()); + } + + { + auto array_table_with_uid = std::make_unique( + "ArrayWithAnonymizationUid", std::vector{ + {"int64_array", int64array_type_}, + {"double_array", double_array_type_}, + {"uid", types_->get_int64()}}); + ZETASQL_CHECK_OK(array_table_with_uid->SetAnonymizationInfo("uid")); + AddOwnedTable(array_table_with_uid.release()); + } + + { + auto table_with_string_uid = std::make_unique( + "T1StringAnonymizationUid", + std::vector{{"uid", types_->get_string()}, + {"c2", types_->get_string()}}); + ZETASQL_CHECK_OK(table_with_string_uid->SetAnonymizationInfo("uid")); + AddOwnedTable(table_with_string_uid.release()); + } + + { + auto table_with_string_uid = std::make_unique( + "T2StringAnonymizationUid", + std::vector{{"c1", types_->get_string()}, + {"uid", types_->get_string()}}); + ZETASQL_CHECK_OK(table_with_string_uid->SetAnonymizationInfo("uid")); + AddOwnedTable(table_with_string_uid.release()); + } + + { + auto table_with_proto_uid = std::make_unique( + "ProtoAnonymizationUid", + std::vector{{"uid", proto_KitchenSinkPB_}}); + ZETASQL_CHECK_OK(table_with_proto_uid->SetAnonymizationInfo("uid")); + AddOwnedTable(table_with_proto_uid.release()); + } + + { + auto value_table_with_uid = std::make_unique( + "KitchenSinkWithUidValueTable", proto_KitchenSinkPB_); + ZETASQL_CHECK_OK(value_table_with_uid->SetAnonymizationInfo("string_val")); + AddOwnedTable(value_table_with_uid.release()); + } + + { + auto value_table_with_uid = std::make_unique( + "TestStructWithUidValueTable", struct_type_); + ZETASQL_CHECK_OK(value_table_with_uid->SetAnonymizationInfo("a")); + AddOwnedTable(value_table_with_uid.release()); + } + + { + auto value_table_with_doubly_nested_uid = std::make_unique( + "TestWithDoublyNestedStructUidValueTable", doubly_nested_struct_type_); + ZETASQL_CHECK_OK(value_table_with_doubly_nested_uid->SetAnonymizationInfo( + {"f", "d", "a"})); + AddOwnedTable(value_table_with_doubly_nested_uid.release()); + } + + { + auto value_table_with_proto_uid = std::make_unique( + "TestWithProtoUidValueTable", proto_MessageWithKitchenSinkPB_); + ZETASQL_CHECK_OK(value_table_with_proto_uid->SetAnonymizationInfo( + {"kitchen_sink", "nested_value", "nested_int64"})); + AddOwnedTable(value_table_with_proto_uid.release()); + } + + { + auto value_table_with_proto_uid_of_wrong_type = + std::make_unique("TestWithWrongTypeProtoUidValueTable", + proto_MessageWithKitchenSinkPB_); + ZETASQL_CHECK_OK(value_table_with_proto_uid_of_wrong_type->SetAnonymizationInfo( + std::vector({"kitchen_sink", "nested_value"}))); + AddOwnedTable(value_table_with_proto_uid_of_wrong_type.release()); + } + + AddOwnedTable( + new SimpleTable("GeographyTable", {{"key", types_->get_int64()}, + {"text", types_->get_string()}, + {"geo1", types_->get_geography()}, + {"geo2", types_->get_geography()}})); + + AddOwnedTable(new SimpleTable("NumericTypeTable", + {{"numeric_col", types_->get_numeric()}})); + + AddOwnedTable(new SimpleTable( + "BigNumericTypeTable", {{"bignumeric_col", types_->get_bignumeric()}})); + + AddOwnedTable( + new SimpleTable("JSONTable", {{"json_col", types_->get_json()}})); + + SimpleTable* two_integers = new SimpleTable( + "TwoIntegers", + {{"key", types_->get_int64()}, {"value", types_->get_int64()}}); + ZETASQL_CHECK_OK(two_integers->SetPrimaryKey({0})); + AddOwnedTable(two_integers); + + SimpleTable* four_integers = + new SimpleTable("FourIntegers", {{"key1", types_->get_int64()}, + {"value1", types_->get_int64()}, + {"key2", types_->get_int64()}, + {"value2", types_->get_int64()}}); + ZETASQL_CHECK_OK(four_integers->SetPrimaryKey({0, 2})); + AddOwnedTable(four_integers); + + // Tables with no columns are legal. + AddOwnedTable(new SimpleTable("NoColumns")); + + // Add tables for testing name collisions. + AddOwnedTable( + new SimpleTable("NameConflictTable", {{"key", types_->get_int32()}})); + AddOwnedTable(new SimpleTable( + "name_conflict_table", {{"a", types_->get_string()}, + {"name_conflict_field", types_->get_string()}})); + + // Add tables for testing create model with aliased queries. + AddOwnedTable( + new SimpleTable("user_training_data", {{"data", types_->get_int32()}})); + AddOwnedTable(new SimpleTable("user_custom_holiday", + {{"region", types_->get_string()}, + {"holiday_name", types_->get_string()}, + {"primary_date", types_->get_date()}})); + + // Create two tables with common prefix. + // One table "Int32Array" exists under the catalog "ArrayTableOrCatalog", so + // its full path is "ArrayTableOrCatalog.Int32Array". + // Another table "ArrayTableOrCatalog" exists under the root catalog, so its + // full path is "ArrayTableOrCatalog". "ArrayTableOrCatalog.Int32Array" refers + // to an array column. + SimpleCatalog* array_catalog = + catalog_->MakeOwnedSimpleCatalog("ArrayTableOrCatalog"); + SimpleTable* array_table_1 = + new SimpleTable("Int32Array", {{"Int32Array", int32array_type_}}); + ZETASQL_CHECK_OK(array_table_1->set_full_name("ArrayTableOrCatalog.Int32Array")); + SimpleTable* array_table_2 = new SimpleTable( + "ArrayTableOrCatalog", {{"Int32Array", int32array_type_}}); + + array_catalog->AddOwnedTable("Int32Array", array_table_1); + AddOwnedTable(array_table_2); + + // Add table for testing case insensitive lookup of column names. + const StructType* struct_with_unicode_column_table; + ZETASQL_CHECK_OK(types_->MakeStructType({{"1𐌰:aô", types_->get_string()}}, + &struct_with_unicode_column_table)); + AddOwnedTable(new SimpleTable("unicode_column_table", + {{"å学", types_->get_int64()}, + {"ô", types_->get_string()}, + {"a", struct_with_unicode_column_table}})); + return absl::OkStatus(); +} // NOLINT(readability/fn_size) + +void SampleCatalogImpl::LoadProtoTables() { + // Add a named struct type. + const StructType* struct_TestStruct; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}, + &struct_TestStruct)); + catalog_->AddType("TestStruct", struct_TestStruct); + + const StructType* struct_AnotherTestStruct; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"K", types_->get_int32()}, {"v", types_->get_bytes()}}, + &struct_AnotherTestStruct)); + catalog_->AddType("AnotherTestStruct", struct_TestStruct); + + AddOwnedTable( + new SimpleTable("TestTable", {{"key", types_->get_int32()}, + {"TestEnum", enum_TestEnum_}, + {"KitchenSink", proto_KitchenSinkPB_}})); + + // We want to be sure that AmbiguousHasTestTable is not the first table + // serialized by SimpleCatalog::Serialize. See b/125914476 for more detail. + AddOwnedTable(new SimpleTable( + "ZZZ_AmbiguousHasTestTable", + {{"key", types_->get_int32()}, {"AmbiguousHas", proto_ambiguous_has_}})); + + AddOwnedTable( + new SimpleTable("CivilTimeTestTable", + {{"key", types_->get_int32()}, + {"CivilTimeTypesSink", proto_CivilTimeTypesSinkPB_}})); + + AddOwnedTable( + new SimpleTable("FieldFormatsTable", + {{"key", types_->get_int32()}, + {"FieldFormatsProto", proto_field_formats_proto_}})); + + AddOwnedTable( + new SimpleTable("ApproxDistanceFunctionOptionsProtoTable", + {{"key", types_->get_int32()}, + {"options", proto_approx_distance_function_options_}})); + + // EnumTable has two pseudo-columns Filename and RowId. + AddOwnedTable(new SimpleTable( + "EnumTable", + {new SimpleColumn("EnumTable", "key", types_->get_int32()), + new SimpleColumn("EnumTable", "TestEnum", enum_TestEnum_), + new SimpleColumn("EnumTable", "AnotherTestEnum", enum_AnotherTestEnum_), + new SimpleColumn("EnumTable", "Filename", types_->get_string(), + {.is_pseudo_column = true}), + new SimpleColumn("EnumTable", "RowId", types_->get_bytes(), + {.is_pseudo_column = true})}, + true /* take_ownership */)); + + AddOwnedTable(new SimpleTable( + "MapFieldTable", {{"key", types_->get_int32()}, + {"MessageWithMapField", proto_MessageWithMapField_}})); + + AddOwnedTable(new SimpleTable( + "Proto3Table", {{"key", types_->get_int32()}, + {"Proto3KitchenSink", proto3_KitchenSinkPB_}})); + + AddOwnedTable(new SimpleTable( + "Proto3InvalidMapTable", + {{"key", types_->get_int32()}, + {"MessageWithInvalidMap", proto3_MessageWithInvalidMap_}})); + + // This table only has pseudo-columns. + AddOwnedTable(new SimpleTable( + "AllPseudoColumns", + { + new SimpleColumn("AllPseudoColumns", "Key", types_->get_int32(), + {.is_pseudo_column = true}), + new SimpleColumn("AllPseudoColumns", "Value", types_->get_string(), + {.is_pseudo_column = true}), + }, + true /* take_ownership */)); + + // Another table with only pseudo-columns, this time with a repeated field. We + // don't extend AllPseudoColumns to avoid breaking pre-existing tests. + AddOwnedTable(new SimpleTable( + "AllPseudoColumnsWithRepeated", + { + new SimpleColumn("AllPseudoColumns", "Key", types_->get_int32(), + {.is_pseudo_column = true}), + new SimpleColumn("AllPseudoColumns", "Value", types_->get_string(), + {.is_pseudo_column = true}), + new SimpleColumn("AllPseudoColumns", "RepeatedValue", + string_array_type_, {.is_pseudo_column = true}), + }, + true /* take_ownership */)); + + { + // This table has an anonymous pseudo-column, which should be inaccessible. + auto table = new SimpleTable("AnonymousPseudoColumn"); + AddOwnedTable(table); + ZETASQL_CHECK_OK(table->set_allow_anonymous_column_name(true)); + ZETASQL_CHECK_OK(table->AddColumn( + new SimpleColumn("AnonymousPseudoColumn", "key", types_->get_int32()), + true /* take_ownership */)); + ZETASQL_CHECK_OK(table->AddColumn( + new SimpleColumn("AnonymousPseudoColumn", "", types_->get_string(), + {.is_pseudo_column = true}), + true /* take_ownership */)); + } + + { + // This table has two duplicate columns. + auto table = new SimpleTable("DuplicateColumns"); + ZETASQL_CHECK_OK(table->set_allow_duplicate_column_names(true)); + AddOwnedTable(table); + ZETASQL_CHECK_OK( + table->AddColumn(new SimpleColumn("DuplicateColumns", "DuplicateColumn", + types_->get_int32()), + /*is_owned=*/true)); + ZETASQL_CHECK_OK( + table->AddColumn(new SimpleColumn("DuplicateColumns", "DuplicateColumn", + types_->get_string()), + /*is_owned=*/true)); + } + + AddOwnedTable(new SimpleTable( + "AllNonKeysNonWritable", + { + new SimpleColumn("AllNonKeysNonWritable", "Key", types_->get_int32(), + {.is_writable_column = true}), + new SimpleColumn("AllNonKeysNonWritable", "Value", + types_->get_string(), {.is_writable_column = false}), + new SimpleColumn("AllNonKeysNonWritable", "RepeatedValue", + int32array_type_, {.is_writable_column = false}), + new SimpleColumn("AllNonKeysNonWritable", "ProtoValue", + proto_TestExtraPB_, {.is_writable_column = false}), + new SimpleColumn("AllNonKeysNonWritable", "StructValue", struct_type_, + {.is_writable_column = false}), + }, + true /* take_ownership */)); + + SimpleTable* complex_types = + new SimpleTable("ComplexTypes", {{"key", types_->get_int32()}, + {"TestEnum", enum_TestEnum_}, + {"KitchenSink", proto_KitchenSinkPB_}, + {"Int32Array", int32array_type_}, + {"TestStruct", nested_struct_type_}, + {"TestProto", proto_TestExtraPB_}}); + ZETASQL_CHECK_OK(complex_types->SetPrimaryKey({0})); + AddOwnedTable(complex_types); + + AddOwnedTable(new SimpleTable( + "MoreComplexTypes", + {{"key", types_->get_int32()}, + {"ArrayOfStruct", struct_array_type_}, + {"StructOfArrayOfStruct", struct_with_array_field_type_}})); + + AddOwnedTable(new SimpleTable( + "StructWithKitchenSinkTable", + {{"kitchen_sink", proto_KitchenSinkPB_}, + {"s", struct_with_kitchen_sink_type_}, + {"t", struct_of_array_of_struct_with_kitchen_sink_type_}})); + + AddOwnedTable( + new SimpleTable("DoublyNestedStructTable", + {{"key", types_->get_int32()}, + {"doubly_nested_struct", doubly_nested_struct_type_}})); + + AddOwnedTable(new SimpleTable("KitchenSinkValueTable", proto_KitchenSinkPB_)); + + AddOwnedTable(new SimpleTable("MessageWithKitchenSinkValueTable", + proto_MessageWithKitchenSinkPB_)); + + AddOwnedTable(new SimpleTable("EmptyMessageValueTable", proto_EmptyMessage_)); + + catalog_->AddOwnedTable( + new SimpleTable("TestExtraPBValueTable", proto_TestExtraPB_)); + + catalog_->AddOwnedTable(new SimpleTable("TestAbPBValueTable", proto_abPB_)); + + catalog_->AddOwnedTable(new SimpleTable("TestBcPBValueTable", proto_bcPB_)); + + catalog_->AddOwnedTable(new SimpleTable("TestBcPBValueProtoTable", + {{"value", proto_bcPB_}, + {"Filename", types_->get_string()}, + {"RowId", types_->get_int64()}})); + + // TestExtraValueTable also has pseudo-columns Filename and RowID. + SimpleTable* extra_value_table; + AddOwnedTable(( + extra_value_table = new SimpleTable( + "TestExtraValueTable", + {new SimpleColumn("TestExtraValueTable", "value", proto_TestExtraPB_), + new SimpleColumn("TestExtraValueTable", "Filename", + types_->get_string(), {.is_pseudo_column = true}), + new SimpleColumn("TestExtraValueTable", "RowId", types_->get_bytes(), + {.is_pseudo_column = true})}, + true /* take_ownership */))); + extra_value_table->set_is_value_table(true); + + // AmbiguousFieldValueTable has a pseudo-column int32_val1 that is + // also a field name. + SimpleTable* ambiguous_field_value_table; + AddOwnedTable((ambiguous_field_value_table = new SimpleTable( + "AmbiguousFieldValueTable", + { + new SimpleColumn("TestExtraValueTable", "value", + proto_TestExtraPB_), + new SimpleColumn("TestExtraValueTable", "int32_val1", + types_->get_string(), + {.is_pseudo_column = true}), + }, + true /* take_ownership */))); + ambiguous_field_value_table->set_is_value_table(true); + + SimpleTable* int64_value_table; + AddOwnedTable(int64_value_table = new SimpleTable( + "Int64ValueTable", + {new SimpleColumn("Int64ValueTable", "IntValue", + types_->get_int64())}, + /*take_ownership=*/true)); + int64_value_table->set_is_value_table(true); + ZETASQL_CHECK_OK(int64_value_table->SetPrimaryKey({0})); + + AddOwnedTable(new SimpleTable( + "ArrayTypes", + {{"Int32Array", int32array_type_}, + {"Int64Array", int64array_type_}, + {"UInt32Array", uint32array_type_}, + {"UInt64Array", uint64array_type_}, + {"StringArray", string_array_type_}, + {"BytesArray", bytes_array_type_}, + {"BoolArray", bool_array_type_}, + {"FloatArray", float_array_type_}, + {"DoubleArray", double_array_type_}, + {"DateArray", date_array_type_}, + // These corresponding legacy types were removed, but we keep the fields + // in the sample table in order not to disturb analyzer test results too + // much (all the variable ids would change if we were to remove them). + // TODO: Eventually remove these. + {"TimestampSecondsArray", timestamp_array_type_}, + {"TimestampMillisArray", timestamp_array_type_}, + {"TimestampMicrosArray", timestamp_array_type_}, + // Real types resume here. + {"TimestampArray", timestamp_array_type_}, + {"ProtoArray", proto_array_type_}, + {"StructArray", struct_array_type_}, + {"JsonArray", json_array_type_}, + {"NumericArray", numeric_array_type_}, + {"BigNumericArray", bignumeric_array_type_}, + {"IntervalArray", interval_array_type_}})); + + const EnumType* enum_TestEnum = + GetEnumType(zetasql_test__::TestEnum_descriptor()); + AddOwnedTable(new SimpleTable("SimpleTypesWithStruct", + {{"key", types_->get_int32()}, + {"TestEnum", enum_TestEnum}, + {"TestStruct", nested_struct_type_}})); + + const ProtoType* proto_recursive_type = + GetProtoType(zetasql_test__::RecursivePB::descriptor()); + AddOwnedTable(new SimpleTable("RecursivePBTable", + {{"RecursivePB", proto_recursive_type}})); + + AddOwnedTable(new SimpleTable( + "KeywordTable", + {{"current_date", types_->get_date()}, + {"current_timestamp", types_->get_timestamp()}, + // The corresponding legacy types were removed, but we keep the fields in + // the sample table in order not to disturb analyzer test results too + // much (all the variable ids would change if we were to remove them). + // TODO: Eventually remove these. + {"current_timestamp_seconds", types_->get_timestamp()}, + {"current_timestamp_millis", types_->get_timestamp()}, + {"current_timestamp_micros", types_->get_timestamp()}})); + + AddOwnedTable(new SimpleTable("TestStructValueTable", struct_type_)); + + AddOwnedTable(new SimpleTable("TestNestedStructValueTable", + doubly_nested_struct_type_)); + + AddOwnedTable(new SimpleTable("StructWithOneFieldValueTable", + struct_with_one_field_type_)); + + AddOwnedTable(new SimpleTable("Int32ValueTable", types_->get_int32())); + + AddOwnedTable(new SimpleTable("Int32ArrayValueTable", int32array_type_)); + + AddOwnedTable( + new SimpleTable("AnnotatedEnumTable", enum_TestEnumWithAnnotations_)); +} + +void SampleCatalogImpl::LoadViews(const LanguageOptions& language_options) { + // Ensure the language options used allow CREATE FUNCTION + LanguageOptions language = language_options; + language.AddSupportedStatementKind(RESOLVED_CREATE_VIEW_STMT); + language.EnableLanguageFeature(FEATURE_CREATE_VIEW_WITH_COLUMN_LIST); + AnalyzerOptions analyzer_options; + analyzer_options.set_language(language); + + auto add_view = [&analyzer_options, this](absl::string_view create_view) { + std::unique_ptr analyzer_output; + ZETASQL_CHECK_OK(AddViewFromCreateView(create_view, analyzer_options, + /*allow_non_temp=*/true, analyzer_output, + *catalog_)); + sql_object_artifacts_.emplace_back(std::move(analyzer_output)); + }; + add_view("CREATE VIEW TwoIntsView SQL SECURITY INVOKER AS SELECT 1 a, 2 b;"); + add_view( + "CREATE VIEW UnprojectedColumnView SQL SECURITY INVOKER AS " + "SELECT a, b FROM (SELECT 1 AS a, 2 AS b, 3 AS c);"); + add_view( + "CREATE VIEW ColumnListView(a, b) SQL SECURITY INVOKER AS " + "SELECT 1, 2;"); + add_view( + "CREATE VIEW CteView SQL SECURITY INVOKER AS " + "WITH t AS (SELECT 1 a, 2 b) SELECT * FROM t;"); + add_view( + "CREATE VIEW OneStructView SQL SECURITY INVOKER AS " + "SELECT STRUCT(1 AS a, 2 AS b) AS ab;"); + add_view( + "CREATE VIEW AsStructView SQL SECURITY INVOKER AS " + "SELECT AS STRUCT 1 a, 2 b;"); + add_view( + "CREATE VIEW OneScalarView SQL SECURITY INVOKER AS " + "SELECT '123' AS ab"); + add_view( + "CREATE VIEW AsScalarView SQL SECURITY INVOKER AS " + "SELECT AS VALUE '123'"); + add_view( + "CREATE VIEW ScanTableView SQL SECURITY INVOKER AS " + "SELECT key AS a, value AS b FROM TwoIntegers;"); + add_view( + "CREATE VIEW ScanViewView SQL SECURITY INVOKER AS " + "SELECT a, 'b' AS b FROM ScanTableView;"); + add_view( + "CREATE VIEW UnspecifiedRightsView SQL SECURITY DEFINER AS " + "SELECT 1 AS a;"); + add_view( + "CREATE VIEW DefinerRightsView SQL SECURITY DEFINER AS " + "SELECT 1 AS a, 'x' AS b, false AS c;"); +} + +void SampleCatalogImpl::LoadNestedCatalogs() { + SimpleCatalog* nested_catalog = + catalog_->MakeOwnedSimpleCatalog("nested_catalog"); + + // Add nested_catalog with some tables with the same and different names. + nested_catalog->AddTable(key_value_table_); + nested_catalog->AddTable("NestedKeyValue", key_value_table_); + + { + // Add a table that only appears in this nested catalog (and in turn, can + // only be found via the nested name). + SimpleTable* nested_key_value_table = new SimpleTable( + "KeyValueNested", + {{"Key", types_->get_int64()}, {"Value", types_->get_string()}}); + ZETASQL_CHECK_OK( + nested_key_value_table->set_full_name("nested_catalog.KeyValueNested")); + nested_catalog->AddOwnedTable(nested_key_value_table); + } + + // Add nested_catalog with some connections + nested_catalog->AddConnection( + owned_connections_.find("connection1")->second.get()); + nested_catalog->AddConnection( + owned_connections_.find("connection2")->second.get()); + nested_catalog->AddConnection( + owned_connections_.find("NestedConnection")->second.get()); + + // Add recursive_catalog which points back to the same catalog. + // This allows resolving names like + // recursive_catalog.recursive_catalog.recursive_catalog.TestTable + catalog_->AddCatalog("recursive_catalog", catalog_.get()); + + // Add a function to the nested catalog: + // nested_catalog.nested_function() -> + FunctionSignature signature( + {types_->get_int64(), {types_->get_int64()}, /*context_id=*/-1}); + std::vector function_name_path = {"nested_catalog", + "nested_function"}; + Function* function = new Function(function_name_path, "sample_functions", + Function::SCALAR, {signature}); + nested_catalog->AddOwnedFunction(function); + + // A scalar function with argument alias support in the nested catalog. + { + FunctionArgumentType aliased( + ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + FunctionArgumentType non_aliased(ARG_TYPE_ANY_2); + std::vector signatures = { + {types_->get_int64(), {aliased, non_aliased}, /*context_id=*/-1}}; + Function* function = new Function( + std::vector{"nested_catalog", "fn_for_argument_alias"}, + "sample_functions", Function::SCALAR, signatures); + nested_catalog->AddOwnedFunction(function); + } + // Add a procedure to the nested catalog: + // nested_catalog.nested_procedure() -> + Procedure* procedure = + new Procedure({"nested_catalog", "nested_procedure"}, signature); + nested_catalog->AddOwnedProcedure(procedure); + + // Add a doubly nested catalog, and a function to the doubly nested catalog: + // nested_catalog.nested_nested_catalog.nested_function() -> + SimpleCatalog* nested_nested_catalog = + nested_catalog->MakeOwnedSimpleCatalog("nested_nested_catalog"); + function_name_path = {"nested_catalog", "nested_nested_catalog", + "nested_function"}; + function = new Function(function_name_path, "sample_functions", + Function::SCALAR, {signature}); + nested_nested_catalog->AddOwnedFunction(function); + + // Add table "nested" to the nested catalog and doubly nested catalog + // with the same name "nested": + // nested_catalog.nested + // nested_catalog.nested.nested + nested_catalog->AddTable("nested", key_value_table_); + SimpleCatalog* duplicate_name_nested_catalog = + nested_catalog->MakeOwnedSimpleCatalog("nested"); + duplicate_name_nested_catalog->AddTable("nested", key_value_table_); + + // Add a struct-typed constant to the doubly nested catalog. + const StructType* nested_constant_struct_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"eee", types_->get_int32()}, {"fff", nested_struct_type_}}, + &nested_constant_struct_type)); + std::unique_ptr constant_struct; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"nested_catalog", "nested_nested_catalog", + "TestConstantStruct"}, + Value::Struct(nested_constant_struct_type, + {Value::Int32(-3456), + Value::Struct(nested_struct_type_, + {Value::Int32(3), + Value::Struct(struct_type_, + {Value::Int32(223), + Value::String("foo")})})}), + &constant_struct)); + nested_nested_catalog->AddOwnedConstant(constant_struct.release()); + + // Add an enum and a proto to the nested catalog. + nested_catalog->AddType(enum_TestEnum_->enum_descriptor()->full_name(), + enum_TestEnum_); + nested_catalog->AddType(proto_KitchenSinkPB_->descriptor()->full_name(), + proto_KitchenSinkPB_); + nested_catalog->AddType( + proto_CivilTimeTypesSinkPB_->descriptor()->full_name(), + proto_CivilTimeTypesSinkPB_); + + // Add TVFs to the nested catalogs. We use this to test name resolution during + // serialization/deserialization. + const std::string kColumnNameKey = "key"; + TVFRelation::ColumnList columns; + columns.emplace_back(kColumnNameKey, types::Int64Type()); + TVFRelation single_key_col_schema(columns); + + int context_id = -1; + nested_catalog->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"nested_catalog", "nested_tvf_one"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + single_key_col_schema, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id), + single_key_col_schema)); + nested_nested_catalog->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"nested_catalog", "nested_nested_catalog", "nested_tvf_two"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + single_key_col_schema, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id), + single_key_col_schema)); + + // Load a nested catalog with a constant whose names conflict with a table + // and its field. + SimpleCatalog* name_conflict_catalog = + catalog_->MakeOwnedSimpleCatalog("name_conflict_table"); + std::unique_ptr constant; + ZETASQL_CHECK_OK( + SimpleConstant::Create({"name_conflict_table", "name_conflict_field"}, + Value::Bool(false), &constant)); + name_conflict_catalog->AddOwnedConstant(constant.release()); + + // Add for testing named constants in catalogs. + SimpleCatalog* nested_catalog_with_constant = + catalog_->MakeOwnedSimpleCatalog("nested_catalog_with_constant"); + ZETASQL_CHECK_OK( + SimpleConstant::Create({"nested_catalog_with_constant", "KnownConstant"}, + Value::Bool(false), &constant)); + nested_catalog_with_constant->AddOwnedConstant(constant.release()); + + // Add for testing conflicts with named + // constants. + SimpleCatalog* nested_catalog_with_catalog = + catalog_->MakeOwnedSimpleCatalog("nested_catalog_with_catalog"); + ZETASQL_CHECK_OK(SimpleConstant::Create( + {"nested_catalog_with_catalog", "TestConstantBool"}, Value::Bool(false), + &constant)); + nested_catalog_with_catalog->AddOwnedConstant(constant.release()); + ZETASQL_CHECK_OK(SimpleConstant::Create({"nested_catalog_with_catalog", "c"}, + Value::Double(-9999.999), &constant)); + nested_catalog_with_catalog->AddOwnedConstant(constant.release()); + SimpleCatalog* nested_catalog_catalog = + nested_catalog_with_catalog->MakeOwnedSimpleCatalog( + "nested_catalog_catalog"); + ZETASQL_CHECK_OK(SimpleConstant::Create( + {"nested_catalog_with_catalog", "nested_catalog_catalog", "a"}, + Value::Float(-1.4987f), &constant)); + nested_catalog_catalog->AddOwnedConstant(constant.release()); + ZETASQL_CHECK_OK(SimpleConstant::Create( + {"nested_catalog_with_catalog", "nested_catalog_catalog", "c"}, + Value::String("foo"), &constant)); + nested_catalog_catalog->AddOwnedConstant(constant.release()); + + // Add a constant to . + ZETASQL_CHECK_OK(SimpleConstant::Create({"nested_catalog", "TestConstantBool"}, + Value::Bool(false), &constant)); + nested_catalog->AddOwnedConstant(constant.release()); + + // Add another constant to that conflicts with a procedure. + ZETASQL_CHECK_OK(SimpleConstant::Create({"nested_catalog", "nested_procedure"}, + Value::Int64(2345), &constant)); + nested_catalog->AddOwnedConstant(constant.release()); + + // Add a constant to which requires backticks. + std::unique_ptr string_constant_nonstandard_name; + + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"nested_catalog", "Test Constant-String"}, + Value::String("Test constant in nested catalog"), + &string_constant_nonstandard_name)); + nested_catalog->AddOwnedConstant(string_constant_nonstandard_name.release()); + + // Add struct constant with the same name as a nested catalog + const StructType* nested_nested_catalog_type; + ZETASQL_CHECK_OK(types_->MakeStructType({{"xxxx", types_->get_int64()}}, + &nested_nested_catalog_type)); + + SimpleCatalog* wwww_catalog = nested_catalog->MakeOwnedSimpleCatalog("wwww"); + + std::unique_ptr wwww_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"nested_catalog", "wwww"}, + Value::Struct(nested_nested_catalog_type, {Value::Int64(8)}), + &wwww_constant)); + nested_catalog->AddOwnedConstant(wwww_constant.release()); + + std::unique_ptr xxxx_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"nested_catalog", "wwww", "xxxx"}, + Value::Struct(nested_nested_catalog_type, {Value::Int64(8)}), + &xxxx_constant)); + wwww_catalog->AddOwnedConstant(xxxx_constant.release()); + + // Load a nested catalog with a name that resembles a system variable. + SimpleCatalog* at_at_nested_catalog = + catalog_->MakeOwnedSimpleCatalog("@@nested_catalog"); + std::unique_ptr at_at_nested_catalog_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"@@nested_catalog", "sysvar2"}, Value::Int64(8), + &at_at_nested_catalog_constant)); + at_at_nested_catalog->AddOwnedConstant( + at_at_nested_catalog_constant.release()); + + { + std::unique_ptr rounding_mode_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"nested_catalog", "constant_rounding_mode"}, + Value::Enum(types::RoundingModeEnumType(), "ROUND_HALF_EVEN"), + &rounding_mode_constant)); + nested_catalog->AddOwnedConstant(std::move(rounding_mode_constant)); + } + + auto udf_catalog = nested_catalog->MakeOwnedSimpleCatalog("udf"); + function = + new Function("timestamp_add", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::REQUIRED}, + {types_->get_int64(), FunctionArgumentType::REQUIRED}, + {types_->get_int64(), FunctionArgumentType::REQUIRED}}, + /*context_id=*/-1}); + udf_catalog->AddOwnedFunction(function); +} + +static FreestandingDeprecationWarning CreateDeprecationWarning( + int id, + DeprecationWarning_Kind kind = DeprecationWarning::PROTO3_FIELD_PRESENCE) { + FreestandingDeprecationWarning warning; + const std::string foo_id = absl::StrCat("foo_", id); + warning.set_message(absl::StrCat("Operation is deprecated")); + warning.set_caret_string(absl::StrCat("some caret string for ", foo_id, "\n", + " ^")); + warning.mutable_deprecation_warning()->set_kind(kind); + + ErrorLocation* warning_location = warning.mutable_error_location(); + warning_location->set_line(10 + id); + warning_location->set_column(20 + id); + warning_location->set_filename(absl::StrCat("module", id, ".sqlm")); + + return warning; +} + +void SampleCatalogImpl::AddFunctionWithArgumentType(std::string type_name, + const Type* arg_type) { + auto function = std::make_unique( + absl::StrCat("fn_on_", type_name), "sample_functions", Function::SCALAR); + function->AddSignature({types_->get_bool(), {arg_type}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); +} + +void SampleCatalogImpl::LoadExtendedSubscriptFunctions() { + // Add new signatures for '$subscript_with_offset' so we can do some + // additional testing. The signatures are: + // 1) [OFFSET()]: + // $subscript_with_offset(string, int64_t) -> (string) + // 2) [OFFSET()]: + // $subscript_with_offset(string, string) -> (string) + const Function* subscript_offset_function; + ZETASQL_CHECK_OK(catalog_->GetFunction("$subscript_with_offset", + &subscript_offset_function)); + ABSL_CHECK(subscript_offset_function != nullptr); + // If we ever update the builtin function implementation to actually include + // a signature, then take a look at this code to see if it is still needed. + ABSL_CHECK_EQ(subscript_offset_function->NumSignatures(), 0); + Function* mutable_subscript_offset_function = + const_cast(subscript_offset_function); + mutable_subscript_offset_function->AddSignature( + {types_->get_string(), + {types_->get_string(), types_->get_int64()}, + /*context_id=*/-1}); + mutable_subscript_offset_function->AddSignature( + {types_->get_string(), + {types_->get_string(), types_->get_string()}, + /*context_id=*/-1}); +} + +const Function* SampleCatalogImpl::AddFunction( + absl::string_view name, Function::Mode mode, + std::vector function_signatures, + FunctionOptions function_options) { + for (const FunctionSignature& sig : function_signatures) { + ZETASQL_CHECK_OK(sig.IsValid(PRODUCT_INTERNAL)); + ZETASQL_CHECK_OK(sig.IsValid(PRODUCT_EXTERNAL)); + } + auto function = std::make_unique(name, "sample_functions", mode, + std::move(function_signatures), + std::move(function_options)); + const Function* function_ptr = function.get(); + catalog_->AddOwnedFunction(std::move(function)); + return function_ptr; +} + +namespace { + +ABSL_CONST_INIT static absl::Mutex catalog_registry_mu(absl::kConstInit); +ABSL_CONST_INIT static bool catalog_registry_closed = false; + +std::vector>& +CatalogRegistry() { + static absl::NoDestructor< + std::vector>> + registry; + return *registry; +} + +struct RegisterForSampleCatalog { + explicit RegisterForSampleCatalog( + absl::FunctionRef fn) { + absl::MutexLock lock(&catalog_registry_mu); + ABSL_CHECK(!catalog_registry_closed) + << "Cannot add new registrations functions for the sample catalog " + "after one was already created."; + CatalogRegistry().push_back(fn); + } +}; +} // namespace + +void SampleCatalogImpl::LoadAllRegisteredCatalogChanges() { + if (!catalog_registry_closed) { + absl::MutexLock lock(&catalog_registry_mu); + catalog_registry_closed = true; + } + for (auto const& fn : CatalogRegistry()) { + fn(this->catalog_.get()); + } +} + +namespace { + +RegisterForSampleCatalog test_function = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + // Add a function to illustrate how repeated/optional arguments are + // resolved. + Function* function = + new Function("test_function", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::REQUIRED}, + {types_->get_int64(), FunctionArgumentType::REPEATED}, + {types_->get_int64(), FunctionArgumentType::REPEATED}, + {types_->get_int64(), FunctionArgumentType::REQUIRED}, + {types_->get_int64(), FunctionArgumentType::OPTIONAL}}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + }); + +RegisterForSampleCatalog volatile_function = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + Function* function = new Function( + "volatile_function", "sample_functions", Function::SCALAR, + {{types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::REQUIRED}}, + /*context_id=*/-1}}, + FunctionOptions().set_volatility(FunctionEnums::VOLATILE)); + catalog_->AddOwnedFunction(function); + }); + +RegisterForSampleCatalog stable_function = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + Function* function = new Function( + "stable_function", "sample_functions", Function::SCALAR, + {{types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::REQUIRED}}, + /*context_id=*/-1}}, + FunctionOptions().set_volatility(FunctionEnums::STABLE)); + catalog_->AddOwnedFunction(function); + }); +} // namespace + +void SampleCatalogImpl::LoadFunctions() { + Function* function; + // Add a function that takes a specific proto as an argument. + function = + new Function("fn_on_KitchenSinkPB", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {proto_KitchenSinkPB_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes a specific enum as an argument. + function = + new Function("fn_on_TestEnum", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {enum_TestEnum_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // These sample functions are named 'fn_on_' with one argument of + // type that returns a bool. + AddFunctionWithArgumentType("bool", types_->get_bool()); + AddFunctionWithArgumentType("int32", types_->get_int32()); + AddFunctionWithArgumentType("int64", types_->get_int64()); + AddFunctionWithArgumentType("uint32", types_->get_uint32()); + AddFunctionWithArgumentType("uint64", types_->get_uint64()); + AddFunctionWithArgumentType("float", types_->get_float()); + AddFunctionWithArgumentType("double", types_->get_double()); + AddFunctionWithArgumentType("date", types_->get_date()); + AddFunctionWithArgumentType("timestamp", types_->get_timestamp()); + AddFunctionWithArgumentType("string", types_->get_string()); + AddFunctionWithArgumentType("bytes", types_->get_bytes()); + + // Add a function with bytes and string overload. + function = new Function("fn_overloaded_bytes_and_string", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_string(), {types_->get_string()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bytes(), {types_->get_bytes()}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with bytes and date overload. + function = new Function("fn_overloaded_bytes_and_date", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), {types_->get_date()}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with bytes and timestamp overload. + function = new Function("fn_overloaded_bytes_and_timestamp", + "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), {types_->get_timestamp()}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with bytes and time overload. + function = new Function("fn_overloaded_bytes_and_time", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), {types_->get_time()}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with bytes and datetime overload. + function = new Function("fn_overloaded_bytes_and_datetime", + "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), {types_->get_datetime()}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with bytes and enum overload. + function = new Function("fn_overloaded_bytes_and_enum", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), {enum_TestEnum_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with bytes and proto overload. + function = new Function("fn_overloaded_bytes_and_proto", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {types_->get_bytes()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), {proto_KitchenSinkPB_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes an arbitrary type argument. + function = new Function("fn_on_arbitrary_type_argument", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {ARG_TYPE_ARBITRARY}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes any type enum. + function = + new Function("fn_on_any_enum", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {ARG_ENUM_ANY}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes any type proto. + function = + new Function("fn_on_any_proto", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {ARG_PROTO_ANY}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes any type struct. + function = + new Function("fn_on_any_struct", "sample_functions", Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {ARG_STRUCT_ANY}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes any array and returns element type. + function = new Function("fn_on_any_array_returns_element", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{ARG_TYPE_ANY_1}, {ARG_ARRAY_TYPE_ANY_1}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes any type and returns an array of that type. + function = new Function("fn_on_any_element_returns_array", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{ARG_ARRAY_TYPE_ANY_1}, {ARG_TYPE_ANY_1}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes an array and returns an int32_t type. + function = new Function("fn_on_int32_array_returns_int32", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{types_->get_int32()}, {int32array_type_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes an array and returns an int64_t type. + function = new Function("fn_on_int64_array_returns_int64", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{types_->get_int64()}, {int64array_type_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes a STRUCT and returns bool. + function = new Function("fn_on_struct_int32_string", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {struct_type_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + const StructType* struct_int32_date_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_int32()}, {"b", types_->get_date()}}, + &struct_int32_date_type)); + + // Add a function that takes a STRUCT and returns bool. + function = new Function("fn_on_struct_int32_date", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {struct_int32_date_type}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + const StructType* struct_int64_string_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_int64()}, {"b", types_->get_string()}}, + &struct_int64_string_type)); + + // Add a function that takes a STRUCT and returns bool. + function = new Function("fn_on_struct_int64_string", "sample_functions", + Function::SCALAR); + function->AddSignature( + {types_->get_bool(), {struct_int64_string_type}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(absl::WrapUnique(function)); + + // Add a function that takes a MAP and returns an int32_t type. + function = new Function("fn_on_int32_map_returns_int32", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{types_->get_int32()}, {int32map_type_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes a MAP and returns an int64_t type. + function = new Function("fn_on_int64_map_returns_int64", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{types_->get_int64()}, {int64map_type_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes a MAP and returns an bytes type. + function = new Function("fn_on_bytes_map_returns_bytes", "sample_functions", + Function::SCALAR); + function->AddSignature( + {{types_->get_bytes()}, {bytesmap_type_}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Adds an scalar function that takes multiple repeated and optional + // arguments. + function = new Function( + "fn_rep_opt", "sample_functions", Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("a0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("r0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REPEATED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("r1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REPEATED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("r2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REPEATED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("a1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + ZETASQL_CHECK_OK(function->signatures()[0].IsValid(ProductMode::PRODUCT_EXTERNAL)); + + AddFunction("fn_repeated_with_optional_named_only", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Optional().String().NameOnly("o1")) + .Build()}); + + AddFunction("fn_repeated_diff_args_optional_named_only", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Repeated().Int64()) + .AddArg(ArgBuilder().Optional().Bool().NameOnly("o1")) + .Build()}); + + AddFunction("fn_repeated_arbitrary_with_optional_named_only", + Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Optional().Any().Name("o1")) + .Build()}); + + AddFunction("fn_repeated_with_optional_named_or_positional", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Optional().String().Name("o1")) + .Build()}); + + AddFunction("fn_repeated_diff_args_optional_named_or_positional", + Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Repeated().Int64()) + .AddArg(ArgBuilder().Optional().Bool().Name("o1")) + .Build()}); + + AddFunction("fn_repeated_arbitrary_with_optional_named_or_positional", + Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Optional().Any().Name("o1")) + .Build()}); + + AddFunction("fn_repeated_with_required_named", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().String().NameOnly("r1")) + .Build()}); + + AddFunction("fn_repeated_diff_args_required_named", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(ArgBuilder().Repeated().Int64()) + .AddArg(ArgBuilder().Bool().NameOnly("r1")) + .Build()}); + + AddFunction("fn_repeated_arbitrary_with_required_named", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Any().NameOnly("r1")) + .Build()}); + + AddFunction("fn_repeated_t1_t2_with_optional_named_t1", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().T1()) + .AddArg(ArgBuilder().Repeated().T2()) + .AddArg(ArgBuilder().Optional().T1().NameOnly("o1")) + .Build()}); + + AddFunction("fn_repeated_t1_arbitrary_with_optional_named_t1", + Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().T1()) + .AddArg(ArgBuilder().Repeated().Any()) + .AddArg(ArgBuilder().Optional().T1().NameOnly("o1")) + .Build()}); + + AddFunction( + "fn_optional_any", Function::SCALAR, + {SignatureBuilder().AddArg(ArgBuilder().Optional().Any()).Build()}); + + AddFunction( + "fn_repeated_any", Function::SCALAR, + {SignatureBuilder().AddArg(ArgBuilder().Repeated().Any()).Build()}); + + AddFunction( + "fn_optional_t1", Function::SCALAR, + {SignatureBuilder().AddArg(ArgBuilder().Optional().T1()).Build()}); + + AddFunction("fn_optional_t1_ret_t1", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Optional().T1()) + .Returns(ArgBuilder().T1()) + .Build()}); + + AddFunction( + "fn_repeated_t1", Function::SCALAR, + {SignatureBuilder().AddArg(ArgBuilder().Repeated().T1()).Build()}); + + AddFunction("fn_repeated_t1_ret_t1", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().T1()) + .Returns(ArgBuilder().T1()) + .Build()}); + + // Adds an aggregate function that takes no argument but supports order by. + function = new Function("sort_count", "sample_functions", Function::AGGREGATE, + {{types_->get_int64(), {}, /*context_id=*/-1}}, + FunctionOptions().set_supports_order_by(true)); + catalog_->AddOwnedFunction(function); + + // Adds an aggregate function that takes multiple arguments and supports + // order by arguments. + function = new Function( + "multi_sort_count", "sample_functions", Function::AGGREGATE, + {{types_->get_int64(), + {types_->get_int32(), types_->get_int64(), types_->get_string()}, + /*context_id=*/-1}}, + FunctionOptions().set_supports_order_by(true)); + catalog_->AddOwnedFunction(function); + + // Adds fn_agg_string_string_collation(STRING, STRING) -> INT64. + // Enables uses_operation_collation to test collation resolution. + function = new Function( + "fn_agg_string_string_collation", "sample_functions", Function::AGGREGATE, + {{types_->get_int64(), + {types_->get_string(), types_->get_string()}, + /*context_id=*/-1, + FunctionSignatureOptions().set_uses_operation_collation(true)}}, + FunctionOptions(FunctionOptions::ORDER_UNSUPPORTED, + /*window_framing_support_in=*/true)); + catalog_->AddOwnedFunction(function); + + // Do not add more to this function. Instead, use RegisterForSampleCatalog + // inside an unnamed namespace. Context: Under some compilation modes all + // local variables get their own stack location and this causes the stack + // limit to be exceeded. +} + +namespace { + +RegisterForSampleCatalog fn_reject_collation = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + + // Adds fn_reject_collation(STRING, ANY TYPE) -> INT64. + // Enables rejects_collation to test collation resolution. + catalog_->AddOwnedFunction(new Function( + "fn_reject_collation", "sample_functions", Function::SCALAR, + {{types_->get_int64(), + {types_->get_string(), + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("second_arg", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}}, + /*context_id=*/-1, + FunctionSignatureOptions().set_rejects_collation(true)}})); + }); + +// Add the following test analytic functions. All functions have the same +// list of function signatures: +// arguments: (), (ARG_TYPE_ANY_1) and (, )) +// return: +// +// They differ in the window support: +// --------------------------------------------------------------------------- +// Mode ORDER BY Window Frame Null Handling +// Modifier +// -------- ----------- --------------------------- +// afn_order ANALYTIC Required Unsupported Unsupported +// afn_no_order_no_frame ANALYTIC Unsupported Unsupported Unsupported +// afn_agg AGGREGATE Optional Supported Unsupported +// afn_null_handling AGGREGATE Optional Unsupported Supported +// -------------------------------------------------------------------------- + +RegisterForSampleCatalog test_analytic_functions = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + std::vector function_signatures; + function_signatures.push_back( + {types_->get_int64(), {}, /*context_id=*/-1}); + function_signatures.push_back( + {types_->get_int64(), {ARG_TYPE_ANY_1}, /*context_id=*/-1}); + function_signatures.push_back( + {types_->get_int64(), + {types_->get_int64(), + FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions().set_argument_name( + "weight", kPositionalOrNamed))}, + -1 /* context */}); + + Function* function = + new Function("afn_order", "sample_functions", Function::ANALYTIC, + function_signatures, + FunctionOptions(FunctionOptions::ORDER_REQUIRED, + /*window_framing_support_in=*/false)); + catalog_->AddOwnedFunction(function); + + function = + new Function("afn_no_order_no_frame", "sample_functions", + Function::ANALYTIC, function_signatures, + FunctionOptions(FunctionOptions::ORDER_UNSUPPORTED, + /*window_framing_support_in=*/false)); + catalog_->AddOwnedFunction(function); + + function = + new Function("afn_agg", "sample_functions", Function::AGGREGATE, + function_signatures, + FunctionOptions(FunctionOptions::ORDER_OPTIONAL, + /*window_framing_support_in=*/true)); + catalog_->AddOwnedFunction(function); + + function = + new Function("afn_null_handling", "sample_functions", + Function::AGGREGATE, function_signatures, + FunctionOptions(FunctionOptions::ORDER_OPTIONAL, + /*window_framing_support_in=*/false) + .set_supports_order_by(true) + .set_supports_limit(true) + .set_supports_null_handling_modifier(true)); + catalog_->AddOwnedFunction(function); + }); + +RegisterForSampleCatalog null_of_type = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + // NULL_OF_TYPE(string) -> (a NULL of type matching the named simple + // type). This is testing resolving functions where the return type is + // determined dynamically based on literal values of the arguments. The + // callback overrides the INT64 return type in the signature. + catalog_->AddOwnedFunction(new Function( + "null_of_type", "sample_functions", Function::SCALAR, + {{{types::Int64Type()}, {types::StringType()}, /*context_id=*/-1}}, + FunctionOptions().set_compute_result_type_callback( + &ComputeResultTypeCallbackForNullOfType))); + }); + +RegisterForSampleCatalog safe_supported_function = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + catalog_->AddOwnedFunction(new Function( + "safe_supported_function", "sample_functions", Function::SCALAR, + {{types_->get_int64(), {}, /*context_id=*/-1}}, FunctionOptions())); + }); + +RegisterForSampleCatalog safe_unsupported_function = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + zetasql::TypeFactory* types_ = catalog_->type_factory(); + catalog_->AddOwnedFunction(new Function( + "safe_unsupported_function", "sample_functions", Function::SCALAR, + {{types_->get_int64(), {}, /*context_id=*/-1}}, + FunctionOptions().set_supports_safe_error_mode(false))); + }); + +RegisterForSampleCatalog deprecation_warnings = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + // Add a function that triggers a deprecation warning. + Function* function = new Function("deprecation_warning", + "sample_functions", Function::SCALAR); + + FunctionSignature deprecation_warning_signature( + types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); + deprecation_warning_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/1)}); + function->AddSignature(deprecation_warning_signature); + catalog_->AddOwnedFunction(function); + + function = new Function("deprecation_warning2", "sample_functions", + Function::SCALAR); + FunctionSignature deprecation_warning2_signature( + types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); + deprecation_warning2_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/2)}); + function->AddSignature(deprecation_warning2_signature); + catalog_->AddOwnedFunction(function); + + // Add a function that triggers two deprecation warnings with the same + // kind. + function = new Function("two_deprecation_warnings_same_kind", + "sample_functions", Function::SCALAR); + + FunctionSignature two_deprecation_warnings_same_kind_signature( + types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); + two_deprecation_warnings_same_kind_signature + .SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/2), + CreateDeprecationWarning(/*id=*/3)}); + function->AddSignature(two_deprecation_warnings_same_kind_signature); + catalog_->AddOwnedFunction(function); + + // Add a function that triggers two deprecation warnings with different + // kinds. + function = new Function("two_deprecation_warnings", "sample_functions", + Function::SCALAR); + FunctionSignature two_deprecation_warnings_signature( + types::Int64Type(), /*arguments=*/{}, /*context_id=*/-1); + two_deprecation_warnings_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/4), + CreateDeprecationWarning( + /*id=*/5, DeprecationWarning::DEPRECATED_FUNCTION_SIGNATURE)}); + function->AddSignature(two_deprecation_warnings_signature); + catalog_->AddOwnedFunction(function); + }); + +RegisterForSampleCatalog fn_map_type_any_1_2_lambda_any_1_any_2_return_bool = + RegisterForSampleCatalog([](zetasql::SimpleCatalog* catalog_) { + // Function taking a map and a lambda with key argument type and value + // return type, and returning a bool. + const auto mode = Function::SCALAR; + auto function = std::make_unique( + "fn_map_type_any_1_2_lambda_any_1_any_2_return_bool", + "sample_functions", mode); + function->AddSignature( + {types::BoolType(), + {ARG_MAP_TYPE_ANY_1_2, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2)}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + }); + +} // namespace + +void SampleCatalogImpl::LoadFunctions2() { + // Do not add more to this function. Instead, use RegisterForSampleCatalog + // inside an unnamed namespace. Context: Under some compilation modes all + // local variables get their own stack location and this causes the stack + // limit to be exceeded. + + Function* function; + + catalog_->AddOwnedFunction(new Function( + "anon_non_anon", "sample_functions", Function::SCALAR, + {{types_->get_int64(), {}, /*context_id=*/-1}}, FunctionOptions())); + + function = new AnonFunction( + "anon_test", "sample_functions", + {{types_->get_int64(), + {/*expr=*/types_->get_int64(), + /*lower_bound=*/{types_->get_int64(), FunctionArgumentType::OPTIONAL}, + /*upper_bound=*/{types_->get_int64(), FunctionArgumentType::OPTIONAL}}, + /*context_id=*/-1}}, + FunctionOptions() + .set_supports_clamped_between_modifier(true) + .set_supports_over_clause(false), + "sum"); + catalog_->AddOwnedFunction(function); + + // Add a function that takes two named arguments with one signature. + const auto named_required_format_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( + "format_string", kPositionalOrNamed)); + const auto named_required_date_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( + "date_string", kPositionalOrNamed)); + const auto named_required_format_arg_error_if_positional = + FunctionArgumentType(types_->get_string(), + FunctionArgumentTypeOptions().set_argument_name( + "format_string", kNamedOnly)); + const auto named_required_date_arg_error_if_positional = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( + "date_string", kNamedOnly)); + const auto named_optional_date_arg_error_if_positional = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("date_string", kNamedOnly)); + const auto named_optional_format_arg = FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("format_string", kPositionalOrNamed)); + const auto named_optional_date_arg = FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("date_string", kPositionalOrNamed)); + const auto named_optional_const_format_arg = FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_be_constant() + .set_argument_name("format_string", kPositionalOrNamed)); + const auto non_named_required_format_arg = + FunctionArgumentType(types_->get_string(), FunctionArgumentTypeOptions()); + const auto non_named_required_date_arg = + FunctionArgumentType(types_->get_string(), FunctionArgumentTypeOptions()); + const auto non_named_optional_format_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)); + const auto non_named_optional_date_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)); + const auto named_optional_arg_named_not_null = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_be_non_null() + .set_argument_name("arg", kNamedOnly)); + const auto named_rounding_mode = + FunctionArgumentType(types::RoundingModeEnumType(), + FunctionArgumentTypeOptions().set_argument_name( + "rounding_mode", kPositionalOrNamed)); + + const auto mode = Function::SCALAR; + + function = new Function("fn_named_args", "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {named_required_format_arg, named_required_date_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + function = new Function("fn_const_named_arg", "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {named_optional_const_format_arg, named_optional_date_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add functions with two named optional/repeated arguments on one signature. + function = new Function("fn_named_args_optional", "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {named_optional_format_arg, named_optional_date_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes two named arguments with two signatures with + // optional argument types. + function = + new Function("fn_named_args_two_signatures", "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {named_required_format_arg, named_required_date_arg}, + /*context_id=*/-1}); + function->AddSignature({types_->get_bool(), + {named_required_date_arg, named_required_format_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function that takes two non-named arguments and one named argument in + // each of two signatures. + function = new Function("fn_three_named_args_two_signatures", + "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {non_named_required_format_arg, non_named_required_date_arg, + named_required_format_arg}, + /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), + {non_named_required_format_arg, non_named_required_date_arg, + named_required_date_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with two named arguments where neither may be specified + // positionally. + function = new Function("fn_named_args_error_if_positional", + "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {named_required_format_arg_error_if_positional, + named_required_date_arg_error_if_positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with two named arguments where the first may not be + // specified positionally. + function = new Function("fn_named_args_error_if_positional_first_arg", + "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {named_required_format_arg_error_if_positional, named_required_date_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with two named arguments where the second may not be + // specified positionally. + function = new Function("fn_named_args_error_if_positional_second_arg", + "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {named_required_format_arg, named_required_date_arg_error_if_positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with two named arguments, one required and one optional, + // and neither may be specified positionally. + function = new Function("fn_named_optional_args_error_if_positional", + "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {named_required_format_arg_error_if_positional, + named_optional_date_arg_error_if_positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with two optional arguments, one regular and one named that + // cannot be specified positionally. + function = + new Function("fn_optional_named_optional_args", "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {{types_->get_string(), FunctionArgumentType::OPTIONAL}, + named_optional_date_arg_error_if_positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with three optional arguments, one regular and two named, + // both cannot be specified positionally and first cannot be NULL. + function = new Function("fn_optional_named_optional_not_null_args", + "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {{types_->get_string(), FunctionArgumentType::OPTIONAL}, + named_optional_arg_named_not_null, + named_optional_date_arg_error_if_positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with two signatures, one using regular arguments and one + // using named arguments that cannot be specified positionally. + function = + new Function("fn_regular_and_named_signatures", "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {{types_->get_string(), FunctionArgumentType::REQUIRED}, + {types_->get_string(), FunctionArgumentType::OPTIONAL}}, + /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), + {{types_->get_string(), FunctionArgumentType::REQUIRED}, + named_optional_date_arg_error_if_positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add a function with one unnamed and two named STRING arguments and + // returning a STRING. + function = new Function("fn_named_arguments_returns_string", + "sample_functions", mode); + function->AddSignature( + {types_->get_string(), + {{types_->get_string(), FunctionArgumentType::REQUIRED}, + named_required_format_arg, + named_required_date_arg}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // A FunctionSignatureArgumentConstraintsCallback that checks for NULL + // arguments. + auto sanity_check_nonnull_arg_constraints = + [](const FunctionSignature& signature, + absl::Span arguments) -> std::string { + ABSL_CHECK(signature.IsConcrete()); + ABSL_CHECK_EQ(signature.NumConcreteArguments(), arguments.size()); + for (int i = 0; i < arguments.size(); ++i) { + ABSL_CHECK(arguments[i].type()->Equals(signature.ConcreteArgumentType(i))); + if (arguments[i].is_null()) { + if (signature.ConcreteArgument(i).has_argument_name()) { + return absl::StrCat("Argument `", + signature.ConcreteArgument(i).argument_name(), + "`: NULL argument is not allowed"); + } else { + return absl::StrCat("Argument ", i + 1, + ": NULL argument is not allowed"); + } + } + } + return ""; + }; + + // A PostResolutionArgumentConstraintsCallback that restricts all the provided + // INT64 arguments to be nonnegative if they are literals. + auto post_resolution_arg_constraints = + [](const FunctionSignature& signature, + absl::Span arguments, + const LanguageOptions& language_options) -> absl::Status { + for (int i = 0; i < arguments.size(); ++i) { + ABSL_CHECK(arguments[i].type()->Equals(signature.ConcreteArgumentType(i))); + if (!arguments[i].type()->IsInt64() || !arguments[i].is_literal()) { + continue; + } + if (arguments[i].literal_value()->int64_value() < 0) { + return MakeSqlError() + << "Argument " + << (signature.ConcreteArgument(i).has_argument_name() + ? signature.ConcreteArgument(i).argument_name() + : std::to_string(i + 1)) + << " must not be negative"; + } + } + return absl::OkStatus(); + }; + + // Add a function with an argument constraint that verifies the concrete + // arguments in signature matches the input argument list, and rejects + // any NULL arguments. + function = + new Function("fn_named_opt_args_nonnull_nonnegative_constraints", + "sample_functions", mode, + FunctionOptions().set_post_resolution_argument_constraint( + post_resolution_arg_constraints)); + FunctionSignature signature_with_constraints{ + types_->get_bool(), + {{types_->get_string(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1_string", kPositionalOrNamed)}, + {types_->get_int64(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_argument_name("o2_int64", kPositionalOrNamed)}, + {types_->get_double(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_argument_name("o3_double", kPositionalOrNamed)}}, + /*context_id=*/-1, + FunctionSignatureOptions().set_constraints( + sanity_check_nonnull_arg_constraints)}; + function->AddSignature(signature_with_constraints); + catalog_->AddOwnedFunction(function); + + // Similar as the previous function, but the arguments are unnamed. + function = + new Function("fn_unnamed_opt_args_nonnull_nonnegative_constraints", + "sample_functions", mode, + FunctionOptions().set_post_resolution_argument_constraint( + post_resolution_arg_constraints)); + FunctionSignature signature_with_unnamed_args_constraints{ + types_->get_bool(), + {{types_->get_string(), FunctionArgumentType::OPTIONAL}, + {types_->get_int64(), FunctionArgumentType::OPTIONAL}, + {types_->get_double(), FunctionArgumentType::OPTIONAL}}, + /*context_id=*/-1, + FunctionSignatureOptions().set_constraints( + sanity_check_nonnull_arg_constraints)}; + function->AddSignature(signature_with_unnamed_args_constraints); + catalog_->AddOwnedFunction(function); + + // Adds a templated function that generates its result type via the callback. + function = new Function("fn_result_type_from_arg", "sample_functions", mode, + FunctionOptions().set_compute_result_type_callback( + &ComputeResultTypeFromStringArgumentValue)); + function->AddSignature( + {{types_->get_string()}, + {{types_->get_string(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kPositionalOrNamed)}, + {types_->get_string(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_argument_name("type_name", kPositionalOrNamed)}}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Function with signatures that should not show up in signature mismatch + // error messages. + function = new Function("fn_with_hidden_signatures", "sample_functions", mode, + FunctionOptions()); + // Two good signatures + function->AddSignature({{types_->get_string()}, + {{types_->get_int64()}}, + /*context_id=*/-1}); + function->AddSignature({{types_->get_string()}, + {{types_->get_int32()}}, + /*context_id=*/-1}); + // Two hidden signatures + FunctionSignature deprecated_signature{{types_->get_string()}, + {{types_->get_string()}}, + /*context_id=*/-1}; + deprecated_signature.SetIsDeprecated(true); + function->AddSignature(deprecated_signature); + FunctionSignature internal_signature{ + {types_->get_string()}, + {{types_->get_string()}, {types_->get_string()}}, + /*context_id=*/-1, + FunctionSignatureOptions().set_is_internal(true)}; + function->AddSignature(internal_signature); + catalog_->AddOwnedFunction(function); + + // Adds a function accepting a Sequence argument. + FunctionOptions function_options; + std::function(const absl::Span)> + evaluator = [&](absl::Span args) -> absl::StatusOr { + return Value::Int64(1); + }; + function_options.set_evaluator(FunctionEvaluator(evaluator)); + + function = new Function("fn_with_sequence_arg", "sample_functions", mode, + function_options); + function->AddSignature({{types_->get_int64()}, + {FunctionArgumentType::AnySequence()}, + /*context_id=*/-1}); + // Adds a function signature for accepting both a Sequence argument and a + // lambda to ensure one doesn't break the other. + function->AddSignature( + {{types_->get_int64()}, + {ARG_TYPE_ANY_1, FunctionArgumentType::AnySequence(), + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2)}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + // Add function with constant expression argument. + function = new Function("fn_with_constant_expr_arg", "sample_functions", + Function::SCALAR); + const auto string_const_expression_arg = zetasql::FunctionArgumentType( + types_->get_string(), zetasql::FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::REQUIRED) + .set_must_be_constant_expression()); + function->AddSignature( + {types_->get_bool(), {string_const_expression_arg}, /*context_id=*/-1}); + catalog_->AddOwnedFunction(function); + + { + auto function = std::make_unique("fn_with_named_rounding_mode", + "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {named_rounding_mode}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // Function with non-concrete return type but with a + // ComputeResultTypeCallback. Calling this function should not cause function + // resolver to complain the signature is not concrete. + { + auto function = std::make_unique( + "non_concrete_return_type_with_compute_result_type_callback", + "sample_functions", mode, + FunctionOptions().set_compute_result_type_callback( + &ComputeResultTypeCallbackToStruct)); + const FunctionArgumentType positional(ARG_TYPE_ANY_1); + function->AddSignature({ARG_TYPE_ARBITRARY, + {positional, positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // Arguments and return type are both ARG_TYPE_ANY_3. + { + auto function = std::make_unique("fn_with_arg_type_any_3", + "sample_functions", mode); + const FunctionArgumentType positional(ARG_TYPE_ANY_3); + function->AddSignature({ARG_TYPE_ANY_3, + {positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_TYPE_ANY_3 arguments + ARRAY_ARG_TYPE_ANY_3 result. + { + auto function = std::make_unique("fn_arg_type_any_3_array_result", + "sample_functions", mode); + const FunctionArgumentType optional( + ARG_TYPE_ANY_3, FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kPositionalOrNamed)); + function->AddSignature({ARG_ARRAY_TYPE_ANY_3, + {optional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_ARRAY_TYPE_ANY_3 arguments + ARG_TYPE_ANY_3 result. + { + auto function = std::make_unique( + "fn_arg_type_array_any_3_result_type_any_3", "sample_functions", mode); + const FunctionArgumentType named_optional( + ARG_ARRAY_TYPE_ANY_3, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kNamedOnly)); + function->AddSignature({ARG_TYPE_ANY_3, + {named_optional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_ARRAY_TYPE_ANY_3 arguments + ARG_ARRAY_TYPE_ANY_3 result. + { + auto function = std::make_unique( + "fn_repeated_array_any_3_return_type_array_any_3", "sample_functions", + mode); + const FunctionArgumentType repeated( + ARG_ARRAY_TYPE_ANY_3, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REPEATED)); + function->AddSignature({ARG_ARRAY_TYPE_ANY_3, + {repeated}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // Arguments and return type are both ARG_TYPE_ANY_4. + { + auto function = std::make_unique("fn_with_arg_type_any_4", + "sample_functions", mode); + const FunctionArgumentType positional(ARG_TYPE_ANY_4); + function->AddSignature({ARG_TYPE_ANY_4, + {positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_TYPE_ANY_4 arguments + ARRAY_ARG_TYPE_ANY_4 result. + { + auto function = std::make_unique("fn_arg_type_any_4_array_result", + "sample_functions", mode); + const FunctionArgumentType optional( + ARG_TYPE_ANY_4, FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kPositionalOrNamed)); + function->AddSignature({ARG_ARRAY_TYPE_ANY_4, + {optional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_ARRAY_TYPE_ANY_4 arguments + ARG_TYPE_ANY_4 result. + { + auto function = std::make_unique( + "fn_arg_type_array_any_4_result_type_any_4", "sample_functions", mode); + const FunctionArgumentType named_optional( + ARG_ARRAY_TYPE_ANY_4, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kNamedOnly)); + function->AddSignature({ARG_TYPE_ANY_4, + {named_optional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_ARRAY_TYPE_ANY_4 arguments + ARG_ARRAY_TYPE_ANY_4 result. + { + auto function = std::make_unique( + "fn_repeated_array_any_4_return_type_array_any_4", "sample_functions", + mode); + const FunctionArgumentType repeated( + ARG_ARRAY_TYPE_ANY_4, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REPEATED)); + function->AddSignature({ARG_ARRAY_TYPE_ANY_4, + {repeated}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // Arguments and return type are both ARG_TYPE_ANY_5. + { + auto function = std::make_unique("fn_with_arg_type_any_5", + "sample_functions", mode); + const FunctionArgumentType positional(ARG_TYPE_ANY_5); + function->AddSignature({ARG_TYPE_ANY_5, + {positional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_TYPE_ANY_5 arguments + ARRAY_ARG_TYPE_ANY_5 result. + { + auto function = std::make_unique("fn_arg_type_any_5_array_result", + "sample_functions", mode); + const FunctionArgumentType optional( + ARG_TYPE_ANY_5, FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kPositionalOrNamed)); + function->AddSignature({ARG_ARRAY_TYPE_ANY_5, + {optional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_ARRAY_TYPE_ANY_5 arguments + ARG_TYPE_ANY_5 result. + { + auto function = std::make_unique( + "fn_arg_type_array_any_5_result_type_any_5", "sample_functions", mode); + const FunctionArgumentType named_optional( + ARG_ARRAY_TYPE_ANY_5, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("o1", kNamedOnly)); + function->AddSignature({ARG_TYPE_ANY_5, + {named_optional}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // ARG_ARRAY_TYPE_ANY_5 arguments + ARG_ARRAY_TYPE_ANY_5 result. + { + auto function = std::make_unique( + "fn_repeated_array_any_5_return_type_array_any_5", "sample_functions", + mode); + const FunctionArgumentType repeated( + ARG_ARRAY_TYPE_ANY_5, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REPEATED)); + function->AddSignature({ARG_ARRAY_TYPE_ANY_5, + {repeated}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // Regular non-aggregate function with argument alias support. + { + auto function = std::make_unique("fn_for_argument_alias", + "sample_functions", mode); + // Signature with alias on an optional argument. + const FunctionArgumentType aliased_1( + types_->get_bool(), + FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + const FunctionArgumentType non_aliased_1(types_->get_bool()); + const FunctionArgumentType optional_arg( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED)); + function->AddSignature({types_->get_bool(), + {aliased_1, non_aliased_1, optional_arg}, + /*context_id=*/-1}); + + // Signature with alias on a repeated argument. + const FunctionArgumentType aliased_2( + types_->get_string(), + FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + const FunctionArgumentType non_aliased_2(types_->get_bool()); + const FunctionArgumentType repeated( + types_->get_int64(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::REPEATED) + .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED)); + function->AddSignature({types_->get_bool(), + {aliased_2, non_aliased_2, repeated}, + /*context_id=*/-1}); + + catalog_->AddOwnedFunction(std::move(function)); + } + // Aggregate function with argument alias support. + { + FunctionArgumentType aliased( + ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + FunctionArgumentType non_aliased(ARG_TYPE_ANY_2); + std::vector function_signatures = { + {types_->get_int64(), {aliased, non_aliased}, /*context_id=*/-1}}; + catalog_->AddOwnedFunction( + new Function("aggregate_fn_for_argument_alias", "sample_functions", + Function::AGGREGATE, function_signatures)); + } + // Analytic functions with argument alias support. + { + FunctionArgumentType aliased( + ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + FunctionArgumentType non_aliased(ARG_TYPE_ANY_2); + std::vector function_signatures = { + {types_->get_int64(), {aliased, non_aliased}, /*context_id=*/-1}}; + catalog_->AddOwnedFunction( + new Function("analytic_fn_for_argument_alias", "sample_functions", + Function::ANALYTIC, function_signatures, + FunctionOptions(FunctionOptions::ORDER_REQUIRED, + /*window_framing_support_in=*/false))); + } + + // Function with argument aliases and ComputeReturnTypeCallback. + { + auto function = std::make_unique( + "fn_to_struct_with_optional_aliases", "sample_functions", mode, + FunctionOptions().set_compute_result_type_callback( + &ComputeResultTypeCallbackToStructUseArgumentAliases)); + // Signature where no argument aliases are allowed. + { + const FunctionArgumentType arg_1(types_->get_bool()); + const FunctionArgumentType arg_2(types_->get_bool()); + function->AddSignature({ARG_TYPE_ARBITRARY, + {arg_1, arg_2}, + /*context_id=*/-1}); + } + // Signature where one of the arguments can have an alias. + { + const FunctionArgumentType arg_1( + types_->get_int64(), + FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + const FunctionArgumentType arg_2(types_->get_int64()); + function->AddSignature({ARG_TYPE_ARBITRARY, + {arg_1, arg_2}, + /*context_id=*/-1}); + } + // Signature where both the arguments can have aliases. + { + const FunctionArgumentType arg_1( + types_->get_string(), + FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + const FunctionArgumentType arg_2( + types_->get_string(), + FunctionArgumentTypeOptions().set_argument_alias_kind( + FunctionEnums::ARGUMENT_ALIASED)); + function->AddSignature({ARG_TYPE_ARBITRARY, + {arg_1, arg_2}, + /*context_id=*/-1}); + } + // Signature with optional arguments. + { + const FunctionArgumentType optional_supports_alias( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("default_string"))); + const FunctionArgumentType optional_not_supports_alias( + types_->get_int64(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int64(100))); + function->AddSignature( + {ARG_TYPE_ARBITRARY, + {optional_supports_alias, optional_not_supports_alias}, + /*context_id=*/-1}); + } + // Signature with repeated arguments. + { + // Use one required positional arguments to avoid ambiguity. + const FunctionArgumentType positional(types_->get_bytes()); + const FunctionArgumentType repeated_supports_alias( + types_->get_int64(), + FunctionArgumentTypeOptions() + .set_argument_alias_kind(FunctionEnums::ARGUMENT_ALIASED) + .set_cardinality(FunctionArgumentType::REPEATED)); + const FunctionArgumentType repeated_not_supports_alias( + types_->get_string(), FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REPEATED)); + function->AddSignature( + {ARG_TYPE_ARBITRARY, + {positional, repeated_supports_alias, repeated_not_supports_alias}, + /*context_id=*/-1}); + } + catalog_->AddOwnedFunction(std::move(function)); + } + // Window function with named lambda. + { + FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions().set_argument_name("named_lambda", + kNamedOnly)); + Function* function = + new Function("afn_named_lambda", "sample_functions", Function::ANALYTIC, + {{ARG_TYPE_ANY_1, + {ARG_TYPE_ANY_1, named_lambda}, + /*context_id=*/-1}}, + FunctionOptions(FunctionOptions::ORDER_REQUIRED, + /*window_framing_support_in=*/false)); + catalog_->AddOwnedFunction(function); + } + // Scalar function with named lambda. + { + FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions().set_argument_name("named_lambda", + kNamedOnly)); + Function* function = + new Function("fn_named_lambda", "sample_functions", Function::SCALAR, + {{ARG_TYPE_ANY_1, + {ARG_TYPE_ANY_1, named_lambda}, + /*context_id=*/-1}}); + catalog_->AddOwnedFunction(function); + } + // Aggregate function with named lambda. + { + FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("named_lambda", kNamedOnly) + .set_is_not_aggregate()); + Function* function = new Function("fn_aggregate_named_lambda", + "sample_functions", Function::AGGREGATE, + {{ARG_TYPE_ANY_1, + {ARG_TYPE_ANY_1, named_lambda}, + /*context_id=*/-1}}); + catalog_->AddOwnedFunction(function); + } + // Function with multiple named arguments, including named lambdas. This + // function can be used to verify that named lambdas can be specified in any + // order. + { + FunctionArgumentType named_1 = FunctionArgumentType( + ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_name( + "named_1", kPositionalOrNamed)); + FunctionArgumentType named_2 = FunctionArgumentType( + ARG_TYPE_ANY_2, FunctionArgumentTypeOptions().set_argument_name( + "named_2", kPositionalOrNamed)); + FunctionArgumentType named_only_lambda = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1, ARG_TYPE_ANY_2}, ARG_TYPE_ANY_3, + FunctionArgumentTypeOptions().set_argument_name("named_lambda", + kPositionalOrNamed)); + std::vector function_signatures = { + {ARG_TYPE_ANY_3, + {named_1, named_2, named_only_lambda}, + /*context_id=*/-1}}; + catalog_->AddOwnedFunction( + new Function("fn_multiple_named_arguments", "sample_functions", + Function::SCALAR, function_signatures)); + } + // Function with multiple named arguments. + { + FunctionArgumentType named_lambda_1 = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions().set_argument_name("named_lambda_1", + kPositionalOrNamed)); + FunctionArgumentType named_lambda_2 = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_3, + FunctionArgumentTypeOptions().set_argument_name("named_lambda_2", + kPositionalOrNamed)); + std::vector function_signatures = { + {ARG_TYPE_ANY_3, + {ARG_TYPE_ANY_1, named_lambda_1, named_lambda_2}, + /*context_id=*/-1}}; + catalog_->AddOwnedFunction( + new Function("fn_multiple_named_lambda_arguments", "sample_functions", + Function::SCALAR, function_signatures)); + } + // Function with signatures having a custom ComputeResultAnnotationsCallback. + { + auto function = std::make_unique("fn_custom_annotation_callback", + "sample_functions", mode); + // Signature with custom annotation callback. + const StructType* struct_type; + ZETASQL_CHECK_OK(type_factory()->MakeStructType( + {{"key", type_factory()->get_string()}, + {"value", type_factory()->get_string()}}, + &struct_type)); + function->AddSignature( + {struct_type, + {types_->get_string(), types_->get_string()}, + /*context_id=*/-1, + FunctionSignatureOptions().set_compute_result_annotations_callback( + &ComputeResultAnnotationsCallbackToStruct)}); + // Signature without custom annotation callback. + function->AddSignature( + {struct_type, + {types_->get_int64(), types_->get_string(), types_->get_string()}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function with array input and a custom ComputeResultAnnotationsCallback. + { + auto function = std::make_unique( + "fn_custom_annotation_callback_array_arg", "sample_functions", mode); + const Type* array_string_type = nullptr; + const Type* result_array_type = nullptr; + { + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_string(), &array_string_type)); + const StructType* struct_type; + ZETASQL_CHECK_OK(type_factory()->MakeStructType( + {{"key", type_factory()->get_string()}, + {"value", type_factory()->get_string()}}, + &struct_type)); + ZETASQL_CHECK_OK(types_->MakeArrayType(struct_type, &result_array_type)); + } + // Signature with custom annotation callback. + function->AddSignature( + {result_array_type, + {array_string_type, array_string_type}, + /*context_id=*/-1, + FunctionSignatureOptions().set_compute_result_annotations_callback( + &ComputeResultAnnotationsCallbackArraysToArrayOfStruct)}); + // Signature without custom annotation callback. + function->AddSignature( + {result_array_type, + {types_->get_int64(), array_string_type, array_string_type}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function that throws an error when input arguments have annotations. + { + auto function = std::make_unique( + "fn_custom_annotation_callback_sql_error", "sample_functions", mode); + function->AddSignature( + {types_->get_string(), + {types_->get_string(), types_->get_string()}, + /*context_id=*/-1, + FunctionSignatureOptions().set_compute_result_annotations_callback( + &ComputeResultAnnotationsCallbackSqlError)}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function with generic argument list with custom annotation callback. + { + auto function = std::make_unique( + "fn_custom_annotation_with_generic_argument_list", "sample_functions", + mode); + FunctionArgumentType lambda = FunctionArgumentType::Lambda( + {types_->get_string()}, types_->get_string()); + function->AddSignature( + {types_->get_string(), + {lambda, types_->get_string(), types_->get_string()}, + /*context_id=*/-1, + FunctionSignatureOptions().set_compute_result_annotations_callback( + &ComputeResultAnnotationsCallbackUseTheFinalAnnotation)}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking an array of any element type and returning the element + // type. + { + auto function = std::make_unique( + "fn_req_any_array_returns_any_arg", "sample_functions", mode); + function->AddSignature({ARG_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking a map type and returning the key arg type + { + auto function = std::make_unique( + "fn_map_type_any_1_2_return_type_any_1", "sample_functions", mode); + function->AddSignature({ARG_TYPE_ANY_1, + {ARG_MAP_TYPE_ANY_1_2}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking a map type and returning the value arg type + { + auto function = std::make_unique( + "fn_map_type_any_1_2_return_type_any_2", "sample_functions", mode); + function->AddSignature({ARG_TYPE_ANY_2, + {ARG_MAP_TYPE_ANY_1_2}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking a map type and optional key arg type, and returning value + // arg type. + // Models MAP_GET(). + { + auto function = std::make_unique( + "fn_req_map_type_any_1_2_req_any_1_opt_any_2_returns_any_2", + "sample_functions", mode); + function->AddSignature({ARG_TYPE_ANY_2, + {ARG_MAP_TYPE_ANY_1_2, + ARG_TYPE_ANY_1, + {ARG_TYPE_ANY_2, FunctionEnums::OPTIONAL}}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking ANY_1 and ANY_2 types, and returning a MAP_TYPE_ANY_1_2 + // arg type. + { + auto function = std::make_unique( + "fn_any_1_any_2_return_map_type_any_1_2", "sample_functions", mode); + function->AddSignature({ARG_MAP_TYPE_ANY_1_2, + {ARG_TYPE_ANY_1, ARG_TYPE_ANY_2}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking a MAP_TYPE_ANY_1_2 arg type, and returning a bool. + { + auto function = std::make_unique( + "fn_map_type_any_1_2_any_2_return_bool", "sample_functions", mode); + function->AddSignature({types_->get_bool(), + {ARG_MAP_TYPE_ANY_1_2, ARG_TYPE_ANY_2}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking a bool, and returning a bool. Requires that V_1_4_MAP_TYPE + // is enabled. + { + auto function = std::make_unique("fn_requires_map_type", + "sample_functions", mode); + function->AddSignature( + {types_->get_bool(), + {types_->get_bool()}, + /*context_id=*/-1, + FunctionSignatureOptions().AddRequiredLanguageFeature( + FEATURE_V_1_4_MAP_TYPE)}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking a bool, and returning a bool. Requires that V_1_4_MAP_TYPE + // is enabled. + { + auto function = std::make_unique( + "fn_requires_map_type_for_bool_signature", "sample_functions", mode); + function->AddSignature( + {types_->get_string(), {types_->get_string()}, /*context_id=*/-1}); + function->AddSignature( + {types_->get_bool(), + {types_->get_bool()}, + /*context_id=*/-1, + FunctionSignatureOptions().AddRequiredLanguageFeature( + FEATURE_V_1_4_MAP_TYPE)}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function with an alternating repeated argument pair. + { + auto function = std::make_unique( + "fn_alternating_repeated_arg_pair", "sample_functions", mode); + function->AddSignature({ARG_MAP_TYPE_ANY_1_2, + {{ARG_TYPE_ANY_1, FunctionEnums::REPEATED}, + {ARG_TYPE_ANY_2, FunctionEnums::REPEATED}}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function with an alternating repeated argument triple. + { + auto function = std::make_unique( + "fn_alternating_repeated_arg_triple", "sample_functions", mode); + function->AddSignature({ARG_MAP_TYPE_ANY_1_2, + {{ARG_TYPE_ANY_1, FunctionEnums::REPEATED}, + {ARG_TYPE_ANY_2, FunctionEnums::REPEATED}, + {ARG_TYPE_ANY_3, FunctionEnums::REPEATED}}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + // Function taking two arrays of key and value element types, and returning a + // map. + { + auto function = std::make_unique( + "fn_array_type_any_1_and_array_type_any_2_return_map_type_any_1_2", + "sample_functions", mode); + function->AddSignature({ARG_MAP_TYPE_ANY_1_2, + {ARG_ARRAY_TYPE_ANY_1, ARG_ARRAY_TYPE_ANY_2}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); + } + + // Do not add more to this function. Instead, use RegisterForSampleCatalog + // inside an unnamed namespace. Context: Under some compilation modes all + // local variables get their own stack location and this causes the stack + // limit to be exceeded. +} // NOLINT(readability/fn_size) + +void SampleCatalogImpl::LoadFunctionsWithDefaultArguments() { + // Adds an scalar function that takes multiple optional named arguments with + // some of them having default values. + Function* function = new Function( + "fn_optional_named_default_args", "sample_functions", Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("a0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("o1_default"))}, + {types_->get_double(), + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Double(0.2))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + + // Similar to the one above, but the optional arguments are templated. + function = new Function( + "fn_optional_named_default_args_templated", "sample_functions", + Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("a0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("o0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("o1_default"))}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int32(2))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + + // Similar to the one above, but the default argument value is -2 rather than + // 2. + function = new Function( + "fn_optional_named_default_args_templated_negative", "sample_functions", + Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("a0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("o0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("o1_default"))}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int32(-2))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + + // Adds an scalar function that takes multiple unnamed optional arguments with + // some of them having default values. + function = new Function( + "fn_optional_unnamed_default_args", "sample_functions", Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("o1_default"))}, + {types_->get_double(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Double(0.3))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + + // Adds an scalar function that takes (concrete typed, templated type). See + // b/333445090. + function = new Function( + "fn_optional_unnamed_default_any_args", "sample_functions", + Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("abc"))}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("def"))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + + // Similar to the one above, but the optional arguments are templated. + function = new Function( + "fn_optional_unnamed_default_args_templated", "sample_functions", + Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("o1_default"))}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int32(5))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + + // A scalar function with both named and unnamed optional arguments plus the + // last argument with a default value. + function = new Function( + "fn_req_opt_unnamed_named_default", "sample_functions", Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("dv"))}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + ZETASQL_CHECK_OK(function->signatures()[0].IsValid(ProductMode::PRODUCT_EXTERNAL)); + catalog_->AddOwnedFunction(function); + + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_optional_named_default_args"}, + FunctionSignature( + /*result_type=*/ARG_TYPE_RELATION, + /*arguments=*/ + { + {ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("relation", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {types_->get_bool(), + FunctionArgumentTypeOptions() + .set_argument_name("r1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {types_->get_double(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Double(3.14))}, + {types_->get_uint32(), + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Uint32(10086))}, + }, + /*context_id=*/-1))); + + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_optional_named_default_args_templated"}, + FunctionSignature( + /*result_type=*/ARG_TYPE_RELATION, + /*arguments=*/ + { + {ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("relation", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {types_->get_bool(), + FunctionArgumentTypeOptions() + .set_argument_name("r1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::REQUIRED)}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("o0", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Double(3.14))}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("abc"))}, + }, + /*context_id=*/-1))); + + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_optional_unnamed_default_args"}, + FunctionSignature( + /*result_type=*/ARG_TYPE_RELATION, + /*arguments=*/ + { + {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_bool(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {types_->get_float(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Float(0.618))}, + {types_->get_uint32(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Uint32(168))}, + }, + /*context_id=*/-1))); + + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_optional_unnamed_default_args_templated"}, + FunctionSignature( + /*result_type=*/ARG_TYPE_RELATION, + /*arguments=*/ + { + {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_bool(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::String("xyz"))}, + {ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int32(-1))}, + }, + /*context_id=*/-1))); + + // A TVF with a combined of named and unnamed optional arguments. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_optional_unnamed_named"}, + FunctionSignature( + /*result_type=*/ARG_TYPE_RELATION, + /*arguments=*/ + { + {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_bool(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + }, + /*context_id=*/-1))); + + // A TVF with a combined of named and unnamed optional arguments plus a + // default argument at last. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_optional_unnamed_named_default"}, + FunctionSignature( + /*result_type=*/ARG_TYPE_RELATION, + /*arguments=*/ + { + {ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_bool(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + {types_->get_int32(), + FunctionArgumentTypeOptions() + .set_argument_name("o2", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int32(314))}, + }, + /*context_id=*/-1))); + + // A scalar function with both unnamed and named optional arguments. + function = new Function( + "fn_req_opt_unnamed_named", "sample_functions", Function::SCALAR, + /*function_signatures=*/ + { + {/*result_type=*/types_->get_int64(), + /*arguments=*/ + { + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::REQUIRED)}, + {types_->get_string(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)}, + {types_->get_string(), + FunctionArgumentTypeOptions() + .set_argument_name("o1", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)}, + }, + /*context_id=*/-1}, + }, + FunctionOptions()); + catalog_->AddOwnedFunction(function); + ZETASQL_CHECK_OK(function->signatures()[0].IsValid(ProductMode::PRODUCT_EXTERNAL)); + + // A scalar function with one optional argument of any type, returning that + // type. + function = new Function("fn_optional_any_arg_returns_any", "sample_functions", + Function::SCALAR, + /*function_signatures=*/ + {{ARG_TYPE_ANY_1, + {{ARG_TYPE_ANY_1, FunctionEnums::OPTIONAL}}, + /*context_id=*/-1}}, + FunctionOptions()); + catalog_->AddOwnedFunction(std::move(function)); + + // A scalar function with one optional argument of any type, returning an + // array with that element type. + function = new Function("fn_optional_any_arg_returns_any_array", + "sample_functions", Function::SCALAR, + /*function_signatures=*/ + {{ARG_ARRAY_TYPE_ANY_1, + {{ARG_TYPE_ANY_1, FunctionEnums::OPTIONAL}}, + /*context_id=*/-1}}, + FunctionOptions()); + catalog_->AddOwnedFunction(std::move(function)); + + // A scalar function with a required array of any type and an optional of any + // type, returning the element type. + function = new Function( + "fn_req_any_array_optional_any_arg_returns_any_arg", "sample_functions", + Function::SCALAR, + /*function_signatures=*/ + {{ARG_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, {ARG_TYPE_ANY_1, FunctionEnums::OPTIONAL}}, + /*context_id=*/-1}}, + FunctionOptions()); + catalog_->AddOwnedFunction(std::move(function)); +} + +void SampleCatalogImpl::LoadTemplatedSQLUDFs() { + // Return an empty struct as the result type for now. + // The function resolver will dynamically compute a different result type at + // analysis time based on the function SQL body. + const FunctionArgumentType result_type(ARG_TYPE_ARBITRARY); + int context_id = 0; + + // Add a UDF with a simple valid templated SQL body. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_one"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, ParseResumeLocation::FromString("1"))); + + // Add a templated SQL function that calls another templated SQL function. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_call_udf_templated_return_one"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, + ParseResumeLocation::FromString("udf_templated_return_one()"))); + + // Add a templated SQL function that calls another templated SQL function + // twice. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_call_udf_templated_return_one_twice"}, + FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, + ParseResumeLocation::FromString("udf_call_udf_templated_return_one() + " + "udf_call_udf_templated_return_one()"))); + + // Add a UDF with a valid templated SQL body that refers to an argument. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_bool_arg"}, + FunctionSignature(result_type, {FunctionArgumentType(types::BoolType())}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a valid templated SQL body that refers to an argument. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_int64_arg"}, + FunctionSignature(result_type, {FunctionArgumentType(types::Int64Type())}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a valid templated SQL body that refers to an argument. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_any_scalar_arg"}, + FunctionSignature(result_type, {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a valid templated SQL body that performs addition on an + // argument. The function signature accepts a single argument of any type. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_arg_plus_integer"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x + 42"))); + + // Add a UDF with a valid templated SQL body that accepts an input argument + // where the name contains '$'. The function signature accepts a single + // argument of any type. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_arg_plus_integer_accept_dollars_col_name"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"$col1"}, + ParseResumeLocation::FromString("`$col1`"))); + + // Add a UDF with a valid templated SQL body that performs concatenation on an + // argument. The function signature accepts a single argument of any type. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_arg_concat_string"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("concat(x, 'abc')"))); + + // Add a UDF with a valid templated SQL body that performs concatenation on + // two arguments. The function signature accepts two arguments of any type. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_arg_concat_two_strings"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x", "y"}, + ParseResumeLocation::FromString("concat(x, y)"))); + + // Add a UDF with a valid templated SQL body that performs a proto field + // access on an argument. The function signature accepts a single argument of + // any type. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_arg_proto_field_access"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("x.int32_field"))); + + // Add an invalid templated SQL function with a parse error in the function + // body. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_parse_error"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, ParseResumeLocation::FromString("a b c d e"))); + + // Add an invalid templated SQL function with an analysis error in the + // function body. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_analysis_error"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, ParseResumeLocation::FromString("'abc' + 42"))); + + // Add a UDF that refers to 'udf_templated_analysis_error' to show two levels + // of nested error messages. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_call_udf_templated_analysis_error"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, + ParseResumeLocation::FromString("udf_templated_analysis_error() + 1"))); + + // Add a UDF that refers to 'udf_call_udf_templated_analysis_error' to show + // three levels of nested error messages. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_call_udf_call_udf_templated_analysis_error"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, + ParseResumeLocation::FromString( + "udf_call_udf_templated_analysis_error() + 1"))); + + // Add a UDF that refers to 'udf_call_udf_call_udf_templated_analysis_error' + // to show four levels of nested error messages. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_call_udf_call_udf_call_udf_templated_analysis_error"}, + FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, + ParseResumeLocation::FromString( + "udf_call_udf_call_udf_templated_analysis_error() + 1"))); + + // Add an invalid templated SQL function that attempts to refer to a query + // parameter. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_function_body_refer_to_parameter"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, + ParseResumeLocation::FromString("@test_param_bool"))); + + // Add an invalid templated SQL function where the function body is empty. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_function_body_empty"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, ParseResumeLocation::FromString(""))); + + // Add an invalid templated SQL function that directly calls itself. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_recursive"}, FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, + ParseResumeLocation::FromString("udf_recursive()"))); + + // Add two invalid templated SQL functions that indirectly call themselves + // through one or more other templated SQL function calls. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_calls_self_indirectly_1"}, + FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, + ParseResumeLocation::FromString("udf_calls_self_indirectly_2()"))); + + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_calls_self_indirectly_2"}, + FunctionSignature(result_type, {}, context_id++), + /*argument_names=*/{}, + ParseResumeLocation::FromString("udf_calls_self_indirectly_1()"))); + + // Add a templated SQL function that calls a templated SQL TVF that calls the + // original templated SQL function again, to make sure cycle detection works. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_calls_tvf_calls_same_udf"}, + FunctionSignature(result_type, {}, context_id++), /*argument_names=*/{}, + ParseResumeLocation::FromString( + "(select * from tvf_calls_udf_calls_same_tvf())"))); + + // Add a templated SQL function with duplicate argument names. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_duplicate_arg_names"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x", "x"}, + ParseResumeLocation::FromString("concat(x, 'abc')"))); + + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_like_any"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("x like any ('z')"))); + + // Add a templated SQL function that triggers a deprecation warning. + FunctionSignature deprecation_warning_signature(result_type, /*arguments=*/{}, + /*context_id=*/-1); + deprecation_warning_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/101)}); + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_calls_deprecation_warning"}, deprecation_warning_signature, + /*argument_names=*/{}, + ParseResumeLocation::FromString("deprecation_warning()"))); + + // Add a UDF with a simple valid templated SQL body that returns its one input + // argument, and has an expected result type of a 32-bit integer. We will use + // this to test comparing the result type against the expected type when + // testing templated SQL function calls. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_one_templated_arg_return_int32"}, + FunctionSignature(types::Int32Type(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a simple valid templated SQL body that returns its one input + // argument, and has an expected result type of a 64-bit integer. We will use + // this to test comparing the result type against the expected type when + // testing templated SQL function calls. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_one_templated_arg_return_int64"}, + FunctionSignature(types::Int64Type(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a simple valid templated SQL body that returns its one input + // argument, and has an expected result type of a date. We will use this to + // test comparing the result type against the expected type when testing + // templated SQL function calls. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_one_templated_arg_return_date"}, + FunctionSignature(types::DateType(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a simple valid templated SQL body that returns its one input + // argument, and has an expected result type of a struct. We will use + // this to test comparing the result type against the expected type when + // testing templated SQL function calls. + const StructType* return_type = nullptr; + std::vector fields; + fields.emplace_back("int_field", types::Int64Type()); + fields.emplace_back("string_field", types::StringType()); + ZETASQL_CHECK_OK(type_factory()->MakeStructType(fields, &return_type)); + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_one_templated_arg_return_struct_int64_string"}, + FunctionSignature(return_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("x"))); + + // Add a UDF with a simple valid templated SQL body that returns a constant + // integer, and has an expected result type of a string. We will use + // this to test comparing the result type against the expected type when + // testing templated SQL function calls. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_42_return_type_string"}, + FunctionSignature(types::StringType(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("42"))); + + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_42_return_type_int32"}, + FunctionSignature(types::Int32Type(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("42"))); + + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_return_999999999999999_return_type_int32"}, + FunctionSignature(types::Int32Type(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("999999999999999"))); + + catalog_->AddOwnedFunction(std::make_unique( + std::vector{"udf_any_and_string_args_return_string_arg"}, + FunctionSignature(FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(types::StringType(), + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/std::vector{"a", "x"}, + ParseResumeLocation::FromString("x"))); + + catalog_->AddOwnedFunction(std::make_unique( + std::vector{"udf_any_and_double_args_return_any"}, + FunctionSignature(FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(types::DoubleType(), + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/std::vector{"a", "x"}, + ParseResumeLocation::FromString("IF(x < 0, 'a', 'b')"))); + + const ArrayType* double_array_type = nullptr; + ZETASQL_CHECK_OK( + type_factory()->MakeArrayType(types::DoubleType(), &double_array_type)); + catalog_->AddOwnedFunction(std::make_unique( + std::vector{"udf_any_and_double_array_args_return_any"}, + FunctionSignature(FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(double_array_type, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/std::vector{"a", "x"}, + ParseResumeLocation::FromString("IF(x[SAFE_OFFSET(0)] < 0, 'a', 'b')"))); + + // Add a SQL UDA with a valid templated SQL body that refers to an aggregate + // argument only. + FunctionArgumentType int64_aggregate_arg_type(types::Int64Type()); + FunctionArgumentTypeOptions agg_options; + agg_options.set_is_not_aggregate(); + FunctionArgumentType int64_not_aggregate_arg_type(types::Int64Type(), + agg_options); + + FunctionOptions fn_opts_allow_null_handling; + fn_opts_allow_null_handling.set_supports_null_handling_modifier(true); + + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_valid_templated_return_sum_int64_arg"}, + FunctionSignature(result_type, {int64_aggregate_arg_type}, context_id++), + /*argument_names=*/{"x"}, ParseResumeLocation::FromString("sum(x)"), + Function::AGGREGATE, fn_opts_allow_null_handling)); + + // Add a SQL UDA with a valid templated SQL body that refers to a NOT + // AGGREGATE argument only. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_valid_templated_return_int64_not_aggregate_arg"}, + FunctionSignature(result_type, {int64_not_aggregate_arg_type}, + context_id++), + /*argument_names=*/{"y"}, ParseResumeLocation::FromString("y"), + Function::AGGREGATE)); + + // Add a SQL UDA with a valid templated SQL body that refers to an AGGREGATE + // argument and also a NOT AGGREGATE argument in the same script. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_valid_templated_return_int64_aggregate_and_not_aggregate_arg"}, + FunctionSignature( + result_type, {int64_aggregate_arg_type, int64_not_aggregate_arg_type}, + context_id++), + /*argument_names=*/{"x", "y"}, + ParseResumeLocation::FromString("sum(x) + y"), Function::AGGREGATE)); + + // Add a SQL UDA with an invalid templated SQL body that refers to an + // AGGREGATE argument and also a NOT AGGREGATE argument in the same script. + // The function attempts to refer to the AGGREGATE argument outside of an + // aggregate function, which is invalid. Note that NOT AGGREGATE arguments can + // still be aggregated. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_invalid_templated_return_int64_aggregate_and_not_aggregate_arg"}, + FunctionSignature( + result_type, {int64_aggregate_arg_type, int64_not_aggregate_arg_type}, + context_id++), + /*argument_names=*/{"x", "y"}, + ParseResumeLocation::FromString("sum(y) + x"), Function::AGGREGATE)); + + // Add a SQL UDA with an invalid templated SQL body, since there is an ORDER + // BY clause in an aggregate function nested inside. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_invalid_templated_return_sum_int64_arg_nested_order_by"}, + FunctionSignature(result_type, {int64_aggregate_arg_type}, context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("sum(x order by x)"), + Function::AGGREGATE)); + + FunctionArgumentTypeOptions required_non_agg_options = + FunctionArgumentTypeOptions(FunctionArgumentType::REQUIRED) + .set_is_not_aggregate(true); + catalog_->AddOwnedFunction(std::make_unique( + std::vector{"uda_any_and_string_args_return_string"}, + FunctionSignature( + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(types::StringType(), required_non_agg_options)}, + context_id++), + /*argument_names=*/std::vector{"a", "x"}, + ParseResumeLocation::FromString("IF(LOGICAL_OR(a), x, x || '_suffix')"), + Function::AGGREGATE)); + + catalog_->AddOwnedFunction(std::make_unique( + std::vector{"uda_any_and_double_args_return_any"}, + FunctionSignature( + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, required_non_agg_options), + FunctionArgumentType(types::DoubleType(), + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/std::vector{"a", "x"}, + ParseResumeLocation::FromString("STRING_AGG(IF(x < 0, 'a', 'b'))"), + Function::AGGREGATE)); + + ZETASQL_CHECK_OK( + type_factory()->MakeArrayType(types::DoubleType(), &double_array_type)); + catalog_->AddOwnedFunction(std::make_unique( + std::vector{"uda_any_and_double_array_args_return_any"}, + FunctionSignature( + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED), + FunctionArgumentType(double_array_type, required_non_agg_options)}, + context_id++), + /*argument_names=*/std::vector{"a", "x"}, + ParseResumeLocation::FromString( + "IF(x[SAFE_OFFSET(0)] < 0, MAX(a), MIN(a))"), + Function::AGGREGATE)); + + // This function template cannot be invoked because the UDA does not have + // type information for the `GROUP_ROWS()` TVF. We added it here to reproduce + // unhelpful error messages. + // See discussion in: b/285159284. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"WrappedGroupRows"}, + FunctionSignature(result_type, {ARG_TYPE_ARBITRARY}, context_id++), + /*argument_names=*/{"e"}, + ParseResumeLocation::FromString( + R"sql( + SUM(e) WITH GROUP ROWS(SELECT e + FROM GROUP_ROWS() + WHERE e IS NOT NULL))sql"), + Function::AGGREGATE)); + + // Add a SQL UDA with a valid templated SQL body with two NOT AGGREGATE + // arguments having default values. + FunctionArgumentType int64_default_not_aggregate_arg_type( + types::Int64Type(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_is_not_aggregate() + .set_default(values::Int64(0)) + .set_argument_name("delta", kPositionalOrNamed)); + FunctionArgumentType bool_default_not_aggregate_arg_type( + types::BoolType(), + FunctionArgumentTypeOptions(FunctionArgumentType::OPTIONAL) + .set_is_not_aggregate() + .set_default(values::Bool(false)) + .set_argument_name("allow_nulls", kPositionalOrNamed)); + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_templated_two_not_aggregate_args"}, + FunctionSignature(result_type, + {result_type, int64_default_not_aggregate_arg_type, + bool_default_not_aggregate_arg_type}, + context_id++), + /*argument_names=*/{"cval", "delta", "allow_nulls"}, + ParseResumeLocation::FromString(R"( + IF(COUNT(1) = COUNT(DISTINCT cval) + delta OR + (allow_nulls AND COUNTIF(cval IS NOT NULL) = COUNT(DISTINCT cval)), + NULL, "NOT NULL"))"), + Function::AGGREGATE)); + + // Add a templated (scalar) SQL function with definer rights + auto templated_scalar_definer_rights_function = + std::make_unique( + std::vector{"templated_scalar_definer_rights"}, + FunctionSignature( + result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/std::vector{"x"}, + ParseResumeLocation::FromString( + "x + (select count(*) from KeyValue)")); + templated_scalar_definer_rights_function->set_sql_security( + ResolvedCreateStatementEnums::SQL_SECURITY_DEFINER); + catalog_->AddOwnedFunction( + std::move(templated_scalar_definer_rights_function)); + + // Add a templated UDF whose function body returns collated value by calling + // COLLATE function. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_collate_function"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("collate(x, 'und:ci')"))); + + // Add a templated UDF whose function body returns collated array value by + // using COLLATE function result as an array element. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_collate_function_in_array"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("[collate(x, 'und:ci')]"))); + + // Add a templated UDF whose function body returns collated struct value by + // using COLLATE function result as a struct field. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_collate_function_in_struct"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("(collate(x, 'und:ci'), 1)"))); + + // Add a templated UDF whose function body calls COLLATE function but has an + // explicit STRING return type without collation. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"udf_templated_collate_function_return_string"}, + FunctionSignature(types::StringType(), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("collate(x, 'und:ci')"))); + + // Add a templated SQL UDA which calls MIN aggregate function with COLLATE + // function inside. + catalog_->AddOwnedFunction(new TemplatedSQLFunction( + {"uda_templated_aggregate_with_min_collate_function"}, + FunctionSignature(result_type, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*argument_names=*/{"x"}, + ParseResumeLocation::FromString("min(collate(x, 'und:ci'))"), + Function::AGGREGATE)); +} + +namespace { +const char kColumnNameBool[] = "column_bool"; +const char kColumnNameBytes[] = "column_bytes"; +const char kColumnNameDate[] = "column_date"; +const char kColumnNameDouble[] = "column_double"; +const char kColumnNameFloat[] = "column_float"; +const char kColumnNameInt32[] = "column_int32"; +const char kColumnNameInt64[] = "column_int64"; +const char kColumnNameString[] = "column_string"; +const char kColumnNameTime[] = "column_time"; +const char kColumnNameUInt32[] = "column_uint32"; +const char kColumnNameUInt64[] = "column_uint64"; +const char kColumnNameKey[] = "key"; +const char kColumnNameValue[] = "value"; +const char kColumnNameFilename[] = "filename"; + +const char kTypeBool[] = "bool"; +const char kTypeBytes[] = "bytes"; +const char kTypeDate[] = "date"; +const char kTypeDouble[] = "double"; +const char kTypeFloat[] = "float"; +const char kTypeInt32[] = "int32"; +const char kTypeInt64[] = "int64"; +const char kTypeString[] = "string"; +const char kTypeTime[] = "time"; +const char kTypeUInt32[] = "uint32"; +const char kTypeUInt64[] = "uint64"; +const char kTypeInterval[] = "interval"; + +struct OutputColumn { + std::string description; + std::string name; + const Type* type; +}; + +struct TVFArgument { + std::string description; + const Type* type; +}; + +} // namespace + +static std::vector GetOutputColumnsForAllTypes( + TypeFactory* types) { + return {{kTypeBool, kColumnNameBool, types->get_bool()}, + {kTypeBytes, kColumnNameBytes, types->get_bytes()}, + {kTypeDate, kColumnNameDate, types->get_date()}, + {kTypeDouble, kColumnNameDouble, types->get_double()}, + {kTypeFloat, kColumnNameFloat, types->get_float()}, + {kTypeInt32, kColumnNameInt32, types->get_int32()}, + {kTypeInt64, kColumnNameInt64, types->get_int64()}, + {kTypeString, kColumnNameString, types->get_string()}, + {kTypeTime, kColumnNameTime, types->get_time()}, + {kTypeUInt32, kColumnNameUInt32, types->get_uint32()}, + {kTypeUInt64, kColumnNameUInt64, types->get_uint64()}}; +} + +static std::vector GetTVFArgumentsForAllTypes(TypeFactory* types) { + return {{kTypeBool, types->get_bool()}, + {kTypeBytes, types->get_bytes()}, + {kTypeDate, types->get_date()}, + {kTypeDouble, types->get_double()}, + {kTypeFloat, types->get_float()}, + {kTypeInt32, types->get_int32()}, + {kTypeInt64, types->get_int64()}, + {kTypeString, types->get_string()}, + {kTypeTime, types->get_time()}, + {kTypeUInt32, types->get_uint32()}, + {kTypeUInt64, types->get_uint64()}, + {kTypeInterval, types->get_interval()}}; +} + +static TVFRelation GetOutputSchemaWithTwoTypes( + absl::Span output_columns_for_all_types) { + TVFRelation::ColumnList columns; + for (int i = 0; i < 2; ++i) { + columns.emplace_back(output_columns_for_all_types[i].name, + output_columns_for_all_types[i].type); + } + return TVFRelation(columns); +} + +void SampleCatalogImpl::LoadTableValuedFunctions1() { + TVFRelation empty_output_schema({}); + + const std::vector kOutputColumnsAllTypes = + GetOutputColumnsForAllTypes(types_); + + const std::vector kArgumentsAllTypes = + GetTVFArgumentsForAllTypes(types_); + + TVFRelation output_schema_two_types = + GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); + + // Generate an output schema that returns an int64_t value table. + TVFRelation output_schema_int64_value_table = + TVFRelation::ValueTable(types_->get_int64()); + + // Generate an output schema that returns a proto value table. + TVFRelation output_schema_proto_value_table = TVFRelation::ValueTable( + GetProtoType(zetasql_test__::TestExtraPB::descriptor())); + + // Generate an output schema that returns every possible type. + TVFRelation::ColumnList columns; + columns.reserve(kOutputColumnsAllTypes.size()); + for (const auto& kv : kOutputColumnsAllTypes) { + columns.emplace_back(kv.name, kv.type); + } + TVFRelation output_schema_all_types(columns); + + // Add a TVF that takes no arguments. + int context_id = 0; + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + output_schema_two_types)); + + // Add a TVF that returns an empty output schema. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_empty_output_schema"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + empty_output_schema, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + empty_output_schema)); + + // Add a TVF that takes no arguments and returns all POD types. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args_return_all_pod_types"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_all_types, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + output_schema_all_types)); + + // Add a TVF for each POD type that accepts exactly one argument of that type. + for (const auto& kv : kArgumentsAllTypes) { + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {absl::StrCat("tvf_exactly_1_", kv.description, "_arg")}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(kv.type)}, context_id++), + output_schema_two_types)); + } + + // Add TVFs that accept between two and nine INT64 arguments. + for (int i = 2; i < 10; ++i) { + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {absl::StrCat("tvf_exactly_", i, "_int64_args")}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(i, types::Int64Type()), + context_id++), + output_schema_two_types)); + } + + // For each templated argument type, add a TVF that accepts exactly one + // argument of that type. + const std::vector> + kSignatureArgumentKinds = { + {"arg_type_any_1", ARG_TYPE_ANY_1}, + {"arg_type_any_2", ARG_TYPE_ANY_2}, + {"arg_array_type_any_1", ARG_ARRAY_TYPE_ANY_1}, + {"arg_array_type_any_2", ARG_ARRAY_TYPE_ANY_2}, + {"arg_proto_any", ARG_PROTO_ANY}, + {"arg_struct_any", ARG_STRUCT_ANY}, + {"arg_enum_any", ARG_ENUM_ANY}, + {"arg_type_relation", ARG_TYPE_RELATION}, + {"arg_type_arbitrary", ARG_TYPE_ARBITRARY}, + }; + for (const std::pair& kv : + kSignatureArgumentKinds) { + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {absl::StrCat("tvf_exactly_one_", kv.first)}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(kv.second)}, context_id++), + output_schema_two_types)); + } + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_exactly_one_proto"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(proto_KitchenSinkPB_)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a repeating final argument of type int64_t. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_repeating_int64_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(types::Int64Type(), + FunctionArgumentType::REPEATED)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a repeating final argument of ARG_TYPE_ANY_1. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_repeating_any_one_type_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ANY_1, + FunctionArgumentType::REPEATED)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a repeating final argument of ARG_TYPE_ARBITRARY. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_repeating_arbitrary_type_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REPEATED)}, + context_id++), + output_schema_two_types)); + + // Stop here to avoid hitting lint limits for function length. +} + +void SampleCatalogImpl::LoadTableValuedFunctions2() { + TVFRelation empty_output_schema({}); + + const std::vector kOutputColumnsAllTypes = + GetOutputColumnsForAllTypes(types_); + + TVFRelation output_schema_two_types = + GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); + + // Generate an output schema that returns an int64_t value table. + TVFRelation output_schema_int64_value_table = + TVFRelation::ValueTable(types_->get_int64()); + + // Generate an output schema that returns a proto value table. + TVFRelation output_schema_proto_value_table = TVFRelation::ValueTable( + GetProtoType(zetasql_test__::TestExtraPB::descriptor())); + + TVFRelation output_schema_two_types_with_pseudo_columns( + {TVFSchemaColumn(kOutputColumnsAllTypes[0].name, + kOutputColumnsAllTypes[0].type), + TVFSchemaColumn(kOutputColumnsAllTypes[1].name, + kOutputColumnsAllTypes[1].type), + TVFSchemaColumn("RowId", types_->get_int64(), + /*is_pseudo_column_in=*/true), + TVFSchemaColumn("PartitionName", types_->get_string(), + /*is_pseudo_column_in=*/true)}); + + // Generate an output schema that returns an int64_t value table. + TVFRelation output_schema_value_table_with_pseudo_columns = + TVFRelation::ValueTable( + GetProtoType(zetasql_test__::TestExtraPB::descriptor()), + {TVFSchemaColumn("RowId", types_->get_int64(), + /*is_pseudo_column_in=*/true), + TVFSchemaColumn("PartitionName", types_->get_string(), + /*is_pseudo_column_in=*/true)}) + .value(); + + int64_t context_id = 0; + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_arg_returning_fixed_output_with_pseudo_columns"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types_with_pseudo_columns, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + output_schema_two_types_with_pseudo_columns)); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_arg_returning_value_table_with_pseudo_columns"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_value_table_with_pseudo_columns, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + output_schema_value_table_with_pseudo_columns)); + + // Add a TVF with exactly one relation argument. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_with_fixed_output"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation()}, context_id++), + output_schema_two_types)); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_models_with_fixed_output"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{"label", types::DoubleType()}}), + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyModel(), FunctionArgumentType::AnyModel()}, + context_id++), + TVFRelation({{"label", types::DoubleType()}}))); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_one_model_arg_with_fixed_output"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeDouble, types::DoubleType()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/false), + { + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}, + {kColumnNameValue, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentType::AnyModel(), + }, + context_id++), + TVFRelation({{kTypeDouble, types::DoubleType()}, + {kTypeString, types::StringType()}}))); + + // Add a TVF with exactly one relation argument. The output schema is set to + // be the same as the input schema. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_one_relation_arg_output_schema_is_input_schema"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation()}, context_id++))); + + // Add a TVF with exactly one optional relation argument. The output schema is + // set to be the same as the input schema. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_optional_relation_arg_return_int64_value_table"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_int64_value_table, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL)}, + context_id++), + output_schema_int64_value_table)); + + // Add a TVF with one relation argument and one integer argument. The output + // schema is set to be the same as the input schema of the relation argument. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_one_relation_arg_output_schema_is_input_schema_plus_int64_arg"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType(types::Int64Type())}, + context_id++))); + + // Add one TVF with two relation arguments that forwards the schema of the + // first relation argument to the output of the TVF. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_two_relation_args_output_schema_is_input_schema"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType::AnyRelation()}, + context_id++))); + + // Add one TVF with two relation arguments with the second one optional that + // forwards the schema of the first relation argument to the output of the + // TVF. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_two_relation_args_second_optional_output_schema_is_input_schema"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL)}, + context_id++))); + + // Add one TVF with three arguments: The first one is required model; The + // second is optional table; The third is optional struct. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_model_evaluation_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyModel(), + FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType(ARG_STRUCT_ANY, + FunctionArgumentType::OPTIONAL)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly two relation arguments. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_relation_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType::AnyRelation()}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly two relation arguments that returns an int64_t value + // table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_relation_args_return_int64_value_table"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_int64_value_table, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType::AnyRelation()}, + context_id++), + output_schema_int64_value_table)); + + // Add a TVF with exactly two relation arguments that returns a proto value + // table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_relation_args_return_proto_value_table"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_proto_value_table, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType::AnyRelation()}, + context_id++), + output_schema_proto_value_table)); + + // Add a TVF with exactly one argument of ARG_TYPE_RELATION and another + // argument of type int64_t. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_int64_arg_one_relation_arg"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(types::Int64Type()), + FunctionArgumentType::AnyRelation()}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one argument of ARG_TYPE_RELATION and another + // argument of type int64_t. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_one_int64_arg"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType(types::Int64Type())}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument and repeating int64_t arguments. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_repeating_int64_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType(types::Int64Type(), + FunctionArgumentType::REPEATED)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with one relation argument and also a repeating final argument of + // ARG_TYPE_ANY_1. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_repeating_any_one_type_args"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType(ARG_TYPE_ANY_1, + FunctionArgumentType::REPEATED)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column and one string column. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_int64_string_input_columns"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeInt64, types::Int64Type()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column and one string column, and no extra columns are allowed + // in the input relation. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_only_int64_string_input_columns"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeInt64, types::Int64Type()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/false)}, + context_id++), + output_schema_two_types)); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_only_int64_struct_int64_input_columns"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeInt64, types::Int64Type()}, + {"struct_int64", struct_with_one_field_type_}}), + /*extra_relation_input_columns_allowed=*/false)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with two relation arguments, one with a required input schema of + // one uint64_t column and one string column, and the other with a required + // input schema of one date column and one string column. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_relation_args_uint64_string_and_date_string_input_columns"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeUInt64, types::Uint64Type()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/true), + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeDate, types::DateType()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF one relation argument with a required input schema of many + // supported types. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_required_input_schema_many_types"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeBool, types_->get_bool()}, + {kTypeBytes, types_->get_bytes()}, + {kTypeDate, types_->get_date()}, + {kTypeDouble, types_->get_double()}, + {kTypeFloat, types_->get_float()}, + {kTypeInt32, types_->get_int32()}, + {kTypeInt64, types_->get_int64()}, + {kTypeString, types_->get_string()}, + {kTypeTime, types_->get_timestamp()}, + {kTypeUInt32, types_->get_uint32()}, + {kTypeUInt64, types_->get_uint64()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + const std::string kMyEnum = "myenum"; + const std::string kMyDate = "mydate"; + const std::string kInt64a = "int64a"; + const std::string kInt64b = "int64b"; + const std::string kInt64c = "int64c"; + + // Add a TVF with exactly one relation argument with a required input schema + // of one enum column. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_one_enum_input_column"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column( + kMyEnum, + GetEnumType(zetasql_test__::TestEnum_descriptor()))}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one date column. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_one_date_input_column"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kMyDate, types_->get_date())}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of three int64_t columns. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_three_int64_input_columns"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64()), + TVFRelation::Column(kInt64b, types_->get_int64()), + TVFRelation::Column(kInt64c, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one proto column value table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_input_proto_value_table"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation::ValueTable(GetProtoType( + zetasql_test__::TestExtraPB::descriptor())), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column value table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_int64_input_value_table"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation::ValueTable(types::Int64Type()), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column value table, and forwards the input schema to the + // output schema. + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_one_relation_arg_int64_input_value_table_forward_schema"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::RelationWithSchema( + TVFRelation::ValueTable(types::Int64Type()), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++))); + + // Add a TVF with exactly one relation argument with a fixed schema that + // returns a proto value table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_return_proto_value_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_proto_value_table, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation( + {TVFRelation::Column(kTypeString, types_->get_string()), + TVFRelation::Column(kTypeInt64, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_proto_value_table)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column, and extra input columns are allowed. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_key_input_column_extra_input_columns_allowed"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one string column, and extra input columns are allowed. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_filename_input_column_extra_input_columns_allowed"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameFilename, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column, and extra input columns are not allowed. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_key_input_column_extra_input_columns_banned"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/false)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one column whose name is "uint32" but whose type is actually uint64_t. + // Then it is possible to call this TVF with the SimpleTypes table and type + // coercion should coerce the provided column named "uint32" to type uint64_t. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_uint64_input_column_named_uint32"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeUInt32, types::Uint64Type()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with exactly one relation argument with a required input schema + // of one int64_t column and one string column. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_key_filename_input_columns"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}, + {kColumnNameFilename, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF that takes two scalar named arguments. + const auto named_required_format_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( + "format_string", kPositionalOrNamed)); + const auto named_required_date_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( + "date_string", kPositionalOrNamed)); + const auto named_required_any_relation_arg = FunctionArgumentType( + ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_argument_name( + "any_relation_arg", kPositionalOrNamed)); + const auto named_required_schema_relation_arg = FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false) + .set_argument_name("schema_relation_arg", kPositionalOrNamed)); + const auto named_required_value_table_relation_arg = FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions( + output_schema_proto_value_table, + /*extra_relation_input_columns_allowed=*/false) + .set_argument_name("value_table_relation_arg", kPositionalOrNamed)); + const auto named_optional_string_arg = FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("format_string", kPositionalOrNamed)); + const auto named_optional_date_arg = FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("date_string", kPositionalOrNamed)); + const auto named_optional_any_relation_arg = FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("any_relation_arg", kPositionalOrNamed)); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_required_scalar_args"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_required_format_arg, named_required_date_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with two named optional arguments. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_optional_scalar_args"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_optional_string_arg, named_optional_date_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with one optional named "any table" relation argument. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_optional_any_relation_arg"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_optional_any_relation_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with one optional named "any table" relation argument and an + // optional scalar argument. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_optional_any_relation_arg_optional_scalar_arg"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_optional_any_relation_arg, named_optional_string_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with one required named "any table" relation argument. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_required_any_relation_arg"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_required_any_relation_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with one named relation argument with a required schema. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_required_schema_relation_arg"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_required_schema_relation_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with one named relation argument with a value table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_required_value_table_relation_arg"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_required_value_table_relation_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + // Add a TVF with a combination of named scalar and relation arguments. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_scalar_and_relation_args"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_required_format_arg, named_required_schema_relation_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + { + // Add a TVF with relation arg + scalar arg + named lambda. + FunctionArgumentType relation_arg = FunctionArgumentType( + ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_argument_name( + "any_relation", kPositionalOrNamed)); + FunctionArgumentType scalar_arg = FunctionArgumentType( + ARG_TYPE_ANY_1, FunctionArgumentTypeOptions().set_argument_name( + "any_scalar", kPositionalOrNamed)); + FunctionArgumentType named_lambda_arg = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions().set_argument_name("named_lambda", + kNamedOnly)); + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_relation_arg_scalar_arg_named_lambda"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {relation_arg, scalar_arg, named_lambda_arg}, + /*context_id=*/-1}, + output_schema_two_types)); + + FunctionArgumentType scalar_arg_positional_must_be_not_null = + FunctionArgumentType( + types_->get_int64(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_be_non_null(true) + .set_min_value(3)); + FunctionArgumentType scalar_arg_positional_must_be_not_null_with_default = + FunctionArgumentType( + types_->get_double(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_be_non_null(true) + .set_default(values::Double(3.14))); + FunctionArgumentType scalar_arg_positional_must_be_constant = + FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_be_constant(true)); + FunctionArgumentType scalar_arg_positional_must_be_constant_expression = + FunctionArgumentType( + types_->get_double(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_be_constant_expression(true) + .set_max_value(100)); + FunctionArgumentType scalar_arg_positional_must_support_equality = + FunctionArgumentType( + ARG_STRUCT_ANY, FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_support_equality(true)); + FunctionArgumentType scalar_arg_positional_must_support_ordering = + FunctionArgumentType( + ARG_TYPE_ANY_1, FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_support_ordering(true)); + FunctionArgumentType scalar_arg_positional_must_support_grouping = + FunctionArgumentType( + ARG_TYPE_ANY_2, FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_must_support_grouping(true)); + FunctionArgumentType + scalar_arg_positional_array_element_must_support_equality = + FunctionArgumentType( + ARG_ARRAY_TYPE_ANY_3, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_array_element_must_support_equality(true)); + FunctionArgumentType + scalar_arg_positional_array_element_must_support_ordering = + FunctionArgumentType( + ARG_ARRAY_TYPE_ANY_4, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_array_element_must_support_ordering(true)); + FunctionArgumentType + scalar_arg_positional_array_element_must_support_grouping = + FunctionArgumentType( + ARG_ARRAY_TYPE_ANY_5, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_array_element_must_support_grouping(true)); + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_positional_scalar_args_with_constraints"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + { + scalar_arg_positional_must_be_not_null, + scalar_arg_positional_must_be_constant, + scalar_arg_positional_must_be_constant_expression, + scalar_arg_positional_must_support_equality, + scalar_arg_positional_must_support_ordering, + scalar_arg_positional_must_support_grouping, + scalar_arg_positional_array_element_must_support_equality, + scalar_arg_positional_array_element_must_support_ordering, + scalar_arg_positional_array_element_must_support_grouping, + scalar_arg_positional_must_be_not_null_with_default, + }, + /*context_id=*/-1}, + output_schema_two_types)); + + FunctionArgumentType scalar_arg_named_must_be_not_null = + FunctionArgumentType( + types_->get_int64(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_1", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_be_non_null(true) + .set_min_value(3)); + FunctionArgumentType scalar_arg_named_must_be_constant = + FunctionArgumentType( + types_->get_string(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_2", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_be_constant(true)); + FunctionArgumentType scalar_arg_named_must_be_constant_expression = + FunctionArgumentType( + types_->get_double(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_3", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_be_constant_expression(true) + .set_max_value(100)); + FunctionArgumentType scalar_arg_named_must_support_equality = + FunctionArgumentType( + ARG_STRUCT_ANY, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_4", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_support_equality(true)); + FunctionArgumentType scalar_arg_named_must_support_ordering = + FunctionArgumentType( + ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_5", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_support_ordering(true)); + FunctionArgumentType scalar_arg_named_must_support_grouping = + FunctionArgumentType( + ARG_TYPE_ANY_2, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_6", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_support_grouping(true)); + FunctionArgumentType scalar_arg_named_array_element_must_support_equality = + FunctionArgumentType( + ARG_ARRAY_TYPE_ANY_3, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_7", FunctionEnums::POSITIONAL_OR_NAMED) + .set_array_element_must_support_equality(true)); + FunctionArgumentType scalar_arg_named_array_element_must_support_ordering = + FunctionArgumentType( + ARG_ARRAY_TYPE_ANY_4, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_8", FunctionEnums::POSITIONAL_OR_NAMED) + .set_array_element_must_support_ordering(true)); + FunctionArgumentType scalar_arg_named_array_element_must_support_grouping = + FunctionArgumentType( + ARG_ARRAY_TYPE_ANY_5, + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_9", FunctionEnums::POSITIONAL_OR_NAMED) + .set_array_element_must_support_grouping(true)); + FunctionArgumentType scalar_arg_named_must_be_not_null_with_default = + FunctionArgumentType( + types_->get_double(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_argument_name("arg_10", FunctionEnums::POSITIONAL_OR_NAMED) + .set_must_be_non_null(true) + .set_default(values::Double(3.14))); + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_scalar_args_with_constraints"}, + {FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + { + scalar_arg_named_must_be_not_null, + scalar_arg_named_must_be_constant, + scalar_arg_named_must_be_constant_expression, + scalar_arg_named_must_support_equality, + scalar_arg_named_must_support_ordering, + scalar_arg_named_must_support_grouping, + scalar_arg_named_array_element_must_support_equality, + scalar_arg_named_array_element_must_support_ordering, + scalar_arg_named_array_element_must_support_grouping, + scalar_arg_named_must_be_not_null_with_default, + }, + /*context_id=*/-1}, + output_schema_two_types)); + } +} // NOLINT(readability/fn_size) + +// Tests handling of optional relation arguments. +// If the relation is present, it doubles the value of each row. +// If the input relation is absent, returns a single zero. +// +// It has one optional value table argument with a single int64_t column. +// The output schema is also an int64_t value table. +class TvfOptionalRelation : public FixedOutputSchemaTVF { + class TvfOptionalRelationIterator : public EvaluatorTableIterator { + public: + explicit TvfOptionalRelationIterator( + std::unique_ptr input) + : input_(std::move(input)) {} + + bool NextRow() override { + if (!input_) { + if (rows_returned_ > 0) { + return false; + } + value_ = values::Int64(0); + ++rows_returned_; + return true; + } + + if (!input_->NextRow()) { + return false; + } + + value_ = values::Int64(input_->GetValue(0).int64_value() * 2); + ++rows_returned_; + return true; + } + + int NumColumns() const override { return 1; } + std::string GetColumnName(int i) const override { + ABSL_DCHECK_EQ(i, 0); + return ""; + } + const Type* GetColumnType(int i) const override { + ABSL_DCHECK_EQ(i, 0); + return types::Int64Type(); + } + const Value& GetValue(int i) const override { + ABSL_DCHECK_EQ(i, 0); + return value_; + } + absl::Status Status() const override { + return input_ ? input_->Status() : absl::OkStatus(); + } + absl::Status Cancel() override { return input_->Cancel(); } + + private: + int64_t rows_returned_ = 0; + std::unique_ptr input_; + Value value_; + }; + + public: + explicit TvfOptionalRelation() + : FixedOutputSchemaTVF( + {R"(tvf_optional_relation)"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + TVFRelation::ValueTable(types::Int64Type()), + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions( + TVFRelation::ValueTable(types::Int64Type()), + /*extra_relation_input_columns_allowed=*/true) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + /*context_id=*/-1), + TVFRelation::ValueTable(types::Int64Type())) {} + + absl::StatusOr> CreateEvaluator( + std::vector input_arguments, + const std::vector& output_columns, + const FunctionSignature* function_call_signature) const override { + ZETASQL_RET_CHECK_LE(input_arguments.size(), 1); + + std::unique_ptr input; + if (input_arguments.size() == 1) { + ZETASQL_RET_CHECK(input_arguments[0].relation); + input = std::move(input_arguments[0].relation); + ZETASQL_RET_CHECK_EQ(input->NumColumns(), 1); + ZETASQL_RET_CHECK_EQ(input->GetColumnType(0), types::Int64Type()); + } + + ZETASQL_RET_CHECK_EQ(output_columns.size(), 1); + return std::make_unique(std::move(input)); + } +}; + +// Tests handling of optional scalar and named arguments. +// +// Calculates and emits the value of y=xa+b, `steps` numbers of times +// incrementing x by `dx` each time. +class TvfOptionalArguments : public FixedOutputSchemaTVF { + class Evaluator : public EvaluatorTableIterator { + public: + Evaluator(double x, int64_t a, int64_t b, double dx, int64_t steps) + : x_(x), a_(a), b_(b), dx_(dx), steps_(steps) {} + + bool NextRow() override { + if (current_step_ >= steps_) { + return false; + } + + value_ = values::Double(x_ * a_ + b_); + + ++current_step_; + x_ += dx_; + return true; + } + int NumColumns() const override { return 1; } + std::string GetColumnName(int i) const override { + ABSL_DCHECK_EQ(i, 0); + return "y"; + } + const Type* GetColumnType(int i) const override { + ABSL_DCHECK_EQ(i, 0); + return types::DoubleType(); + } + const Value& GetValue(int i) const override { + ABSL_DCHECK_EQ(i, 0); + return value_; + } + absl::Status Status() const override { return absl::OkStatus(); } + absl::Status Cancel() override { return absl::OkStatus(); } + + private: + double x_; + int64_t a_; + int64_t b_; + double dx_; + int64_t steps_; + int64_t current_step_ = 0; + Value value_; + }; + + public: + explicit TvfOptionalArguments() + : FixedOutputSchemaTVF( + {R"(tvf_optional_arguments)"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{"value", types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/false), + { + // Starting x value. + FunctionArgumentType(types::DoubleType(), + FunctionArgumentType::OPTIONAL), + // A constant. + FunctionArgumentType( + types::Int64Type(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)), + // B constant. + FunctionArgumentType( + types::Int64Type(), + FunctionArgumentTypeOptions().set_cardinality( + FunctionArgumentType::OPTIONAL)), + // X increment. + FunctionArgumentType( + types::DoubleType(), + FunctionArgumentTypeOptions() + .set_argument_name( + "dx", FunctionEnums::POSITIONAL_OR_NAMED) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + // Number of steps. + FunctionArgumentType( + types::Int64Type(), + FunctionArgumentTypeOptions() + .set_argument_name( + "steps", FunctionEnums::POSITIONAL_OR_NAMED) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + }, + /*context_id=*/-1), + TVFRelation(TVFRelation({{"y", types::DoubleType()}}))) {} + + absl::StatusOr> CreateEvaluator( + std::vector input_arguments, + const std::vector& output_columns, + const FunctionSignature* function_call_signature) const override { + ZETASQL_RET_CHECK_LE(input_arguments.size(), 5); + + double x = 1; + if (!input_arguments.empty()) { + ZETASQL_RET_CHECK(input_arguments[0].value); + ZETASQL_RET_CHECK(input_arguments[0].value->type()->IsDouble()); + if (!input_arguments[0].value->is_null()) { + x = input_arguments[0].value->double_value(); + } + } + + int64_t a = 2; + if (input_arguments.size() >= 2) { + ZETASQL_RET_CHECK(input_arguments[1].value); + ZETASQL_RET_CHECK(input_arguments[1].value->type()->IsInt64()); + if (!input_arguments[1].value->is_null()) { + a = input_arguments[1].value->int64_value(); + } + } + + int64_t b = 3; + if (input_arguments.size() >= 3) { + ZETASQL_RET_CHECK(input_arguments[2].value); + ZETASQL_RET_CHECK(input_arguments[2].value->type()->IsInt64()); + if (!input_arguments[2].value->is_null()) { + b = input_arguments[2].value->int64_value(); + } + } + + double dx = 1; + if (input_arguments.size() >= 4) { + ZETASQL_RET_CHECK(input_arguments[3].value); + ZETASQL_RET_CHECK(input_arguments[3].value->type()->IsDouble()); + if (!input_arguments[3].value->is_null()) { + dx = input_arguments[3].value->double_value(); + } + } + + int64_t steps = 1; + if (input_arguments.size() >= 5) { + ZETASQL_RET_CHECK(input_arguments[4].value); + ZETASQL_RET_CHECK(input_arguments[4].value->type()->IsInt64()); + if (!input_arguments[4].value->is_null()) { + steps = input_arguments[4].value->int64_value(); + } + } + + return std::make_unique(x, a, b, dx, + steps); + } +}; + +// Tests handling of repeated arguments. +// Takes pairs of string and int arguments and produces a table with a row for +// each pair. +class TvfRepeatedArguments : public FixedOutputSchemaTVF { + class TvfRepeatedArgumentsIterator : public EvaluatorTableIterator { + public: + explicit TvfRepeatedArgumentsIterator(std::vector args) + : args_(std::move(args)) {} + + bool NextRow() override { + if (current_ + 1 >= args_.size()) { + return false; + } + + if (!args_[current_].value || + !args_[current_].value->type()->IsString()) { + status_ = absl::InternalError("Bad key"); + return false; + } + + if (!args_[current_ + 1].value || + !args_[current_ + 1].value->type()->IsInt64()) { + status_ = absl::InternalError("Bad value"); + return false; + } + + key_ = *args_[current_].value; + value_ = *args_[current_ + 1].value; + current_ += 2; + return true; + } + + int NumColumns() const override { return 2; } + std::string GetColumnName(int i) const override { + ABSL_DCHECK_GE(i, 0); + ABSL_DCHECK_LT(i, NumColumns()); + return i == 0 ? "key" : "value"; + } + const Type* GetColumnType(int i) const override { + ABSL_DCHECK_GE(i, 0); + ABSL_DCHECK_LT(i, NumColumns()); + return i == 0 ? types::StringType() : types::Int64Type(); + } + const Value& GetValue(int i) const override { + ABSL_DCHECK_GE(i, 0); + ABSL_DCHECK_LT(i, NumColumns()); + return i == 0 ? key_ : value_; + } + absl::Status Status() const override { return status_; } + absl::Status Cancel() override { return absl::OkStatus(); } + + private: + std::vector args_; + int64_t current_ = 0; + Value key_; + Value value_; + absl::Status status_; + }; + + public: + explicit TvfRepeatedArguments() + : FixedOutputSchemaTVF( + {R"(tvf_repeated_arguments)"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{"key", types::StringType()}, + {"value", types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/false), + { + FunctionArgumentType(types::StringType(), + FunctionArgumentType::REPEATED), + FunctionArgumentType(types::Int64Type(), + FunctionArgumentType::REPEATED), + }, + /*context_id=*/-1), + TVFRelation({{"key", types::StringType()}, + {"value", types::Int64Type()}})) {} + + absl::StatusOr> CreateEvaluator( + std::vector input_arguments, + const std::vector& output_columns, + const FunctionSignature* function_call_signature) const override { + ZETASQL_RET_CHECK(input_arguments.size() % 2 == 0); + return std::make_unique( + std::move(input_arguments)); + } +}; + +// Tests forwarding input schema in a TVF. +// +// This function will pass through values from input columns to matching output +// columns. For INT64 columns it will additionally add the value of the integer +// argument. +// +// It has one relation argument and one integer argument. The output +// schema is set to be the same as the input schema of the relation argument. +class TvfIncrementBy : public ForwardInputSchemaToOutputSchemaTVF { + class TvfIncrementByIterator : public EvaluatorTableIterator { + public: + explicit TvfIncrementByIterator( + std::unique_ptr input, int64_t value_arg, + std::vector output_columns) + : input_(std::move(input)), + value_arg_(value_arg), + output_columns_(std::move(output_columns)), + values_(output_columns_.size()) {} + + bool NextRow() override { + if (!input_->NextRow()) { + status_ = input_->Status(); + return false; + } + + for (int o = 0; o < output_columns_.size(); ++o) { + std::string output_column_name = GetColumnName(o); + const Value* value = nullptr; + for (int i = 0; i < input_->NumColumns(); ++i) { + if (input_->GetColumnName(i) == output_column_name) { + value = &input_->GetValue(i); + break; + } + } + + if (value == nullptr) { + status_ = ::zetasql_base::InternalErrorBuilder() + << "Could not find input column for " << output_column_name; + return false; + } + + values_[o] = value->type()->IsInt64() + ? values::Int64(value->ToInt64() + value_arg_) + : *value; + } + + return true; + } + + int NumColumns() const override { + return static_cast(output_columns_.size()); + } + std::string GetColumnName(int i) const override { + ABSL_DCHECK_LT(i, output_columns_.size()); + return output_columns_[i].name; + } + const Type* GetColumnType(int i) const override { + ABSL_DCHECK_LT(i, output_columns_.size()); + return output_columns_[i].type; + } + const Value& GetValue(int i) const override { + ABSL_DCHECK_LT(i, values_.size()); + return values_[i]; + } + absl::Status Status() const override { return status_; } + absl::Status Cancel() override { return input_->Cancel(); } + + private: + std::unique_ptr input_; + int64_t value_arg_; + absl::Status status_; + std::vector output_columns_; + std::vector values_; + }; + + public: + explicit TvfIncrementBy() + : ForwardInputSchemaToOutputSchemaTVF( + {R"(tvf_increment_by)"}, + FunctionSignature( + ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType( + types::Int64Type(), + FunctionArgumentTypeOptions() + .set_cardinality(FunctionArgumentType::OPTIONAL) + .set_default(values::Int64(1)))}, + /*context_id=*/-1)) {} + + absl::StatusOr> CreateEvaluator( + std::vector input_arguments, + const std::vector& output_columns, + const FunctionSignature* function_call_signature) const override { + ZETASQL_RET_CHECK_EQ(input_arguments.size(), 2); + ZETASQL_RET_CHECK(input_arguments[0].relation); + ZETASQL_RET_CHECK(input_arguments[1].value); + ZETASQL_RET_CHECK_EQ(input_arguments[1].value->type_kind(), TypeKind::TYPE_INT64); + return std::make_unique( + std::move(input_arguments[0].relation), + input_arguments[1].value->ToInt64(), output_columns); + } +}; + +// This function takes two integer values and provides both sum and difference. +// +// Has a fixed input and output schema. +class TvfSumAndDiff : public FixedOutputSchemaTVF { + class TvfSumAndDiffIterator : public EvaluatorTableIterator { + public: + explicit TvfSumAndDiffIterator( + std::unique_ptr input) + : input_(std::move(input)) { + output_columns_["sum"] = values::Int64(0); + output_columns_["diff"] = values::Int64(0); + } + + bool NextRow() override { + if (!input_->NextRow()) { + return false; + } + int64_t a = input_->GetValue(0).int64_value(); + int64_t b = input_->GetValue(1).int64_value(); + output_columns_["sum"] = values::Int64(a + b); + output_columns_["diff"] = values::Int64(a - b); + return true; + } + + int NumColumns() const override { + return static_cast(output_columns_.size()); + } + std::string GetColumnName(int i) const override { + ABSL_DCHECK_LT(i, output_columns_.size()); + auto iter = output_columns_.cbegin(); + std::advance(iter, i); + return iter->first; + } + const Type* GetColumnType(int i) const override { + return GetValue(i).type(); + } + const Value& GetValue(int i) const override { + ABSL_DCHECK_LT(i, output_columns_.size()); + auto iter = output_columns_.cbegin(); + std::advance(iter, i); + return iter->second; + } + absl::Status Status() const override { return input_->Status(); } + absl::Status Cancel() override { return input_->Cancel(); } + + private: + std::unique_ptr input_; + absl::btree_map output_columns_; + }; + + public: + TvfSumAndDiff() + : FixedOutputSchemaTVF( + {R"(tvf_sum_diff)"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{"sum", types::Int64Type()}, + {"diff", types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation( + {{"a", types::Int64Type()}, {"b", types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/false)}, + /*context_id=*/-1), + TVFRelation( + {{"sum", types::Int64Type()}, {"diff", types::Int64Type()}})) {} + + absl::StatusOr> CreateEvaluator( + std::vector input_arguments, + const std::vector& output_columns, + const FunctionSignature* function_call_signature) const override { + ZETASQL_RET_CHECK_EQ(input_arguments.size(), 1); + ZETASQL_RET_CHECK(input_arguments[0].relation); + return std::make_unique( + std::move(input_arguments[0].relation)); + } +}; + +void SampleCatalogImpl::LoadTableValuedFunctionsWithEvaluators() { + catalog_->AddOwnedTableValuedFunction(new TvfOptionalRelation()); + catalog_->AddOwnedTableValuedFunction(new TvfOptionalArguments()); + catalog_->AddOwnedTableValuedFunction(new TvfRepeatedArguments()); + catalog_->AddOwnedTableValuedFunction(new TvfIncrementBy()); + catalog_->AddOwnedTableValuedFunction(new TvfSumAndDiff()); +} + +void SampleCatalogImpl::LoadFunctionsWithStructArgs() { + const std::vector kOutputColumnsAllTypes = + GetOutputColumnsForAllTypes(types_); + TVFRelation output_schema_two_types = + GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); + + const Type* array_string_type = nullptr; + ZETASQL_CHECK_OK(types_->MakeArrayType(types_->get_string(), &array_string_type)); + + const Type* struct_type1 = nullptr; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"field1", array_string_type}, {"field2", array_string_type}}, + &struct_type1)); + const Type* struct_type2 = nullptr; + ZETASQL_CHECK_OK(types_->MakeStructType({{"field1", array_string_type}, + {"field2", array_string_type}, + {"field3", array_string_type}}, + &struct_type2)); + + const auto named_struct_arg1 = FunctionArgumentType( + struct_type1, FunctionArgumentTypeOptions().set_argument_name( + "struct_arg1", kPositionalOrNamed)); + const auto named_struct_arg2 = FunctionArgumentType( + struct_type2, FunctionArgumentTypeOptions().set_argument_name( + "struct_arg2", kPositionalOrNamed)); + + // A TVF with struct args. + auto tvf = std::make_unique( + std::vector{"tvf_named_struct_args"}, + FunctionSignature{FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {named_struct_arg1, named_struct_arg2}, + /*context_id=*/-1}, + output_schema_two_types); + catalog_->AddOwnedTableValuedFunction(tvf.release()); + + auto function = std::make_unique( + "fn_named_struct_args", "sample_functions", Function::SCALAR); + function->AddSignature({FunctionArgumentType(array_string_type), + {named_struct_arg1, named_struct_arg2}, + /*context_id=*/-1}); + catalog_->AddOwnedFunction(std::move(function)); +} + +void SampleCatalogImpl::LoadTVFWithExtraColumns() { + int64_t context_id = 0; + + // Add a TVF with appended columns of valid ZetaSQL types. + catalog_->AddOwnedTableValuedFunction( + new ForwardInputSchemaToOutputSchemaWithAppendedColumnTVF( + {"tvf_append_columns"}, + FunctionSignature(ARG_TYPE_RELATION, {ARG_TYPE_RELATION}, + context_id++), + {TVFSchemaColumn("append_col_int64", types::Int64Type()), + TVFSchemaColumn("append_col_int32", types::Int32Type()), + TVFSchemaColumn("append_col_uint32", types::Uint32Type()), + TVFSchemaColumn("append_col_uint64", types::Uint64Type()), + TVFSchemaColumn("append_col_bytes", types::BytesType()), + TVFSchemaColumn("append_col_bool", types::BoolType()), + TVFSchemaColumn("append_col_float", types::FloatType()), + TVFSchemaColumn("append_col_double", types::DoubleType()), + TVFSchemaColumn("append_col_date", types::DateType()), + TVFSchemaColumn("append_col_timestamp", types::TimestampType()), + TVFSchemaColumn("append_col_numeric", types::NumericType()), + TVFSchemaColumn("append_col_bignumeric", types::BigNumericType()), + TVFSchemaColumn("append_col_json", types::JsonType()), + TVFSchemaColumn("append_col_string", types::StringType()), + TVFSchemaColumn("append_col_uuid", types::UuidType())})); + + // Add a TVF with an appended column that has empty name. + catalog_->AddOwnedTableValuedFunction( + new ForwardInputSchemaToOutputSchemaWithAppendedColumnTVF( + {"tvf_append_no_column"}, + FunctionSignature(ARG_TYPE_RELATION, {ARG_TYPE_RELATION}, + context_id++), + {})); + + const auto named_required_any_relation_arg = FunctionArgumentType( + ARG_TYPE_RELATION, FunctionArgumentTypeOptions().set_argument_name( + "any_relation_arg", kPositionalOrNamed)); + + // Add a TVF with one required named "any table" relation argument. + catalog_->AddOwnedTableValuedFunction( + new ForwardInputSchemaToOutputSchemaWithAppendedColumnTVF( + {"tvf_append_columns_any_relation_arg"}, + FunctionSignature(ARG_TYPE_RELATION, + {named_required_any_relation_arg}, + /*context_id=*/context_id++), + {TVFSchemaColumn("append_col_int32", types::Int32Type())})); +} + +void SampleCatalogImpl::LoadDescriptorTableValuedFunctions() { + int64_t context_id = 0; + const std::vector kOutputColumnsAllTypes = + GetOutputColumnsForAllTypes(types_); + + TVFRelation output_schema_two_types = + GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); + + const std::string kInt64a = "int64a"; + const std::string kInt64b = "int64b"; + + // Add a TVF with a table parameter and a descriptor with -1 table offset. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_one_descriptor"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true), + FunctionArgumentType::AnyDescriptor()}, + context_id++), + output_schema_two_types)); + + // Add a TVF with two table parameters, one descriptor with 0 table offset + // and one descriptor with 1 table offset. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_relations_arg_two_descriptors_resolved_names"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true), + FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64b, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true), + FunctionArgumentType::AnyDescriptor(0), + FunctionArgumentType::AnyDescriptor(1)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a table parameter and a descriptor with 0 table offset. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_one_descriptor_resolved_names"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true), + FunctionArgumentType::AnyDescriptor(0)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a descriptor with 1 table offset and a table parameter. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_descriptor_resolved_names_one_relation_arg"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyDescriptor(1), + FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64()), + TVFRelation::Column(kInt64b, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a descriptor with 1 table offset and a table parameter with + // ambiguous column naming problem. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_descriptor_resolved_names_one_relation_arg_ambiguous_naming"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyDescriptor(1), + FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64()), + TVFRelation::Column(kInt64b, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a descriptor with 1 table offset, a table parameter and a + // descriptor with -1 table offset. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_descriptor_resolved_names_one_relation_arg_one_descriptor_arg"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyDescriptor(1), + FunctionArgumentType::RelationWithSchema( + TVFRelation({TVFRelation::Column(kInt64a, types_->get_int64())}), + /*extra_relation_input_columns_allowed=*/true), + FunctionArgumentType::AnyDescriptor()}, + context_id++), + output_schema_two_types)); +} + +void SampleCatalogImpl::LoadConnectionTableValuedFunctions() { + int64_t context_id = 0; + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_connection_arg_with_fixed_output"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyConnection()}, context_id++), + TVFRelation({{kTypeString, types::StringType()}}))); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_connection_one_string_arg_with_fixed_output"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeInt64, types::Int64Type()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyConnection(), + FunctionArgumentType(types::StringType())}, + context_id++), + TVFRelation({{kTypeInt64, types::Int64Type()}, + {kTypeString, types::StringType()}}))); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_connections_with_fixed_output"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + TVFRelation({{kTypeDouble, types::DoubleType()}, + {kTypeString, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyConnection(), + FunctionArgumentType::AnyConnection()}, + context_id++), + TVFRelation({{kTypeDouble, types::DoubleType()}, + {kTypeString, types::StringType()}}))); +} + +void SampleCatalogImpl::LoadTableValuedFunctionsWithDeprecationWarnings() { + // Generate an empty output schema. + TVFRelation empty_output_schema({}); + + const std::vector kOutputColumnsAllTypes = + GetOutputColumnsForAllTypes(types_); + + TVFRelation output_schema_two_types = + GetOutputSchemaWithTwoTypes(kOutputColumnsAllTypes); + + int context_id = 0; + + // Add a TVF that triggers a deprecation warning. + FunctionSignature deprecation_warning_signature( + FunctionArgumentType::RelationWithSchema( + empty_output_schema, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++); + deprecation_warning_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/11)}); + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_deprecation_warning"}, deprecation_warning_signature, + output_schema_two_types)); + + // Add a TVF that triggers two deprecation warnings with the same kind. + FunctionSignature two_deprecation_warnings_same_kind_signature( + FunctionArgumentType::RelationWithSchema( + empty_output_schema, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++); + two_deprecation_warnings_same_kind_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/12), + CreateDeprecationWarning(/*id=*/13)}); + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_deprecation_warnings_same_kind"}, + two_deprecation_warnings_same_kind_signature, output_schema_two_types)); + + // Add a TVF that triggers two deprecation warnings with different kinds. + FunctionSignature two_deprecation_warnings_signature( + FunctionArgumentType::RelationWithSchema( + empty_output_schema, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++); + two_deprecation_warnings_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/14), + CreateDeprecationWarning( + /*id=*/15, DeprecationWarning::DEPRECATED_FUNCTION_SIGNATURE)}); + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_deprecation_warnings"}, two_deprecation_warnings_signature, + output_schema_two_types)); + + // Add a TVF with exactly one relation argument. The output schema is set to + // be the same as the input schema. The TVF also triggers a deprecation + // warning. + FunctionSignature forward_deprecation_signature( + ARG_TYPE_RELATION, {FunctionArgumentType::AnyRelation()}, context_id++); + forward_deprecation_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/16)}); + catalog_->AddOwnedTableValuedFunction(new ForwardInputSchemaToOutputSchemaTVF( + {"tvf_one_relation_arg_output_schema_is_input_schema_deprecation"}, + forward_deprecation_signature)); + + // Add a TVF with three arguments: The first one is required model; The + // second is optional table; The third is named optional struct. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_model_optional_table_named_struct"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyModel(), + FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_STRUCT_ANY, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with two arguments: The first is named optional struct. The + // second is a named optional table; + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_struct_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType( + ARG_STRUCT_ANY, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("barfoo", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with two arguments: The first is named optional struct. The + // second is a named optional table; + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_named_proto_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType( + ARG_PROTO_ANY, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("barfoo", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required model; The + // second is optional scalar; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_model_optional_scalar_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyModel(), + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first two are optional scalars; The + // third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_optional_scalars_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required table; The + // second is optional scalar; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_table_optional_scalar_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is optional table; The + // second is optional scalar; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_optional_table_optional_scalar_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required scalar; The + // second is optional model; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_scalar_optional_model_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ARBITRARY), + FunctionArgumentType(ARG_TYPE_MODEL, FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required scalar; The + // second is optional model; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_optional_scalar_optional_model_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType(ARG_TYPE_MODEL, FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is optional scalar; The + // second is named optional table; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_optional_scalar_named_tables"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("barfoo", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is optional scalar; The + // second is named optional model; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_optional_scalar_named_model_named_table"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + ARG_TYPE_MODEL, + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("barfoo", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with a required table, a named optional table, a mandatory + // named table and a required mandatory named table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_required_named_optional_required_tables"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::REQUIRED), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("table2", kNamedOnly) + .set_cardinality(FunctionArgumentType::REQUIRED)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("table3", kPositionalOrNamed) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("table4", kNamedOnly) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required model; The + // second is optional string; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_model_optional_string_optional_table"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyModel(), + FunctionArgumentType(types::StringType(), + FunctionArgumentType::OPTIONAL), + FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL)}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required model; The + // second is optional table; The third is named optional string with default. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_model_optional_table_named_string_default"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType::AnyModel(), + FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + types::StringType(), + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kPositionalOrNamed) + .set_default(values::String("default")) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with three arguments: The first one is required model; The + // second is a string with default; The third is named optional table. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_optional_table_default_mandatory_string"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType(ARG_TYPE_RELATION, + FunctionArgumentType::OPTIONAL), + FunctionArgumentType( + types::StringType(), + FunctionArgumentTypeOptions() + .set_argument_name("foobar", kNamedOnly) + .set_default(values::String("default")) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); + + // Add a TVF with two table arguments which are both named-only. + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_two_named_only_tables"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_two_types, + /*extra_relation_input_columns_allowed=*/false), + {FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("table1", kNamedOnly) + .set_cardinality(FunctionArgumentType::OPTIONAL)), + FunctionArgumentType( + ARG_TYPE_RELATION, + FunctionArgumentTypeOptions() + .set_argument_name("table2", kNamedOnly) + .set_cardinality(FunctionArgumentType::OPTIONAL))}, + context_id++), + output_schema_two_types)); +} + +// Add a SQL table function to catalog starting from a full create table +// function statement. +void SampleCatalogImpl::AddSqlDefinedTableFunctionFromCreate( + absl::string_view create_table_function, + const LanguageOptions& language_options, absl::string_view user_id_column) { + // Ensure the language options used allow CREATE FUNCTION + LanguageOptions language = language_options; + language.AddSupportedStatementKind(RESOLVED_CREATE_TABLE_FUNCTION_STMT); + language.EnableLanguageFeature(FEATURE_CREATE_TABLE_FUNCTION); + language.EnableLanguageFeature(FEATURE_TABLE_VALUED_FUNCTIONS); + language.EnableLanguageFeature(FEATURE_TEMPLATE_FUNCTIONS); + language.EnableLanguageFeature(FEATURE_V_1_1_WITH_ON_SUBQUERY); + language.EnableLanguageFeature(FEATURE_V_1_3_INLINE_LAMBDA_ARGUMENT); + AnalyzerOptions analyzer_options; + analyzer_options.set_language(language); + std::unique_ptr analyzer_output; + ZETASQL_CHECK_OK(AnalyzeStatement(create_table_function, analyzer_options, + catalog_.get(), catalog_->type_factory(), + &analyzer_output)) + << "[" << create_table_function << "]"; + const ResolvedStatement* resolved = analyzer_output->resolved_statement(); + ABSL_CHECK(resolved->Is()); + const auto* resolved_create = + resolved->GetAs(); + + std::unique_ptr function; + if (resolved_create->query() != nullptr) { + std::unique_ptr sql_tvf; + ZETASQL_CHECK_OK(SQLTableValuedFunction::Create(resolved_create, &sql_tvf)); + function = std::move(sql_tvf); + } else { + function = std::make_unique( + resolved_create->name_path(), resolved_create->signature(), + resolved_create->argument_name_list(), + ParseResumeLocation::FromString(resolved_create->code())); + } + + if (!user_id_column.empty()) { + ZETASQL_CHECK_OK(function->SetUserIdColumnNamePath({std::string(user_id_column)})); + } + catalog_->AddOwnedTableValuedFunction(std::move(function)); + sql_object_artifacts_.emplace_back(std::move(analyzer_output)); +} + +void SampleCatalogImpl::LoadNonTemplatedSqlTableValuedFunctions( + const LanguageOptions& language_options) { + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelectWithUserId() + AS SELECT 1 AS a, 2 AS b;)", + language_options, "a"); + + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelect() + AS SELECT 1 AS a, 2 AS b;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelectUnion() + AS SELECT 1 AS a, 2 AS b + UNION ALL + SELECT 1, 4;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelectCTE() + AS WITH t AS (SELECT 1 AS a, 2 AS b) SELECT * FROM t;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelectFromTvf() + AS SELECT * FROM NullarySelect();)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelectCallingScalarUDF() + AS SELECT NullaryPi() AS pi;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION NullarySelectCallingLambdaArgFunction() + AS SELECT ARRAY_FILTER([1, 2, 3], e -> e > 1) as arr;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION UnaryScalarArg(arg0 INT64) + AS SELECT arg0;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION UnaryScalarArgMultipleReferences(arg0 INT64) + AS SELECT arg0 + arg0 AS ret0, arg0 AS ret1;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION BinaryScalarArg(arg0 INT64, arg1 INT64) + AS SELECT arg0, arg1, arg0 + arg1 AS ret2;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION UnaryScalarArgSubqueryReference(arg0 INT64) + AS SELECT (SELECT arg0) AS ret0;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION UnaryScalarArgSubqueryWithReference(arg0 INT64) + AS SELECT (WITH t AS (SELECT arg0) SELECT t.arg0 FROM t) AS ret0;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION UnaryTableArg(arg0 TABLE) + AS SELECT * FROM arg0;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION ScalarParamUsedAsTVFArgument(arg0 INT64) + AS (SELECT * FROM UnaryScalarArg(arg0)) + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION UnaryAbTableArg(arg0 TABLE) + AS SELECT * FROM arg0; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION UnaryAbTableArgSelfJoin( + arg0 TABLE) + AS SELECT * FROM arg0 CROSS JOIN arg0 AS t1; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION UnaryAbTableArgScannedInCTE( + arg0 TABLE) + AS WITH t AS (SELECT * FROM arg0) SELECT * FROM t; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION BinaryTableArg( + arg0 TABLE, arg1 TABLE) + AS SELECT * FROM arg0 CROSS JOIN arg1; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION BinaryAbTableArg( + arg0 TABLE, arg1 TABLE) + AS SELECT * FROM arg0 CROSS JOIN arg1; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION UnaryAbTableArgWithScalarArgs( + x INT64, arg0 TABLE, y STRING) + AS SELECT * FROM arg0 WHERE arg0.a = x AND arg0.b = y; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION UnaryAbTableArgWithScalarArgsTempl( + x ANY TYPE, arg0 ANY TABLE, y ANY TYPE) + AS SELECT * FROM arg0 WHERE arg0.a = x AND arg0.b = y; + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION CallsUnaryAbTableArgWithScalarArgsTempl( + ignored_param ANY TYPE) + AS SELECT * FROM UnaryAbTableArgWithScalarArgsTempl( + 1, (SELECT 1 a, "2" b, DATE '2020-08-22' AS c), "b"); + )sql", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION JoinsTableArgToScannedTable( + arg_table TABLE, arg_scalar STRING + ) + AS SELECT arg_scalar AS c, * + FROM TwoIntegers JOIN arg_table USING (key, value); + )sql", + language_options); + + // Functions for definer-rights inlining + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION DefinerRightsTvf(a INT64) SQL SECURITY DEFINER + AS SELECT * FROM KeyValue WHERE KeyValue.Key = a; )sql", + language_options); + + if (language_options.LanguageFeatureEnabled( + FEATURE_V_1_3_COLLATION_SUPPORT) && + language_options.LanguageFeatureEnabled( + FEATURE_V_1_3_ANNOTATION_FRAMEWORK)) { + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION ScalarArgWithCollatedOutputCols(arg0 STRING) + AS SELECT + COLLATE(arg0, 'und:ci') AS col_ci, + [COLLATE(arg0, 'und:ci')] AS col_array_ci, + ([COLLATE(arg0, 'und:ci')], 1) AS col_struct_ci;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION TableArgWithCollatedOutputCols( + arg0 TABLE) + AS SELECT + COLLATE(a, 'und:ci') AS col_ci, + [COLLATE(a, 'und:ci')] AS col_array_ci, + ([COLLATE(a, 'und:ci')], 1) AS col_struct_ci + FROM arg0;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION ValueTableArgWithCollatedOutputCols( + arg0 TABLE>) + AS SELECT + COLLATE(str_field, 'und:ci') AS col_ci, + [COLLATE(str_field, 'und:ci')] AS col_array_ci, + ([COLLATE(str_field, 'und:ci')], 1) AS col_struct_ci + FROM arg0;)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION ScalarArgWithCollatedOutputValueTableCol( + arg0 STRING) + AS SELECT AS VALUE COLLATE(arg0, 'und:ci');)", + language_options); + AddSqlDefinedTableFunctionFromCreate( + R"(CREATE TABLE FUNCTION ScalarArgsOfCollatableTypes( + arg0 STRING, arg1 ARRAY, arg2 STRUCT) + AS SELECT arg0, arg1, arg2;)", + language_options); + } + if (language_options.LanguageFeatureEnabled( + FEATURE_V_1_1_ORDER_BY_IN_AGGREGATE)) { + AddSqlDefinedTableFunctionFromCreate( + R"sql( + CREATE TABLE FUNCTION UnaryTableArgAggregatedWithOrderBy( + arg0 TABLE) + AS WITH t AS (SELECT ARRAY_AGG(a ORDER BY a) AS arr FROM arg0) + SELECT * FROM t; + )sql", + language_options); + } +} + +void SampleCatalogImpl::LoadTemplatedSQLTableValuedFunctions() { + const std::string kColumnNameKey = "key"; + const std::string kColumnNameDate = "date"; + int context_id = 0; + + // Add a TVF with a simple valid templated SQL body. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_one"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, ParseResumeLocation::FromString("select 1 as x"))); + + // Add a templated SQL TVF that calls another templated SQL TVF. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_call_tvf_templated_select_one"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select * from tvf_templated_select_one()"))); + + // Add a templated SQL TVF that calls another templated SQL TVF twice. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_call_tvf_templated_select_one_twice"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select * from tvf_templated_select_one() union all " + "select * from tvf_templated_select_one()"))); + + // Add a TVF with a valid templated SQL body that refers to a scalar argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_int64_arg"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(types::Int64Type())}, + context_id++), + /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); + + // Add a TVF with a valid templated SQL body that refers to a scalar argument + // with a name that is potentially ambiguous with an in scope column name. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_int64_arg_with_name_ambiguity"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(types::Int64Type())}, + context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("SELECT x FROM (SELECT -99 AS x)"))); + + // Add a TVF with a valid templated SQL body that refers to a scalar argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_any_scalar_arg"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++), + /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); + + // Add a TVF with a valid templated SQL body that performs addition on a + // scalar argument. The function signature accepts a single argument of any + // scalar type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_scalar_arg_plus_integer"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select x + 42"))); + + // Add a TVF with a valid templated SQL body that accepts an input argument + // where the name contains '$'. The function signature accepts a single + // argument of any scalar type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_scalar_arg_plus_integer_accept_dollars_col_name"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*arg_name_list=*/{"$col1"}, + ParseResumeLocation::FromString("select `$col1` as x"))); + + // Add a TVF with a valid templated SQL body that returns an output column + // where the name contains '$'. The function signature accepts a single + // argument of any scalar type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_scalar_arg_plus_integer_return_dollars_col_name"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select x as `$col1`"))); + + // Add a TVF with a valid templated SQL body that performs concatenation on a + // scalar argument. The function signature accepts a single argument of any + // scalar type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_scalar_arg_concat_string"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select concat(x, 'abc') as y"))); + + // Add a TVF with a valid templated SQL body that performs a proto field + // access on a scalar argument. The function signature accepts a single + // argument of any scalar type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_scalar_arg_proto_field_access"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY, + FunctionArgumentType::REQUIRED)}, + context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select x.int32_field as y"))); + + // Add a TVF with a valid templated SQL body that refers to a relation + // argument using specific column names. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_relation_arg_using_column_names"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation()}, context_id++), + /*arg_name_list=*/{"t"}, + ParseResumeLocation::FromString("select key, value from t"))); + + // Add a TVF with a simple valid templated SQL body that selects a name from + // a templated input table argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_a"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation()}, context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select a from x"))); + + // Add a TVF with a valid templated SQL body that refers to a relation + // argument using "select *". + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_relation_arg_using_select_star"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation()}, context_id++), + /*arg_name_list=*/{"t"}, + ParseResumeLocation::FromString("select * from t"))); + + // Add a TVF with a templated SQL body that refers to a relation argument + // using "select 1". The TVF is missing an output column name. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_relation_arg_using_select_one"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation()}, context_id++), + /*arg_name_list=*/{"t"}, + ParseResumeLocation::FromString("(select 1 from t limit 1)"))); + + // Add a TVF with a valid templated SQL body that refers to a relation + // argument and also a table in the catalog. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_relation_arg_and_catalog_table"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation()}, context_id++), + /*arg_name_list=*/{"t"}, + ParseResumeLocation::FromString( + "(select * from t) union all (select * from keyvalue)"))); + + // Add a TVF with a valid templated SQL body that refers to two relation + // arguments and uses a SQL WITH clause. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_two_relation_args"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType::AnyRelation(), + FunctionArgumentType::AnyRelation()}, + context_id++), + /*arg_name_list=*/{"s", "t"}, + ParseResumeLocation::FromString( + "with w1 as (select * from s),\n" + " w2 as (select * from t)\n" + "select * from w1 inner join w2 using (key) order by key limit 1"))); + + // Add a TVF with a valid templated SQL body that refers to both a scalar + // argument and a relation argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_scalar_and_relation_args"}, + FunctionSignature( + ARG_TYPE_RELATION, + {FunctionArgumentType(types::Int64Type()), + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + /*arg_name_list=*/{"x", "t"}, + ParseResumeLocation::FromString("select key from t where key < x"))); + + // This is the same TVF as above, but without a schema specified for the + // relation argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_scalar_and_relation_args_no_schema"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(types::Int64Type()), + FunctionArgumentType::AnyRelation()}, + context_id++), + /*arg_name_list=*/{"x", "t"}, + ParseResumeLocation::FromString("select key from t where key < x"))); + + // This is the same TVF as above, but without a schema specified for the + // relation argument. + const StructType* arg_type = nullptr; + std::vector fields{{"y", types::Int64Type()}, + {"z", types::StringType()}}; + ZETASQL_CHECK_OK(type_factory()->MakeStructType(fields, &arg_type)); + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_with_struct_param"}, + FunctionSignature( + ARG_TYPE_RELATION, + {FunctionArgumentType(arg_type), FunctionArgumentType::AnyRelation()}, + context_id++), + /*arg_name_list=*/{"x", "t"}, + ParseResumeLocation::FromString("select x.y from t as x"))); + + // Add a TVF with a valid templated SQL body that refers to both a scalar + // date argument and a relation argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_scalar_date_and_relation_args"}, + FunctionSignature( + ARG_TYPE_RELATION, + {FunctionArgumentType(types::DateType()), + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameDate, types::DateType()}}), + /*extra_relation_input_columns_allowed=*/true)}, + context_id++), + /*arg_name_list=*/{"d", "t"}, + ParseResumeLocation::FromString( + "select `date` from t where `date` < d"))); + + // Add a TVF with a valid templated SQL body that refers to both a scalar + // argument of any type and a relation argument of any table. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_any_scalar_and_relation_args"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY), + FunctionArgumentType::AnyRelation()}, + context_id++), + /*arg_name_list=*/{"s", "t"}, + ParseResumeLocation::FromString("select *, s from t"))); + + // Add an invalid TVF with a simple templated SQL body missing an output + // column name. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_one_missing_col_name"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, ParseResumeLocation::FromString("select 1"))); + + // Add an invalid templated SQL TVF with a parse error in the function body. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_parse_error"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, ParseResumeLocation::FromString("a b c d e"))); + + // Add an invalid templated SQL TVF with an analysis error in the function + // body. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_analysis_error"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString("select * from invalidtable"))); + + // Add an invalid templated SQL TVF where the function body is not a query. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_function_body_not_query"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "insert keyvalue (key, value) values (1, 'one')"))); + + // Add an invalid templated SQL TVF that attempts to refer to a query + // parameter. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_function_body_refer_to_parameter"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select @test_param_bool from keyvalue"))); + + // Add an invalid templated SQL TVF where the function body is empty. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_function_body_empty"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, ParseResumeLocation::FromString(""))); + + // Add an invalid templated SQL TVF that directly calls itself. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_recursive"}, FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString("select * from tvf_recursive()"))); + + // Add two invalid templated SQL TVFs that indirectly call themselves. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_calls_self_indirectly_1"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select * from tvf_calls_self_indirectly_2()"))); + + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_calls_self_indirectly_2"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select * from tvf_calls_self_indirectly_1()"))); + + // Add a templated SQL TVF that calls a templated SQL function that calls the + // original templated SQL TVF again, to make sure cycle detection works. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_calls_udf_calls_same_tvf"}, + FunctionSignature(ARG_TYPE_RELATION, {}, context_id++), + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select udf_calls_tvf_calls_same_udf()"))); + + // Add a templated SQL TVF that calls a TVF that triggers a deprecation + // warning. + FunctionSignature deprecation_warning_signature( + ARG_TYPE_RELATION, /*arguments=*/{}, context_id++); + deprecation_warning_signature.SetAdditionalDeprecationWarnings( + {CreateDeprecationWarning(/*id=*/1001)}); + catalog_->AddOwnedTableValuedFunction( + new TemplatedSQLTVF({"tvf_templated_calls_tvf_deprecation_warning"}, + deprecation_warning_signature, + /*arg_name_list=*/{}, + ParseResumeLocation::FromString( + "select * from tvf_deprecation_warning()"))); + + FunctionSignature signature_return_key_int64_col( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}}), + /*extra_relation_input_columns_allowed=*/true), + /*arguments=*/{FunctionArgumentType(ARG_TYPE_ARBITRARY)}, context_id++); + FunctionSignature signature_return_key_int64_and_value_string_cols( + FunctionArgumentType::RelationWithSchema( + TVFRelation({{kColumnNameKey, types::Int64Type()}, + {kColumnNameValue, types::StringType()}}), + /*extra_relation_input_columns_allowed=*/true), + /*arguments=*/ + {FunctionArgumentType(ARG_TYPE_ARBITRARY), + FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++); + FunctionSignature signature_return_value_table_string_col( + FunctionArgumentType::RelationWithSchema( + TVFRelation::ValueTable(types::StringType()), + /*extra_relation_input_columns_allowed=*/true), + /*arguments=*/{FunctionArgumentType(ARG_TYPE_ARBITRARY)}, context_id++); + + // Add a templated TVF with a required signature of a single INT64 column + // named "key". + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_x_with_required_output_schema"}, + signature_return_key_int64_col, + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select x as key"))); + + // Add an invalid templated TVF that returns a duplicate column name. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_duplicate_output_column_with_required_output_schema"}, + signature_return_key_int64_col, + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select x as key, 42 as key"))); + + // Add a templated TVF with a required non-value-table output schema. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_missing_output_column_with_required_output_schema"}, + signature_return_key_int64_col, + /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); + + // Add a templated TVF with a required value-table output schema that returns + // a value table. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_as_value_x_with_required_" + "value_table_output_schema"}, + signature_return_value_table_string_col, + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select as value x"))); + + // Add a templated TVF with a required value-table output schema that returns + // a non-value table of the same type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_x_with_required_value_table_output_schema"}, + signature_return_value_table_string_col, + /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select x"))); + + // Add a templated TVF with a required value-table output schema that returns + // NULL. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_null_with_required_value_table_output_schema"}, + signature_return_value_table_string_col, + /*arg_name_list=*/{"x"}, ParseResumeLocation::FromString("select null"))); + + // Add a templated TVF with a required value-table output schema that returns + // NULL casted to string type. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_null_str_with_required_value_table_output_schema"}, + signature_return_value_table_string_col, + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select cast(null as string)"))); + + // Add a templated TVF with a required output schema with two columns. The + // function body returns the two columns in opposite order. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_return_swapped_cols_required_output_schema"}, + signature_return_key_int64_and_value_string_cols, + /*arg_name_list=*/{"key", "value"}, + ParseResumeLocation::FromString("select value, key"))); + + // Add a templated TVF with a required output schema with two columns. The + // function body returns the two columns in opposite order, plus an extra + // column. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_return_swapped_cols_plus_extra_required_output_schema"}, + signature_return_key_int64_and_value_string_cols, + /*arg_name_list=*/{"key", "value"}, + ParseResumeLocation::FromString("select value, key, 42 as x"))); + + // Add a templated TVF which returns three columns of collated string type, + // collated array type and struct type with collated field by calling COLLATE + // function over input scalar argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_collated_output_columns_with_collate_function"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY), + FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++), + /*arg_name_list=*/{"x", "y"}, + ParseResumeLocation::FromString( + "select COLLATE(x, 'und:ci') as col_ci, [COLLATE(x, 'und:ci')] as " + "col_array_ci, (COLLATE(x, 'und:ci'), y) as col_struct_ci"))); + + // Add a templated TVF which returns three columns of collated string type, + // collated array type and struct type with collated field by referencing + // collated table column. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_collated_output_columns_with_collated_column_ref"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++), + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString( + "select CONCAT(x, string_ci) as concat_ci, array_with_string_ci, " + "struct_with_string_ci from CollatedTable"))); + + // Add a templated TVF which returns columns of collated types with input + // relation argument. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_collated_output_columns_with_relation_arg"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_RELATION), + FunctionArgumentType(types::Int64Type())}, + context_id++), + /*arg_name_list=*/{"x", "y"}, + ParseResumeLocation::FromString( + "select COLLATE(col_str, 'und:ci') as col_ci, [COLLATE(col_str, " + "'und:ci')] as col_array_ci, (COLLATE(col_str, 'und:ci'), y) as " + "col_struct_ci from x"))); + + // Add a templated TVF which returns a value table with collated output + // column. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_collated_output_column_as_value_table"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY), + FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++), + /*arg_name_list=*/{"x", "y"}, + ParseResumeLocation::FromString( + "select as value CONCAT(COLLATE(x, 'und:ci'), y)"))); + + // Add a templated TVF which returns a collated output column and has an + // explicit result schema. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_collated_output_column_returns_int64_string_col"}, + signature_return_key_int64_and_value_string_cols, + /*arg_name_list=*/{"x", "y"}, + ParseResumeLocation::FromString( + "select x as key, COLLATE(y, 'und:ci') as value"))); + + // Add a templated TVF which returns a collated output column and has an + // explicit value table result schema. + catalog_->AddOwnedTableValuedFunction(new TemplatedSQLTVF( + {"tvf_templated_select_collated_output_column_returns_value_table_string_" + "col"}, + signature_return_value_table_string_col, + /*arg_name_list=*/{"x"}, + ParseResumeLocation::FromString("select as value COLLATE(x, 'und:ci')"))); + + // Add a templated definer-rights TVF + auto templated_definer_rights_tvf = std::make_unique( + std::vector{"definer_rights_templated_tvf"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_ARBITRARY)}, + context_id++), + /*arg_name_list=*/std::vector{"x"}, + ParseResumeLocation::FromString("select * from KeyValue WHERE key = x")); + templated_definer_rights_tvf->set_sql_security( + ResolvedCreateStatementEnums::SQL_SECURITY_DEFINER); + catalog_->AddOwnedTableValuedFunction( + std::move(templated_definer_rights_tvf)); + + // b/259000660: Add a templated SQL TVF whose code has a braced proto + // constructor. + auto templated_proto_braced_ctor_tvf = std::make_unique( + std::vector{"templated_proto_braced_ctor_tvf"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_RELATION)}, + context_id++), + /*arg_name_list=*/std::vector{"T"}, + ParseResumeLocation::FromString(R"sql( + SELECT NEW zetasql_test__.TestExtraPB {int32_val1 : v} AS dice_roll + FROM T)sql")); + catalog_->AddOwnedTableValuedFunction( + std::move(templated_proto_braced_ctor_tvf)); + auto templated_struct_braced_ctor_tvf = std::make_unique( + std::vector{"templated_struct_braced_ctor_tvf"}, + FunctionSignature(ARG_TYPE_RELATION, + {FunctionArgumentType(ARG_TYPE_RELATION)}, + context_id++), + /*arg_name_list=*/std::vector{"T"}, + ParseResumeLocation::FromString(R"sql( + SELECT STRUCT {int32_val1 : v} AS dice_roll + FROM T)sql")); + catalog_->AddOwnedTableValuedFunction( + std::move(templated_struct_braced_ctor_tvf)); +} + +void SampleCatalogImpl::LoadTableValuedFunctionsWithAnonymizationUid() { + // Generate an output schema that returns every possible type. + const std::vector output_columns = + GetOutputColumnsForAllTypes(types_); + TVFRelation::ColumnList columns; + columns.reserve(output_columns.size()); + for (const auto& kv : output_columns) { + columns.emplace_back(kv.name, kv.type); + } + TVFRelation output_schema_all_types(columns); + + int context_id = 0; + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args_with_anonymization_uid"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_all_types, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + AnonymizationInfo::Create({"column_int64"}).value_or(nullptr), + output_schema_all_types)); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_relation_arg_with_anonymization_uid"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_all_types, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList({FunctionArgumentType(ARG_TYPE_RELATION)}), + context_id++), + AnonymizationInfo::Create({"column_int64"}).value_or(nullptr), + output_schema_all_types)); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_one_templated_arg_with_anonymization_uid"}, + FunctionSignature( + FunctionArgumentType::RelationWithSchema( + output_schema_all_types, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList({FunctionArgumentType(ARG_TYPE_ARBITRARY)}), + context_id++), + AnonymizationInfo::Create({"column_int64"}).value_or(nullptr), + output_schema_all_types)); + + TVFRelation output_schema_proto( + {{"user_info", GetProtoType(zetasql_test__::TestExtraPB::descriptor())}}); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args_with_nested_anonymization_uid"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_proto, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + AnonymizationInfo::Create({"user_info", "int32_val1"}).value_or(nullptr), + output_schema_proto)); + + TVFRelation output_schema_proto_value_table = TVFRelation::ValueTable( + GetProtoType(zetasql_test__::TestExtraPB::descriptor())); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args_value_table_with_anonymization_uid"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_proto_value_table, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + AnonymizationInfo::Create({"int32_val1"}).value_or(nullptr), + output_schema_proto_value_table)); + + TVFRelation output_schema_proto_value_table_with_nested_int = + TVFRelation::ValueTable( + GetProtoType(zetasql_test__::KitchenSinkPB::descriptor())); + + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args_value_table_with_nested_anonymization_uid"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_proto_value_table_with_nested_int, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + AnonymizationInfo::Create({"nested_value", "nested_int64"}) + .value_or(nullptr), + output_schema_proto_value_table_with_nested_int)); + + // Repro for unvalidated AnonymizationInfo::UserIdColumnNamePath() seen in + // b/254939522 + catalog_->AddOwnedTableValuedFunction(new FixedOutputSchemaTVF( + {"tvf_no_args_value_table_with_invalid_nested_anonymization_uid"}, + FunctionSignature(FunctionArgumentType::RelationWithSchema( + output_schema_proto_value_table_with_nested_int, + /*extra_relation_input_columns_allowed=*/false), + FunctionArgumentTypeList(), context_id++), + AnonymizationInfo::Create({"nested_value.nested_int64"}) + .value_or(nullptr), + output_schema_proto_value_table_with_nested_int)); +} + +void SampleCatalogImpl::AddProcedureWithArgumentType(std::string type_name, + const Type* arg_type) { + auto procedure = absl::WrapUnique( + new Procedure({absl::StrCat("proc_on_", type_name)}, + {types_->get_bool(), {arg_type}, /*context_id=*/-1})); + catalog_->AddOwnedProcedure(std::move(procedure)); +} + +void SampleCatalogImpl::LoadProcedures() { + Procedure* procedure = nullptr; + + // Procedure with no arguments. + procedure = new Procedure({"proc_no_args"}, + {types_->get_bool(), {}, /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // Add a procedure that takes a specific enum as an argument. + const EnumType* enum_TestEnum = + GetEnumType(zetasql_test__::TestEnum_descriptor()); + procedure = + new Procedure({"proc_on_TestEnum"}, + {types_->get_bool(), {enum_TestEnum}, /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // Add a procedure to illustrate how repeated/optional arguments are resolved. + procedure = + new Procedure({"proc_on_req_opt_rep"}, + {types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::REQUIRED}, + {types_->get_int64(), FunctionArgumentType::REPEATED}, + {types_->get_int64(), FunctionArgumentType::REPEATED}, + {types_->get_int64(), FunctionArgumentType::REQUIRED}, + {types_->get_int64(), FunctionArgumentType::OPTIONAL}}, + /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // Add a procedure with templated arguments. + procedure = + new Procedure({"proc_on_any_any"}, {types_->get_int64(), + {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1}, + /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // Add a procedure with templated arguments of arbitrary type. + procedure = new Procedure({"proc_on_arbitrary_arbitrary"}, + {types_->get_int64(), + {ARG_TYPE_ARBITRARY, ARG_TYPE_ARBITRARY}, + /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // Add a procedure with one repeated argument. + procedure = new Procedure( + {"proc_on_rep"}, {types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::REPEATED}}, + /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // Add a procedure with one optional argument. + procedure = new Procedure( + {"proc_on_opt"}, {types_->get_int64(), + {{types_->get_int64(), FunctionArgumentType::OPTIONAL}}, + /*context_id=*/-1}); + catalog_->AddOwnedProcedure(procedure); + + // These sample procedures are named 'proc_on_' with one argument of + // type that returns a bool. + AddProcedureWithArgumentType("bool", types_->get_bool()); + AddProcedureWithArgumentType("int32", types_->get_int32()); + AddProcedureWithArgumentType("int64", types_->get_int64()); + AddProcedureWithArgumentType("uint32", types_->get_uint32()); + AddProcedureWithArgumentType("uint64", types_->get_uint64()); + AddProcedureWithArgumentType("float", types_->get_float()); + AddProcedureWithArgumentType("double", types_->get_double()); + AddProcedureWithArgumentType("date", types_->get_date()); + AddProcedureWithArgumentType("timestamp", types_->get_timestamp()); + AddProcedureWithArgumentType("string", types_->get_string()); + + // Add a procedure with named_lambda. + { + FunctionArgumentType named_lambda = FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1, + FunctionArgumentTypeOptions().set_argument_name("named_lambda", + kNamedOnly)); + auto procedure = std::make_unique( + std::vector{"proc_on_named_lambda"}, + FunctionSignature{types_->get_int64(), + {ARG_TYPE_ANY_1, named_lambda}, + /*context_id=*/-1}); + catalog_->AddOwnedProcedure(std::move(procedure)); + } +} + +void SampleCatalogImpl::LoadConstants() { + // Load constants that are owned by 'catalog_'. + std::unique_ptr int64_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"TestConstantInt64"}, + Value::Int64(1L), &int64_constant)); + catalog_->AddOwnedConstant(int64_constant.release()); + std::unique_ptr string_constant; + ZETASQL_CHECK_OK( + SimpleConstant::Create(std::vector{"TestConstantString"}, + Value::String("foo"), &string_constant)); + catalog_->AddOwnedConstant(string_constant.release()); + + std::unique_ptr string_constant_nonstandard_name; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"Test Constant-String"}, + Value::String("foo bar"), &string_constant_nonstandard_name)); + catalog_->AddOwnedConstant(string_constant_nonstandard_name.release()); + + // Load a constant that is not owned by 'catalog_'. + const ProtoType* const proto_type = + GetProtoType(zetasql_test__::KitchenSinkPB::descriptor()); + absl::Cord text_proto = absl::Cord("int64_key_1: 1, int64_key_2: -999"); + + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"TestConstantProto"}, + Value::Proto(proto_type, text_proto), + &owned_constant_)); + catalog_->AddConstant(owned_constant_.get()); + + // Load a constant that conflicts with a table. + const StructType* table_struct_type; + ZETASQL_CHECK_OK(types_->MakeStructType({{"key", types_->get_int32()}}, + &table_struct_type)); + std::unique_ptr table_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"NameConflictTable"}, + Value::Struct(table_struct_type, {Value::Int32(-3456)}), + &table_constant)); + catalog_->AddOwnedConstant(table_constant.release()); + + // Load a constant that conflicts with a value table. + std::unique_ptr value_table_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"Int32ValueTable"}, + Value::Int32(3), &value_table_constant)); + catalog_->AddOwnedConstant(value_table_constant.release()); + + // Load a constant that conflicts with a type. + std::unique_ptr type_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"NameConflictType"}, + Value::Bool(false), &type_constant)); + catalog_->AddOwnedConstant(type_constant.release()); + + // Load a constant that conflicts with zero-argument functions. + std::unique_ptr zero_argument_function_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"sort_count"}, + Value::Int64(4), + &zero_argument_function_constant)); + catalog_->AddOwnedConstant(zero_argument_function_constant.release()); + + std::unique_ptr + zero_argument_function_constant_with_optional_parentheses; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"CURRENT_DATE"}, Value::Int64(4), + &zero_argument_function_constant_with_optional_parentheses)); + catalog_->AddOwnedConstant( + zero_argument_function_constant_with_optional_parentheses.release()); + + // Load a constant that conflicts with a multi-argument function. + std::unique_ptr multi_argument_function_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"concat"}, + Value::Int64(5), + &multi_argument_function_constant)); + catalog_->AddOwnedConstant(multi_argument_function_constant.release()); + + // Load a constant that conflicts with a zero-argument TVF. + std::unique_ptr zero_argument_tvf_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"tvf_no_args"}, + Value::Int64(6), + &zero_argument_tvf_constant)); + catalog_->AddOwnedConstant(zero_argument_tvf_constant.release()); + + // Load a constant that conflicts with a multi-argument TVF. + std::unique_ptr multi_argument_tvf_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"tvf_exactly_1_int64_arg"}, Value::Int64(7), + &multi_argument_tvf_constant)); + catalog_->AddOwnedConstant(multi_argument_tvf_constant.release()); + + // Load a constant that conflicts with a zero-argument procedure. + // The multi-argument case is handled in the nested catalog. + std::unique_ptr constant; + ZETASQL_CHECK_OK( + SimpleConstant::Create({"proc_no_args"}, Value::Bool(true), &constant)); + catalog_->AddOwnedConstant(constant.release()); + + // Load a constant that conflicts with a catalog. + const StructType* nested_struct_type; + ZETASQL_CHECK_OK(types_->MakeStructType( + {{"a", types_->get_int32()}, {"b", types_->get_int64()}}, + &nested_struct_type)); + const StructType* catalog_struct_type; + ZETASQL_CHECK_OK( + types_->MakeStructType({{"a", types_->get_int32()}, + {"nested_catalog_catalog", nested_struct_type}, + {"TestConstantBool", types_->get_bool()}}, + &catalog_struct_type)); + std::unique_ptr catalog_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"nested_catalog_with_catalog"}, + Value::Struct(catalog_struct_type, + {Value::Int32(-3456), + Value::Struct(nested_struct_type, + {Value::Int32(-3434), Value::Int64(4333)}), + Value::Bool(false)}), + &catalog_constant)); + catalog_->AddOwnedConstant(catalog_constant.release()); + + // Load a constant that conflicts with an expression column in standalone + // expression resolution. + std::unique_ptr standalone_expression_constant; + ZETASQL_CHECK_OK( + SimpleConstant::Create(std::vector{"column_KitchenSink"}, + Value::Int64(8), &standalone_expression_constant)); + catalog_->AddOwnedConstant(standalone_expression_constant.release()); + + // Load a constant with a name that resembles a system variable. + std::unique_ptr sysvar1_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"@@sysvar1"}, + Value::Int64(8), &sysvar1_constant)); + catalog_->AddOwnedConstant(sysvar1_constant.release()); + + std::unique_ptr sysvar2_constant; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"@@sysvar2"}, + Value::Int64(8), &sysvar2_constant)); + catalog_->AddOwnedConstant(sysvar2_constant.release()); + + // Script variables are managed by the ScriptExecutor. Eventually, they get + // put into the catalog as constants. For testing, we'll add some "variables" + // here. + std::unique_ptr string_variable_foo; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"string_variable_foo"}, + Value::String("string_variable_foo_value"), &string_variable_foo)); + catalog_->AddOwnedConstant(std::move(string_variable_foo)); + + std::unique_ptr string_variable_bar; + ZETASQL_CHECK_OK(SimpleConstant::Create( + std::vector{"string_variable_bar"}, + Value::String("string_variable_bar_value"), &string_variable_bar)); + catalog_->AddOwnedConstant(std::move(string_variable_bar)); + + std::unique_ptr int_variable_foo; + ZETASQL_CHECK_OK(SimpleConstant::Create(std::vector{"int_variable_foo"}, + Value::Int32(4), &int_variable_foo)); + catalog_->AddOwnedConstant(std::move(int_variable_foo)); +} + +void SampleCatalogImpl::LoadConnections() { + auto connection1 = std::make_unique("connection1"); + auto connection2 = std::make_unique("connection2"); + auto connection3 = std::make_unique("connection3"); + auto nested_connection = + std::make_unique("NestedConnection"); + auto default_connection = + std::make_unique("$connection_default"); + owned_connections_[connection1->Name()] = std::move(connection1); + owned_connections_[connection2->Name()] = std::move(connection2); + owned_connections_[connection3->Name()] = std::move(connection3); + owned_connections_[default_connection->Name()] = + std::move(default_connection); + // This connection properly should be ONLY added to the nested catalog (e.g. + // skipped in the loop below). But, due to how lookup works with nested + // catalogs in simple_catalog currently, it will fail if it's not in the top- + // level catalog as well. + owned_connections_[nested_connection->Name()] = std::move(nested_connection); + for (auto it = owned_connections_.begin(); it != owned_connections_.end(); + ++it) { + catalog_->AddConnection(it->second.get()); + } +} + +void SampleCatalogImpl::LoadSequences() { + auto sequence1 = std::make_unique("sequence1"); + auto sequence2 = std::make_unique("sequence2"); + owned_sequences_[sequence1->Name()] = std::move(sequence1); + owned_sequences_[sequence2->Name()] = std::move(sequence2); + for (auto it = owned_sequences_.begin(); it != owned_sequences_.end(); ++it) { + catalog_->AddSequence(it->second.get()); + } +} + +void SampleCatalogImpl::AddOwnedTable(SimpleTable* table) { + catalog_->AddOwnedTable(absl::WrapUnique(table)); + zetasql_base::InsertOrDie(&tables_, table->Name(), table); +} + +void SampleCatalogImpl::LoadWellKnownLambdaArgFunctions() { + const Type* int64_type = types_->get_int64(); + const Type* bool_type = types_->get_bool(); + + // Models ARRAY_FILTER + auto function = std::make_unique( + "fn_array_filter", "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_ARRAY_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, bool_type)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, FUNCTION<->BOOL>) -> >", + function->GetSignature(0)->DebugString()); + function->AddSignature( + {ARG_ARRAY_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1, int64_type}, bool_type)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, FUNCTION<(, INT64)->BOOL>) -> >", + function->GetSignature(1)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Models ARRAY_TRANSFORM + function = std::make_unique("fn_array_transform", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_ARRAY_TYPE_ANY_2, + {ARG_ARRAY_TYPE_ANY_1, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_2)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, FUNCTION<->>) -> >", + function->GetSignature(0)->DebugString()); + function->AddSignature({ARG_ARRAY_TYPE_ANY_2, + {ARG_ARRAY_TYPE_ANY_1, + FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1, int64_type}, ARG_TYPE_ANY_2)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, FUNCTION<(, INT64)->>) -> >", + function->GetSignature(1)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + function = std::make_unique("fn_fp_array_sort", "sample_functions", + Function::SCALAR); + function->AddSignature({ARG_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, + FunctionArgumentType::Lambda( + {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1}, int64_type)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, FUNCTION<(, )->INT64>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Models REDUCE function, which takes an input array, an initial state and a + // function to run over each element with the current state to produce the + // final state. + function = std::make_unique("fn_fp_array_reduce", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_2, + {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_2, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_2, ARG_TYPE_ANY_1}, + ARG_TYPE_ANY_2)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, , FUNCTION<(, )->>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); +} + +void SampleCatalogImpl::LoadContrivedLambdaArgFunctions() { + const Type* int64_type = types_->get_int64(); + const Type* string_type = types_->get_string(); + const Type* bool_type = types_->get_bool(); + + // Demonstrate having to get common super type for two different concrete type + // for a single template type. + auto function = std::make_unique( + "fn_fp_T_T_LAMBDA", "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_1, + {ARG_TYPE_ANY_1, ARG_TYPE_ANY_1, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, bool_type)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(, , FUNCTION<->BOOL>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // fn_fp_ArrayT_T is provided here to show current behavior to make it easier + // for reader to understand fn_fp_ArrayT_T_LAMBDA. + function = std::make_unique("fn_fp_ArrayT_T", "sample_functions", + Function::SCALAR); + function->AddSignature({ARG_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_1}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, ) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Demostrate case where we don't have common super type for T1, due to + // ARRAY. + function = std::make_unique("fn_fp_ArrayT_T_LAMBDA", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_1, + {ARG_ARRAY_TYPE_ANY_1, ARG_TYPE_ANY_1, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, bool_type)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(>, , FUNCTION<->BOOL>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Demonstrate that lambda argument type inference conflict with final + // concrete type of templated type influenced by lambda body type. + function = std::make_unique("fn_fp_T_LAMBDA_RET_T", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_1, + {ARG_TYPE_ANY_1, + FunctionArgumentType::Lambda({ARG_TYPE_ANY_1}, ARG_TYPE_ANY_1)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(, FUNCTION<->>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + const auto named_required_format_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentTypeOptions().set_argument_name( + "format_string", kPositionalOrNamed)); + // Signature with lambda and named argument before lambda. + function = std::make_unique("fn_fp_named_then_lambda", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_1, + {named_required_format_arg, + FunctionArgumentType::Lambda({int64_type}, ARG_TYPE_ANY_1)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(STRING format_string, FUNCTION>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Signature with lambda and named argument after lambda. + function = std::make_unique("fn_fp_lambda_then_named", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_1, + {FunctionArgumentType::Lambda({int64_type}, ARG_TYPE_ANY_1), + named_required_format_arg}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(FUNCTION>, STRING format_string) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Signature with lambda and repeated arguments after lambda. + const auto repeated_arg = + FunctionArgumentType(types_->get_int64(), FunctionArgumentType::REPEATED); + function = std::make_unique("fn_fp_lambda_then_repeated", + "sample_functions", Function::SCALAR); + function->AddSignature( + {ARG_TYPE_ANY_1, + {FunctionArgumentType::Lambda({int64_type}, ARG_TYPE_ANY_1), + repeated_arg}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(FUNCTION>, repeated INT64) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + // Signature with lambda and repeated arguments before lambda. + function = std::make_unique("fn_fp_repeated_arg_then_lambda", + "sample_functions", Function::SCALAR); + function->AddSignature({ARG_TYPE_ANY_1, + {repeated_arg, FunctionArgumentType::Lambda( + {int64_type}, ARG_TYPE_ANY_1)}, + /*context_id=*/-1}); + ABSL_CHECK_EQ("(repeated INT64, FUNCTION>) -> ", + function->GetSignature(0)->DebugString()); + catalog_->AddOwnedFunction(function.release()); + + AddFunction( + "fn_fp_repeated_arg_then_lambda_string", Function::SCALAR, + {SignatureBuilder() + .AddArg(ArgBuilder().Repeated().String()) + .AddArg(FunctionArgumentType::Lambda({string_type}, ARG_TYPE_ANY_1)) + .Returns(ArgBuilder().T1()) + .Build()}); + /* + // Signature with lambda and repeated arguments before lambda. + function = std::make_unique("fn_fp_repeated_arg_then_lambda_string", + "sample_functions", Function::SCALAR); + const auto repeated_string_arg = FunctionArgumentType( + types_->get_string(), FunctionArgumentType::REPEATED); + + function->AddSignature( + {ARG_TYPE_ANY_1, + {repeated_string_arg, + FunctionArgumentType::Lambda({string_type}, ARG_TYPE_ANY_1)}}); + catalog_->AddOwnedFunction(function.release()); + */ +} + +void SampleCatalogImpl::AddSqlDefinedFunction( + absl::string_view name, FunctionSignature signature, + const std::vector& argument_names, + absl::string_view function_body_sql, + const LanguageOptions& language_options) { + AnalyzerOptions analyzer_options; + analyzer_options.set_language(language_options); + ABSL_CHECK_EQ(argument_names.size(), signature.arguments().size()); + for (int i = 0; i < argument_names.size(); ++i) { + ABSL_CHECK_NE(signature.argument(i).type(), nullptr); + ZETASQL_CHECK_OK(analyzer_options.AddExpressionColumn( + argument_names[i], signature.argument(i).type())); + } + std::unique_ptr analyzer_output; + ZETASQL_CHECK_OK(AnalyzeExpressionForAssignmentToType( + function_body_sql, analyzer_options, catalog_.get(), + catalog_->type_factory(), signature.result_type().type(), + &analyzer_output)); + std::unique_ptr function; + ZETASQL_CHECK_OK(SQLFunction::Create(name, FunctionEnums::SCALAR, {signature}, + /*function_options=*/{}, + analyzer_output->resolved_expr(), argument_names, + /*aggregate_expression_list=*/{}, + /*parse_resume_location=*/{}, &function)); + catalog_->AddOwnedFunction(function.release()); + sql_object_artifacts_.emplace_back(std::move(analyzer_output)); +} + +// Add a SQL function to catalog starting from a full create_function +// statement. +void SampleCatalogImpl::AddSqlDefinedFunctionFromCreate( + absl::string_view create_function, const LanguageOptions& language_options, + bool inline_sql_functions, + std::optional function_options) { + // Ensure the language options used allow CREATE FUNCTION + LanguageOptions language = language_options; + language.AddSupportedStatementKind(RESOLVED_CREATE_FUNCTION_STMT); + language.EnableLanguageFeature(FEATURE_V_1_3_INLINE_LAMBDA_ARGUMENT); + language.EnableLanguageFeature(FEATURE_V_1_1_WITH_ON_SUBQUERY); + language.EnableLanguageFeature(FEATURE_TEMPLATE_FUNCTIONS); + language.EnableLanguageFeature(FEATURE_CREATE_AGGREGATE_FUNCTION); + language.EnableLanguageFeature(FEATURE_V_1_1_HAVING_IN_AGGREGATE); + language.EnableLanguageFeature( + FEATURE_V_1_1_NULL_HANDLING_MODIFIER_IN_AGGREGATE); + AnalyzerOptions analyzer_options; + analyzer_options.set_language(language); + analyzer_options.set_enabled_rewrites(/*rewrites=*/{}); + analyzer_options.enable_rewrite(REWRITE_INLINE_SQL_FUNCTIONS, + inline_sql_functions); + analyzer_options.enable_rewrite(REWRITE_INLINE_SQL_UDAS, + inline_sql_functions); + sql_object_artifacts_.emplace_back(); + ZETASQL_CHECK_OK(AddFunctionFromCreateFunction( + create_function, analyzer_options, /*allow_persistent_function=*/true, + function_options, sql_object_artifacts_.back(), *catalog_, *catalog_)); +} + +void SampleCatalogImpl::LoadSqlFunctions( + const LanguageOptions& language_options) { + LoadScalarSqlFunctions(language_options); + LoadScalarSqlFunctionsFromStandardModule(language_options); + LoadDeepScalarSqlFunctions(language_options); + LoadScalarSqlFunctionTemplates(language_options); + LoadAggregateSqlFunctions(language_options); +} + +void SampleCatalogImpl::LoadScalarSqlFunctions( + const LanguageOptions& language_options) { + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION NullaryPi() RETURNS FLOAT64 AS (3.141597); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION NullaryWithSubquery() + AS (EXISTS (SELECT 1 FROM UNNEST([1,2,3]) AS e WHERE e = 2)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION NullaryWithCommonTableExpression() + AS (EXISTS (WITH t AS (SELECT [1,2,3] AS arr) + SELECT 1 FROM t, t.arr as e WHERE e = 2)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION NullaryWithLambda() + AS (ARRAY_INCLUDES([1, 2, 3], e -> e = 2)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION NullaryWithSqlFunctionCallPreInlined() + AS (NullaryPi()); )", + language_options, /*inline_sql_functions=*/true); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION NullaryWithSqlFunctionCallNotPreInlined() + AS (NullaryPi()); )", + language_options, /*inline_sql_functions=*/false); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION UnaryIncrement(a INT64) AS ( a + 1 ); )", + language_options); + + // Creating the function using AnalyzeExpressionForAssignmentToType results in + // a function body expression with ResolvedExpressionColumn where arguments + // are referenced instead of a ResolvedArgumentRef. That appears to be done + // in several catalogs, so we want to test that case too. + const Type* int64_type = types_->get_int64(); + FunctionSignature int_int = {int64_type, + {int64_type}, + /*context_id=*/static_cast(0)}; + AddSqlDefinedFunction("UnaryIncrementRefArg", int_int, {"a"}, "a + 1", + language_options); +} + +void SampleCatalogImpl::LoadScalarSqlFunctionsFromStandardModule( + const LanguageOptions& language_options) { + // Function from sql/modules/math_utils/math_utils.sqlm + // References each of its arguments more than once. + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION FloorDiv(n INT64, d INT64) RETURNS INT64 + AS ( DIV(n, d) - IF(n < 0 AND MOD(n, d) != 0, 1, 0) ); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION IgnoresArg(a INT64) AS (1); )", language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION ReferencesArgsInsideSubquery(a INT64, b INT64) + AS ((SELECT a + b)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION ReferencesArgInsideCte(a INT64) + AS ((WITH t AS (SELECT a AS c) SELECT c FROM t)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION ReferencesArgOutsideCte(a INT64) + AS ((WITH t AS (SELECT 1 AS c) SELECT c + a FROM t)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION ReferencesArgInsideLambda(a INT64) + AS (ARRAY_TRANSFORM([1, 2, 3], e->e = a)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION scalar_function_definer_rights() SQL SECURITY DEFINER + AS ((SELECT COUNT(*) FROM KeyValue)); )", + language_options, + /*inline_sql_functions=*/true); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION stable_array_sort(arr ANY TYPE) + AS ((SELECT ARRAY_AGG(e ORDER BY e, off) + FROM UNNEST(arr) AS e WITH OFFSET off)); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE FUNCTION template_with_typed_arg( + arg_any ANY TYPE, arg_string STRING) + AS (arg_any IS NULL OR arg_string IS NULL); + )sql", + language_options); +} + +void SampleCatalogImpl::LoadDeepScalarSqlFunctions( + const LanguageOptions& language_options) { + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION CallsPi0() AS (NullaryPi());)", language_options, + /*inline_sql_functions=*/false); + + for (int i = 0; i < 25; ++i) { + AddSqlDefinedFunctionFromCreate( + absl::StrCat("CREATE FUNCTION CallsPi", i + 1, "() AS (CallsPi", i, + "());"), + language_options, /*inline_sql_functions=*/false); + } +} + +void SampleCatalogImpl::LoadScalarSqlFunctionTemplates( + const LanguageOptions& language_options) { + // This function is logically equivalent to ARRAY_REVERSE + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION REVERSE_ARRAY(input_arr ANY TYPE) + AS (IF (input_arr IS NULL, + NULL, + ARRAY(SELECT e + FROM UNNEST(input_arr) AS e WITH OFFSET + ORDER BY OFFSET desc))); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION TimesTwo(arg ANY TYPE) AS (arg + arg); )", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"( CREATE FUNCTION SUM_DOUBLE_ARRAY(input_arr ANY TYPE) + AS ((SELECT SUM(e) + FROM UNNEST(ARRAY_TRANSFORM(input_arr, e->TimesTwo(e))) e));)", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql(CREATE TEMP FUNCTION b290673529(thing_id ANY TYPE) RETURNS BOOL + AS ( + thing_id IN (SELECT thing_id FROM (SELECT 1 AS thing_id)) + ); + )sql", + language_options, /*inline_sql_functions=*/false); +} + +void SampleCatalogImpl::LoadAggregateSqlFunctions( + const LanguageOptions& language_options) { + AddSqlDefinedFunctionFromCreate( + R"sql( CREATE AGGREGATE FUNCTION NotAggregate() AS (1 + 1);)sql", + language_options); + + // This function provides some open-box testing of the inliner in that it + // enables a test to ensure columns internal to the function body are properly + // re-mapped and don't result in column id collisions in the rewritten + // query if the function is called twice. + AddSqlDefinedFunctionFromCreate( + R"sql( CREATE AGGREGATE FUNCTION NotAggregateInternalColumn() AS ( + (SELECT a + a FROM (SELECT 1 AS a)) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( CREATE AGGREGATE FUNCTION CountStar() AS (COUNT(*));)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( CREATE AGGREGATE FUNCTION CallsCountStar() AS (CountStar());)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION NotAggregateArgs( + a INT64 NOT AGGREGATE + ) AS ( + a + a + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION SumOfNotAggregateArg( + not_agg_arg INT64 NOT AGGREGATE + ) AS ( + SUM(not_agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION ExpressionOutsideSumOfNotAggregate( + not_agg_arg INT64 NOT AGGREGATE + ) AS ( + not_agg_arg + SUM(not_agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION ExpressionInsideSumOfNotAggregate( + not_agg_arg INT64 NOT AGGREGATE + ) AS ( + SUM(not_agg_arg + not_agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION SumOfAggregateArgs( + agg_arg INT64 + ) AS ( + SUM(agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION SumExpressionOfAggregateArgs( + agg_arg INT64 + ) AS ( + SUM(agg_arg + agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION ExprOutsideSumExpressionOfAggregateArgs( + agg_arg INT64 + ) AS ( + 1 + SUM(agg_arg + agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION ExprOutsideAndInsideSum( + agg_arg INT64, + not_agg_arg INT64 NOT AGGREGATE + ) AS ( + not_agg_arg + SUM(not_agg_arg + agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaWithHavingMax( + agg_arg STRING, + another_agg_arg INT64 + ) AS ( + ARRAY_AGG(agg_arg HAVING MAX another_agg_arg) + );)sql", + language_options); + + // TODO: Add example with ARRAY_AGG( ... ORDER BY ... ) + // after that is enabled in UDA bodies. + + // TODO: Add example with WITH GROUP ROWS( ... ) after that + // is enabled in UDA bodies. + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaTemplateWithHavingMax( + agg_arg ANY TYPE + ) AS ( + ARRAY_AGG(agg_arg.a HAVING MAX agg_arg.b) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaTemplateWithIgnoreNulls( + agg_arg ANY TYPE + ) AS ( + STRUCT ( + ARRAY_AGG(agg_arg IGNORE NULLS) AS ignore_nulls, + ARRAY_AGG(agg_arg RESPECT NULLS) AS respect_nulls, + ARRAY_AGG(agg_arg) AS whatever_nulls + ) + );)sql", + language_options); + + // A token UDA with LIMIT in it. We can't do ORDER BY, so the use of LIMIT + // is ... questionable. + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaTemplateWithLimitZero( + agg_arg ANY TYPE + ) AS ( + STRUCT ( + ARRAY_AGG(agg_arg LIMIT 0) AS ignore_nulls + ) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaCallingAnotherUda( + agg_arg ANY TYPE + ) AS ( + CountStar() - COUNT(agg_arg.a) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaCallingAnotherUdaWithNonAggregateArgs( + agg_arg INT64, + non_agg_arg ANY TYPE NOT AGGREGATE + ) AS ( + ExprOutsideAndInsideSum(agg_arg + 1, non_agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaSafeCallingAnotherUdaWithNonAggregateArgs( + agg_arg INT64, + non_agg_arg ANY TYPE NOT AGGREGATE + ) AS ( + SAFE.ExprOutsideAndInsideSum(agg_arg + 1, non_agg_arg) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaInvokingUdf( + agg_arg ANY TYPE + ) AS ( + ExprOutsideAndInsideSum(TimesTwo(agg_arg), 3) + );)sql", + language_options); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaInvokingSafeUdf( + agg_arg ANY TYPE + ) AS ( + ExprOutsideAndInsideSum(SAFE.TimesTwo(agg_arg), 3) + );)sql", + language_options); + + FunctionOptions aggregate_calling_clauses_enabled; + aggregate_calling_clauses_enabled + // No need to test clamped between modifier since it is hard-coded to only + // work on functions with specific names. + .set_supports_distinct_modifier(true) + .set_supports_having_modifier(true) + .set_supports_null_handling_modifier(true) + .set_supports_limit(true) + .set_supports_order_by(true) + .set_uses_upper_case_sql_name(false); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaConcreteWithOptionalCallingClausesEnabled( + agg_arg INT64 + ) AS ( NULL ); + )sql", + language_options, /*inline_sql_functions=*/false, + aggregate_calling_clauses_enabled); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaTemplateWithOptionalCallingClausesEnabled( + agg_arg ANY TYPE + ) AS ( NULL ); + )sql", + language_options, /*inline_sql_functions=*/false, + aggregate_calling_clauses_enabled); + + AddSqlDefinedFunctionFromCreate( + R"sql( + CREATE AGGREGATE FUNCTION UdaInlinedOnCreate(x INT64) AS ( + SumExpressionOfAggregateArgs(x) + );)sql", + language_options, /*inline_sql_functions=*/true); +} + +void SampleCatalogImpl::ForceLinkProtoTypes() { + google::protobuf::LinkMessageReflection(); +} + +} // namespace zetasql diff --git a/zetasql/testdata/sample_catalog_impl.h b/zetasql/testdata/sample_catalog_impl.h new file mode 100644 index 000000000..27e0aec97 --- /dev/null +++ b/zetasql/testdata/sample_catalog_impl.h @@ -0,0 +1,282 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_TESTDATA_SAMPLE_CATALOG_IMPL_H_ +#define ZETASQL_TESTDATA_SAMPLE_CATALOG_IMPL_H_ + +#include +#include +#include +#include +#include + +#include "zetasql/public/analyzer_output.h" +#include "zetasql/public/builtin_function_options.h" +#include "zetasql/public/function.h" +#include "zetasql/public/function_signature.h" +#include "zetasql/public/language_options.h" +#include "zetasql/public/simple_catalog.h" +#include "zetasql/public/type.h" +#include "zetasql/resolved_ast/resolved_ast.h" +#include "absl/container/node_hash_map.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor_database.h" + +namespace zetasql { + +// This is the internal implementation of SampleCatalog. +// +// It differs from SampleCatalog in that it requires a call to +// LoadCatalogImpl to finish initialization, and that returns an absl::Status. +// +// The SampleCatalog class initializes itself in its constructor, and crashes +// if initialization fails. SampleCatalog is testonly. +class SampleCatalogImpl { + public: + // Constructor given an optional 'type_factory'. + // If 'type_factory' is specified then it must outlive this catalog. + // The catalog is not usable until LoadCatalogImpl is called. + explicit SampleCatalogImpl(TypeFactory* type_factory = nullptr); + + // This initializes SampleCatalogImpl and returns a Status. + // In SampleCatalog, this is called by the constructor and will + // check-fail on error. + // Here, it returns errors in some cases, but still has many check-fail cases. + absl::Status LoadCatalogImpl( + const ZetaSQLBuiltinFunctionOptions& builtin_function_options); + + SampleCatalogImpl(const SampleCatalogImpl&) = delete; + SampleCatalogImpl& operator=(const SampleCatalogImpl&) = delete; + virtual ~SampleCatalogImpl(); + + SimpleCatalog* catalog() { return catalog_.get(); } + + TypeFactory* type_factory() { return types_; } + + // Useful for configuring EvaluatorTableIterators for tables in the catalog. + SimpleTable* GetTableOrDie(absl::string_view name); + absl::StatusOr GetTable(absl::string_view name); + + private: + std::unique_ptr alt_descriptor_database_; + std::unique_ptr alt_descriptor_pool_; + std::unique_ptr ambiguous_has_descriptor_pool_; + std::unique_ptr internal_type_factory_; + std::unique_ptr catalog_; + TypeFactory* types_; // Not owned. + + // Returns a copy of `options` that supplies default types for function + // signatures that expect them. This can override map entries in + // the original `options`'s `supplied_argument_types`. + ZetaSQLBuiltinFunctionOptions LoadDefaultSuppliedTypes( + const ZetaSQLBuiltinFunctionOptions& options); + void LoadCatalogBuiltins( + const ZetaSQLBuiltinFunctionOptions& builtin_function_options); + absl::Status LoadTypes(); + absl::Status LoadTables(); + void LoadProtoTables(); + void LoadViews(const LanguageOptions& language_options); + void LoadNestedCatalogs(); + void AddFunctionWithArgumentType(std::string type_name, const Type* arg_type); + + // Creates and adds the Function to the catalog. + // This performs some basic validation. + // The group used is 'sample_functions'. + const Function* AddFunction( + absl::string_view name, Function::Mode mode, + std::vector function_signatures, + FunctionOptions function_options = {}); + + void LoadFunctionsWithStructArgs(); + // Do not add more functions to `LoadFunctions` and `LoadFunctions2`, use + // `RegisterForSampleCatalog` instead. + void LoadFunctions(); + void LoadFunctions2(); + // Use `RegisterForSampleCatalog` in the impl file to register a lambda which + // will add a catalog object - `LoadAllRegisteredCatalogChanges` calls all + // registered lambdas to add the objects to the catalog. + void LoadAllRegisteredCatalogChanges(); + void LoadExtendedSubscriptFunctions(); + void LoadFunctionsWithDefaultArguments(); + void LoadTemplatedSQLUDFs(); + + // Loads several table-valued functions into the sample catalog. For a full + // list of the signatures added, please see the beginning of the method + // definition. LoadTableValuedFunctions() has gotten so large that we have to + // split it up in order to avoid lint warnings. + void LoadTableValuedFunctions1(); + void LoadTableValuedFunctions2(); + void LoadTableValuedFunctionsWithEvaluators(); + void LoadTVFWithExtraColumns(); + void LoadConnectionTableValuedFunctions(); + void LoadDescriptorTableValuedFunctions(); + void LoadTableValuedFunctionsWithDeprecationWarnings(); + + // Add a SQL table function to catalog starting from a full create table + // function statement. + void AddSqlDefinedTableFunctionFromCreate( + absl::string_view create_table_function, + const LanguageOptions& language_options, + absl::string_view user_id_column = ""); + void LoadNonTemplatedSqlTableValuedFunctions( + const LanguageOptions& language_options); + void LoadTemplatedSQLTableValuedFunctions(); + void LoadTableValuedFunctionsWithAnonymizationUid(); + + void AddProcedureWithArgumentType(std::string type_name, + const Type* arg_type); + void LoadProcedures(); + void LoadConstants(); + void LoadConnections(); + void LoadSequences(); + // Load signatures for well known functional programming functions for example + // FILTER, TRANSFORM, REDUCE. + void LoadWellKnownLambdaArgFunctions(); + // Contrived signatures are loaded in order to demonstrate the behavior of + // lambda signature matching and resolving for unusual cases. + // This include: + // * Using lambda with repeated arguments. + // * Using lambda with named arguments. + // * Possible signatures that could result in type inference failure for + // various combinations of templated lambda arguments and other arguments. + void LoadContrivedLambdaArgFunctions(); + + void AddOwnedTable(SimpleTable* table); + absl::Status AddGeneratedColumnToTable( + std::string column_name, std::vector expression_columns, + std::string generated_expr, SimpleTable* table); + + // Add a SQLFunction to catalog_ with a SQL expression as the function body. + void AddSqlDefinedFunction(absl::string_view name, + FunctionSignature signature, + const std::vector& argument_names, + absl::string_view function_body_sql, + const LanguageOptions& language_options); + // Add a SQL function to catalog starting from a full create_function + // statement. + void AddSqlDefinedFunctionFromCreate( + absl::string_view create_function, + const LanguageOptions& language_options, bool inline_sql_functions = true, + std::optional function_options = std::nullopt); + + void LoadSqlFunctions(const LanguageOptions& language_options); + + // Helpers for LoadSqlFunctions so that its both logically broken up and + // so that its less troublesome for dbg build stacks. + void LoadScalarSqlFunctions(const LanguageOptions& language_options); + void LoadScalarSqlFunctionsFromStandardModule( + const LanguageOptions& language_options); + void LoadDeepScalarSqlFunctions(const LanguageOptions& language_options); + void LoadScalarSqlFunctionTemplates(const LanguageOptions& language_options); + void LoadAggregateSqlFunctions(const LanguageOptions& language_options); + + // This can be used force linking of a proto for the generated_pool. + // This may be required if a proto is referenced in file-based tests + // (such as analyzer test), but not otherwise directly linked. + // We don't force linking the entire test_schema since we may need + // to test this partial-linkage in other contexts (and it's expensive). + // Note, this function isn't actually called. But it _does_ need to be + // defined in the class to ensure it can't be pruned. + // This is a all weird linker magic. + void ForceLinkProtoTypes(); + + const ProtoType* GetProtoType(const google::protobuf::Descriptor* descriptor); + const EnumType* GetEnumType(const google::protobuf::EnumDescriptor* descriptor); + + const ArrayType* int32array_type_; + const ArrayType* int64array_type_; + const ArrayType* uint32array_type_; + const ArrayType* uint64array_type_; + const ArrayType* bytes_array_type_; + const ArrayType* bool_array_type_; + const ArrayType* float_array_type_; + const ArrayType* double_array_type_; + const ArrayType* date_array_type_; + const ArrayType* string_array_type_; + const ArrayType* timestamp_array_type_; + const ArrayType* proto_array_type_; + const ArrayType* struct_array_type_; + const ArrayType* json_array_type_; + const ArrayType* numeric_array_type_; + const ArrayType* bignumeric_array_type_; + const ArrayType* interval_array_type_; + + const EnumType* enum_TestEnum_; + const EnumType* enum_AnotherTestEnum_; + const EnumType* enum_TestEnumWithAnnotations_; + const ProtoType* proto_KitchenSinkPB_; + const ProtoType* proto_MessageWithKitchenSinkPB_; + const ProtoType* proto_CivilTimeTypesSinkPB_; + const ProtoType* proto_TestExtraPB_; + const ProtoType* proto_abPB_; + const ProtoType* proto_bcPB_; + + const ProtoType* proto_EmptyMessage_; + const ProtoType* proto3_KitchenSinkPB_; + const ProtoType* proto3_MessageWithInvalidMap_; + const ProtoType* proto_ambiguous_has_; + const ProtoType* proto_field_formats_proto_; + const ProtoType* proto_MessageWithMapField_; + const ProtoType* proto_approx_distance_function_options_; + + // STRUCT + const StructType* struct_type_; + // STRUCT> + const StructType* nested_struct_type_; + // STRUCT>> + const StructType* doubly_nested_struct_type_; + // STRUCT, + // z ARRAY>> + const StructType* struct_with_array_field_type_; + // STRUCT + const StructType* struct_with_one_field_type_; + // STRUCT> + const StructType* struct_with_kitchen_sink_type_; + // STRUCT>> + const StructType* struct_of_array_of_struct_with_kitchen_sink_type_; + + const Type* int32map_type_; + const Type* int64map_type_; + const Type* bytesmap_type_; + + const SimpleTable* key_value_table_; + + // A constant to load. Owned by this catalog to get coverage for + // SimpleCatalog::AddConstant(). + std::unique_ptr owned_constant_; + + // Pointers are owned by 'catalog_'. + absl::node_hash_map tables_; + + // Connections owned by this catalog. + std::unordered_map> + owned_connections_; + + // Sequences owned by this catalog. + std::unordered_map> + owned_sequences_; + + // Manages the lifetime of ResolvedAST objects for SQL defined statements like + // views, SQL functions, column expressions, or SQL TVFs. + std::vector> sql_object_artifacts_; +}; + +} // namespace zetasql + +#endif // ZETASQL_TESTDATA_SAMPLE_CATALOG_IMPL_H_ diff --git a/zetasql/testdata/sample_catalog_test.cc b/zetasql/testdata/sample_catalog_test.cc index 4bed1c795..8c41c880a 100644 --- a/zetasql/testdata/sample_catalog_test.cc +++ b/zetasql/testdata/sample_catalog_test.cc @@ -17,6 +17,12 @@ #include "zetasql/testdata/sample_catalog.h" #include "zetasql/base/testing/status_matchers.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/function.h" +#include "zetasql/public/language_options.h" +#include "zetasql/public/sql_view.h" +#include "zetasql/public/table_valued_function.h" +#include "zetasql/public/types/type_factory.h" #include "zetasql/resolved_ast/resolved_ast_enums.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/zetasql/testdata/sample_system_variables.cc b/zetasql/testdata/sample_system_variables.cc index c24a8edbd..3d3289a83 100644 --- a/zetasql/testdata/sample_system_variables.cc +++ b/zetasql/testdata/sample_system_variables.cc @@ -19,12 +19,14 @@ #include #include -#include "zetasql/public/analyzer.h" +#include "zetasql/public/analyzer_options.h" #include "zetasql/public/type.h" #include "zetasql/public/types/proto_type.h" #include "zetasql/testdata/test_schema.pb.h" +#include "zetasql/base/check.h" +#include "absl/log/log.h" +#include "absl/status/status.h" #include "absl/strings/str_join.h" -#include "zetasql/base/status.h" namespace zetasql { diff --git a/zetasql/testdata/sample_system_variables.h b/zetasql/testdata/sample_system_variables.h index 6869ca7c6..68bd07c1a 100644 --- a/zetasql/testdata/sample_system_variables.h +++ b/zetasql/testdata/sample_system_variables.h @@ -18,6 +18,7 @@ #define ZETASQL_TESTDATA_SAMPLE_SYSTEM_VARIABLES_H_ #include "zetasql/public/analyzer.h" +#include "zetasql/public/analyzer_options.h" #include "zetasql/public/type.h" namespace zetasql { diff --git a/zetasql/testdata/special_catalog.cc b/zetasql/testdata/special_catalog.cc index 9f8e16dec..4a77483c0 100644 --- a/zetasql/testdata/special_catalog.cc +++ b/zetasql/testdata/special_catalog.cc @@ -25,10 +25,11 @@ #include #include "zetasql/public/catalog.h" +#include "zetasql/public/simple_catalog.h" #include "zetasql/public/type.h" #include "zetasql/testdata/test_schema.pb.h" #include "absl/container/btree_set.h" -#include "absl/memory/memory.h" +#include "zetasql/base/check.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" diff --git a/zetasql/testing/test_function.cc b/zetasql/testing/test_function.cc index dfa6f4bfa..6d44e56e5 100644 --- a/zetasql/testing/test_function.cc +++ b/zetasql/testing/test_function.cc @@ -41,7 +41,7 @@ namespace zetasql { QueryParamsWithResult::QueryParamsWithResult( - const std::vector& arguments, + absl::Span arguments, const ValueConstructor& result, absl::Status status) : params_(ValueConstructor::ToValues(arguments)), ordinal_param_size_(arguments.size()), @@ -56,7 +56,7 @@ QueryParamsWithResult::QueryParamsWithResult( result_(result, status, float_margin_arg) {} QueryParamsWithResult::QueryParamsWithResult( - const std::vector& arguments, + absl::Span arguments, const ValueConstructor& result, absl::string_view error_substring) : params_(ValueConstructor::ToValues(arguments)), ordinal_param_size_(arguments.size()), @@ -67,7 +67,7 @@ QueryParamsWithResult::QueryParamsWithResult( } QueryParamsWithResult::QueryParamsWithResult( - const std::vector& arguments, + absl::Span arguments, const ValueConstructor& result, absl::StatusCode code) : params_(ValueConstructor::ToValues(arguments)), ordinal_param_size_(arguments.size()), @@ -202,10 +202,10 @@ std::vector InvertResults( return new_tests; } -FunctionTestCall::FunctionTestCall( - absl::string_view function_name, - const std::vector& arguments, - const ValueConstructor& result, FloatMargin float_margin) +FunctionTestCall::FunctionTestCall(absl::string_view function_name, + absl::Span arguments, + const ValueConstructor& result, + FloatMargin float_margin) : function_name(function_name), params(arguments, result, float_margin) {} FunctionTestCall::FunctionTestCall( diff --git a/zetasql/testing/test_function.h b/zetasql/testing/test_function.h index a172716c7..fb52faed6 100644 --- a/zetasql/testing/test_function.h +++ b/zetasql/testing/test_function.h @@ -82,7 +82,7 @@ class QueryParamsWithResult { }; // Constructs an instance that contains a single result. - QueryParamsWithResult(const std::vector& arguments, + QueryParamsWithResult(absl::Span arguments, const ValueConstructor& result, absl::Status status = absl::OkStatus()); @@ -91,10 +91,10 @@ class QueryParamsWithResult { FloatMargin float_margin_arg, absl::Status status = absl::OkStatus()); - QueryParamsWithResult(const std::vector& arguments, + QueryParamsWithResult(absl::Span arguments, const ValueConstructor& result, absl::StatusCode code); - QueryParamsWithResult(const std::vector& arguments, + QueryParamsWithResult(absl::Span arguments, const ValueConstructor& result, absl::string_view error_substring); @@ -224,7 +224,7 @@ struct FunctionTestCall { QueryParamsWithResult params; FunctionTestCall(absl::string_view function_name, - const std::vector& arguments, + absl::Span arguments, const ValueConstructor& result, FloatMargin float_margin = kExactFloatMargin); diff --git a/zetasql/tools/execute_query/BUILD b/zetasql/tools/execute_query/BUILD index 8667bd91e..e05247fae 100644 --- a/zetasql/tools/execute_query/BUILD +++ b/zetasql/tools/execute_query/BUILD @@ -49,6 +49,7 @@ cc_test( srcs = ["execute_query_tool_test.cc"], data = [ "testdata/KitchenSinkPB.textproto", + "testdata/execute_query_tool.test", "testdata/test.csv", ], deps = [ @@ -59,6 +60,7 @@ cc_test( "//zetasql/base/testing:status_matchers", "//zetasql/base/testing:zetasql_gtest_main", "//zetasql/public:analyzer_options", + "//zetasql/public:builtin_function_options", "//zetasql/public:catalog", "//zetasql/public:options_cc_proto", "//zetasql/public/types", @@ -73,6 +75,8 @@ cc_test( "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", "@com_google_absl//absl/types:span", + "@com_google_file_based_test_driver//file_based_test_driver", + "@com_google_file_based_test_driver//file_based_test_driver:test_case_options", "@com_google_protobuf//:protobuf", ], ) @@ -110,6 +114,7 @@ cc_binary( ":execute_query_loop", ":execute_query_prompt", ":execute_query_tool", + ":execute_query_web", ":execute_query_writer", ":homedir", "//zetasql/base", @@ -121,6 +126,7 @@ cc_binary( "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/flags:parse", "@com_google_absl//absl/functional:bind_front", + "@com_google_absl//absl/log:initialize", "@com_google_absl//absl/memory", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", @@ -331,6 +337,35 @@ cc_test( ], ) +cc_library( + name = "selectable_catalog", + srcs = ["selectable_catalog.cc"], + hdrs = ["selectable_catalog.h"], + deps = [ + "//zetasql/base:status", + "//zetasql/examples/tpch/catalog", + "//zetasql/public:builtin_function_options", + "//zetasql/public:catalog", + "//zetasql/public:language_options", + "//zetasql/public:simple_catalog", + "//zetasql/testdata:sample_catalog_impl", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "selectable_catalog_test", + srcs = ["selectable_catalog_test.cc"], + deps = [ + ":selectable_catalog", + "//zetasql/base/testing:status_matchers", + "//zetasql/base/testing:zetasql_gtest_main", + "//zetasql/public:catalog", + "@com_google_googletest//:gtest", + ], +) + cc_library( name = "execute_query_tool", srcs = [ @@ -344,6 +379,7 @@ cc_library( deps = [ ":execute_query_proto_writer", ":execute_query_writer", + ":selectable_catalog", ":simple_proto_evaluator_table_iterator", ":string_error_collector", "//zetasql/base", @@ -361,9 +397,15 @@ cc_library( "//zetasql/public:analyzer", "//zetasql/public:analyzer_options", "//zetasql/public:analyzer_output", + "//zetasql/public:builtin_function_options", "//zetasql/public:catalog", + "//zetasql/public:error_helpers", "//zetasql/public:evaluator", "//zetasql/public:evaluator_table_iterator", + "//zetasql/public:function", + "//zetasql/public:multi_catalog", + "//zetasql/public:parse_helpers", + "//zetasql/public:parse_resume_location", "//zetasql/public:simple_catalog", "//zetasql/public:simple_catalog_util", "//zetasql/public:sql_formatter", @@ -427,3 +469,72 @@ cc_test( "@com_google_googletest//:gtest", ], ) + +cc_library( + name = "execute_query_web_server", + hdrs = ["execute_query_web_server.h"], +) + +cc_library( + name = "execute_query_web_handler", + srcs = [ + "execute_query_web_handler.cc", + "execute_query_web_writer.cc", + ], + hdrs = [ + "execute_query_web_handler.h", + "execute_query_web_writer.h", + ], + deps = [ + ":execute_query_tool", + ":execute_query_writer", + ":output_query_result", + ":selectable_catalog", + "//zetasql/base:status", + "//zetasql/public:evaluator_table_iterator", + "//zetasql/public:value", + "//zetasql/resolved_ast", + "//zetasql/tools/execute_query/web:embedded_resources", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/types:span", + "@mstch", + ], +) + +cc_test( + name = "execute_query_web_handler_test", + srcs = ["execute_query_web_handler_test.cc"], + deps = [ + ":execute_query_web_handler", + "//zetasql/base/testing:zetasql_gtest_main", + "//zetasql/tools/execute_query/web:embedded_resources", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + "@com_google_googletest//:gtest", + ], +) + +cc_library( + name = "execute_query_web", + srcs = ["execute_query_web.cc"], + hdrs = ["execute_query_web.h"], + deps = [ + ":execute_query_web_handler", + ":execute_query_web_server", + "//zetasql/tools/execute_query/web:embedded_resources", + "@civetweb", + "@com_google_absl//absl/flags:flag", + "@com_google_absl//absl/log", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/synchronization", + ], +) diff --git a/zetasql/tools/execute_query/execute_query.cc b/zetasql/tools/execute_query/execute_query.cc index 9177ad0fb..f2c375237 100644 --- a/zetasql/tools/execute_query/execute_query.cc +++ b/zetasql/tools/execute_query/execute_query.cc @@ -17,6 +17,7 @@ // Tool for running a query against a Catalog constructed from various input // sources. Also serves as a demo of the PreparedQuery API. +#include #include #include #include @@ -33,10 +34,12 @@ #include "zetasql/tools/execute_query/execute_query_loop.h" #include "zetasql/tools/execute_query/execute_query_prompt.h" #include "zetasql/tools/execute_query/execute_query_tool.h" +#include "zetasql/tools/execute_query/execute_query_web.h" #include "zetasql/tools/execute_query/execute_query_writer.h" #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/functional/bind_front.h" +#include "absl/log/initialize.h" #include "absl/memory/memory.h" #include "absl/status/status.h" #include "absl/strings/str_format.h" @@ -55,40 +58,11 @@ ABSL_FLAG( "the query history stored in ~/%s.", kHistoryFileName)); +ABSL_FLAG(bool, web, false, "Run a local webserver to execute queries."); +ABSL_FLAG(int32_t, port, 8080, "Port to run the local webserver on."); + namespace zetasql { namespace { -absl::Status InitializeExecuteQueryConfig(ExecuteQueryConfig& config) { - // Prefer a default of maximum (this will be overwritten if an explicit - // flag value is passed). - config.mutable_analyzer_options() - .mutable_language() - ->EnableMaximumLanguageFeatures(); - config.mutable_analyzer_options() - .mutable_language() - ->SetSupportsAllStatementKinds(); - - // Used to pretty print error message in tandem with - // ExecuteQueryLoopPrintErrorHandler. - config.mutable_analyzer_options().set_error_message_mode( - ERROR_MESSAGE_WITH_PAYLOAD); - - config.mutable_analyzer_options() - .set_show_function_signature_mismatch_details(true); - - ZETASQL_RETURN_IF_ERROR(SetDescriptorPoolFromFlags(config)); - ZETASQL_RETURN_IF_ERROR(SetToolModeFromFlags(config)); - ZETASQL_RETURN_IF_ERROR(SetSqlModeFromFlags(config)); - ZETASQL_RETURN_IF_ERROR(SetLanguageOptionsFromFlags(config)); - ZETASQL_RETURN_IF_ERROR(SetAnalyzerOptionsFromFlags(config)); - ZETASQL_RETURN_IF_ERROR(SetEvaluatorOptionsFromFlags(config)); - ZETASQL_RETURN_IF_ERROR(AddTablesFromFlags(config)); - - ZETASQL_RETURN_IF_ERROR(config.mutable_catalog().AddBuiltinFunctionsAndTypes( - BuiltinFunctionOptions(config.analyzer_options().language()))); - ZETASQL_RETURN_IF_ERROR(SetQueryParametersFromFlags(config)); - - return absl::OkStatus(); -} absl::Status RunTool(const std::vector& args) { ExecuteQueryConfig config; @@ -102,6 +76,10 @@ absl::Status RunTool(const std::vector& args) { ABSL_LOG(QFATAL) << "Interactive mode is not implemented in this version"; } + if (absl::GetFlag(FLAGS_web)) { + return RunExecuteQueryWebServer(absl::GetFlag(FLAGS_port)); + } + const std::string sql = absl::StrJoin(args, " "); ExecuteQuerySingleInput prompt(sql, config); @@ -115,15 +93,20 @@ absl::Status RunTool(const std::vector& args) { int main(int argc, char* argv[]) { const char kUsage[] = "Usage: execute_query [--table_spec=] " - "{ --interactive | }\n"; + "{ --interactive | } { --web --port= }\n"; + std::vector args; { std::vector remaining_args = absl::ParseCommandLine(argc, argv); args.assign(remaining_args.cbegin() + 1, remaining_args.cend()); } + absl::InitializeLog(); + + bool args_needed = !absl::GetFlag(FLAGS_interactive); + args_needed = args_needed && !absl::GetFlag(FLAGS_web); - if (absl::GetFlag(FLAGS_interactive) != args.empty()) { + if (args_needed && args.empty()) { ABSL_LOG(QFATAL) << "\n" << kUsage << "Pass --help for a full list of flags.\n"; } diff --git a/zetasql/tools/execute_query/execute_query_tool.cc b/zetasql/tools/execute_query/execute_query_tool.cc index 1860a434c..6b42c889e 100644 --- a/zetasql/tools/execute_query/execute_query_tool.cc +++ b/zetasql/tools/execute_query/execute_query_tool.cc @@ -16,7 +16,7 @@ #include "zetasql/tools/execute_query/execute_query_tool.h" -#include +#include #include #include #include @@ -34,9 +34,16 @@ #include "zetasql/parser/parser.h" #include "zetasql/public/analyzer.h" #include "zetasql/public/analyzer_output.h" +#include "zetasql/public/builtin_function_options.h" #include "zetasql/public/catalog.h" +#include "zetasql/public/error_helpers.h" #include "zetasql/public/evaluator.h" #include "zetasql/public/evaluator_table_iterator.h" +#include "zetasql/public/function.h" +#include "zetasql/public/function_signature.h" +#include "zetasql/public/multi_catalog.h" +#include "zetasql/public/parse_helpers.h" +#include "zetasql/public/parse_resume_location.h" #include "zetasql/public/simple_catalog.h" #include "zetasql/public/simple_catalog_util.h" #include "zetasql/public/sql_formatter.h" @@ -48,7 +55,7 @@ #include "zetasql/resolved_ast/sql_builder.h" #include "zetasql/tools/execute_query/execute_query_proto_writer.h" #include "zetasql/tools/execute_query/execute_query_writer.h" -#include "absl/container/flat_hash_map.h" +#include "zetasql/tools/execute_query/selectable_catalog.h" #include "absl/container/flat_hash_set.h" #include "absl/flags/flag.h" #include "zetasql/base/check.h" @@ -85,6 +92,11 @@ ABSL_FLAG(std::vector, mode, {"execute"}, "\n 'execute' actually run the query and print the result. (not" " all functionality is supported)."); +ABSL_FLAG(std::string, catalog, "none", + absl::StrCat( + "The base catalog to use for looking up tables, etc.\nChoices:\n", + zetasql::GetSelectableCatalogDescriptionsForFlag())); + ABSL_FLAG(zetasql::internal::EnabledAstRewrites, enabled_ast_rewrites, zetasql::internal::EnabledAstRewrites{ .enabled_ast_rewrites = zetasql::internal::GetAllRewrites()}, @@ -102,6 +114,9 @@ ABSL_FLAG(zetasql::internal::EnabledAstRewrites, enabled_ast_rewrites, "\n Will enable all the default options plus ANONYMIZATION, but" " excluding flatten"); +ABSL_FLAG(bool, fold_literal_cast, true, + "Set the fold_literal_cast option in AnalyzerOptions"); + ABSL_FLAG(std::optional, enabled_language_features, std::nullopt, zetasql::internal::EnabledLanguageFeatures::kFlagDescription); @@ -113,7 +128,7 @@ ABSL_FLAG(bool, strict_name_resolution_mode, false, "Sets LanguageOptions::strict_resolution_mode."); ABSL_FLAG(bool, evaluator_scramble_undefined_orderings, false, - "When true, shuffle the order of rows in intermediate reults that " + "When true, shuffle the order of rows in intermediate results that " "are unordered."); ABSL_FLAG(std::string, table_spec, "", @@ -181,29 +196,11 @@ absl::Status SetToolModeFromFlags(ExecuteQueryConfig& config) { return absl::OkStatus(); } - static const auto* tool_mode_map = - new absl::flat_hash_map({ - {"parse", ToolMode::kParse}, - {"parser", ToolMode::kParse}, - {"unparse", ToolMode::kUnparse}, - {"unparser", ToolMode::kUnparse}, - {"resolve", ToolMode::kResolve}, - {"resolver", ToolMode::kResolve}, - {"analyze", ToolMode::kResolve}, - {"analyzer", ToolMode::kResolve}, - {"sql_builder", ToolMode::kUnAnalyze}, - {"sqlbuilder", ToolMode::kUnAnalyze}, - {"unanalyze", ToolMode::kUnAnalyze}, - {"unanalyzer", ToolMode::kUnAnalyze}, - {"unresolve", ToolMode::kUnAnalyze}, - {"unresolver", ToolMode::kUnAnalyze}, - {"explain", ToolMode::kExplain}, - {"execute", ToolMode::kExecute}, - }); - for (const auto& mode : modes) { - if (tool_mode_map->contains(absl::AsciiStrToLower(mode))) { - config.add_tool_mode(tool_mode_map->at(mode)); + std::optional tool_mode = + ExecuteQueryConfig::parse_tool_mode(mode); + if (tool_mode.has_value()) { + config.add_tool_mode(*tool_mode); } else { return zetasql_base::InvalidArgumentErrorBuilder() << "Invalid --mode: '" << mode << "'"; @@ -227,6 +224,12 @@ absl::Status SetSqlModeFromFlags(ExecuteQueryConfig& config) { } } +static absl::Status SetFoldLiteralCastFromFlags(ExecuteQueryConfig& config) { + config.mutable_analyzer_options().set_fold_literal_cast( + absl::GetFlag(FLAGS_fold_literal_cast)); + return absl::OkStatus(); +} + static absl::Status SetRewritersFromFlags(ExecuteQueryConfig& config) { config.mutable_analyzer_options().set_enabled_rewrites( absl::GetFlag(FLAGS_enabled_ast_rewrites).enabled_ast_rewrites); @@ -292,7 +295,7 @@ absl::Status SetDescriptorPoolFromFlags(ExecuteQueryConfig& config) { static absl::StatusOr GetProtoType( ExecuteQueryConfig& config, absl::string_view proto_name) { const zetasql::Type* type = nullptr; - if (!config.mutable_catalog().GetType(std::string(proto_name), &type).ok() || + if (!config.catalog()->FindType({std::string(proto_name)}, &type).ok() || type == nullptr) { return zetasql_base::NotFoundErrorBuilder() << "Unknown protocol buffer message: '" << proto_name << "'"; @@ -356,13 +359,24 @@ static absl::StatusOr> MakeTableFromTableSpec( } } +absl::Status ExecuteQueryConfig::SetCatalogFromString( + const std::string& value) { + ZETASQL_ASSIGN_OR_RETURN(SelectableCatalog * selectable_catalog, + FindSelectableCatalog(value)); + + ZETASQL_ASSIGN_OR_RETURN(Catalog * catalog, selectable_catalog->GetCatalog()); + SetBaseCatalog(catalog); + + return absl::OkStatus(); +} + absl::Status AddTablesFromFlags(ExecuteQueryConfig& config) { std::vector table_specs = absl::StrSplit(absl::GetFlag(FLAGS_table_spec), ',', absl::SkipEmpty()); for (const std::string& table_spec : table_specs) { ZETASQL_ASSIGN_OR_RETURN(auto table, MakeTableFromTableSpec(table_spec, config)); - config.mutable_catalog().AddOwnedTable(std::move(table)); + config.wrapper_catalog()->AddOwnedTable(std::move(table)); } return absl::OkStatus(); } @@ -410,6 +424,7 @@ absl::Status SetLanguageOptionsFromFlags(ExecuteQueryConfig& config) { } absl::Status SetAnalyzerOptionsFromFlags(ExecuteQueryConfig& config) { + ZETASQL_RETURN_IF_ERROR(SetFoldLiteralCastFromFlags(config)); return SetRewritersFromFlags(config); } @@ -430,9 +445,9 @@ absl::Status SetEvaluatorOptionsFromFlags(ExecuteQueryConfig& config) { absl::Status SetQueryParametersFromFlags(ExecuteQueryConfig& config) { ParameterValueMap parameters; std::string err; - if (!internal::ParseQueryParameterFlag( - absl::GetFlag(FLAGS_parameters), config.analyzer_options(), - &config.mutable_catalog(), ¶meters, &err)) { + if (!internal::ParseQueryParameterFlag(absl::GetFlag(FLAGS_parameters), + config.analyzer_options(), + config.catalog(), ¶meters, &err)) { return absl::InvalidArgumentError(err); } for (const auto& [name, value] : parameters) { @@ -444,13 +459,65 @@ absl::Status SetQueryParametersFromFlags(ExecuteQueryConfig& config) { return absl::OkStatus(); } -ExecuteQueryConfig::ExecuteQueryConfig() : catalog_("") {} +absl::Status InitializeExecuteQueryConfig(ExecuteQueryConfig& config) { + // Prefer a default of maximum (this will be overwritten if an explicit + // flag value is passed). + config.mutable_analyzer_options() + .mutable_language() + ->EnableMaximumLanguageFeatures(); + config.mutable_analyzer_options() + .mutable_language() + ->SetSupportsAllStatementKinds(); + + // Used to pretty print error message in tandem with + // ExecuteQueryLoopPrintErrorHandler. + config.mutable_analyzer_options().set_error_message_mode( + ERROR_MESSAGE_WITH_PAYLOAD); + + config.mutable_analyzer_options() + .set_show_function_signature_mismatch_details(true); + + ZETASQL_RETURN_IF_ERROR(SetDescriptorPoolFromFlags(config)); + ZETASQL_RETURN_IF_ERROR(SetToolModeFromFlags(config)); + ZETASQL_RETURN_IF_ERROR(SetSqlModeFromFlags(config)); + ZETASQL_RETURN_IF_ERROR(config.SetCatalogFromString(absl::GetFlag(FLAGS_catalog))); + ZETASQL_RETURN_IF_ERROR(SetLanguageOptionsFromFlags(config)); + ZETASQL_RETURN_IF_ERROR(SetAnalyzerOptionsFromFlags(config)); + ZETASQL_RETURN_IF_ERROR(SetEvaluatorOptionsFromFlags(config)); + ZETASQL_RETURN_IF_ERROR(AddTablesFromFlags(config)); + + ZETASQL_RETURN_IF_ERROR(config.builtins_catalog()->AddBuiltinFunctionsAndTypes( + BuiltinFunctionOptions(config.analyzer_options().language()))); + ZETASQL_RETURN_IF_ERROR(SetQueryParametersFromFlags(config)); + + return absl::OkStatus(); +} + +ExecuteQueryConfig::ExecuteQueryConfig() + : builtins_catalog_(""), wrapper_catalog_("") { + SetBaseCatalog(nullptr); // This also sets up the MultiCatalog. +} + +void ExecuteQueryConfig::SetBaseCatalog(Catalog* catalog) { + base_catalog_ = catalog; + + std::vector catalogs; + catalogs.push_back(&wrapper_catalog_); + if (base_catalog_ != nullptr) { + catalogs.push_back(base_catalog_); + } + catalogs.push_back(&builtins_catalog_); + + // The only case where this fails is if one of the catalogs is nullptr, + // which can't happen here. + ZETASQL_CHECK_OK(MultiCatalog::Create("", catalogs, &catalog_)); +} void ExecuteQueryConfig::SetDescriptorPool(const google::protobuf::DescriptorPool* pool) { ABSL_CHECK(descriptor_pool_ == nullptr) << __func__ << " can only be called once"; owned_descriptor_pool_.reset(); descriptor_pool_ = pool; - catalog_.SetDescriptorPool(pool); + wrapper_catalog_.SetDescriptorPool(pool); } void ExecuteQueryConfig::SetOwnedDescriptorPool( @@ -458,7 +525,7 @@ void ExecuteQueryConfig::SetOwnedDescriptorPool( ABSL_CHECK(descriptor_pool_ == nullptr) << __func__ << " can only be called once"; owned_descriptor_pool_ = std::move(pool); descriptor_pool_ = owned_descriptor_pool_.get(); - catalog_.SetDescriptorPool(descriptor_pool_); + wrapper_catalog_.SetDescriptorPool(descriptor_pool_); } void ExecuteQueryConfig::SetOwnedDescriptorDatabase( @@ -496,7 +563,7 @@ static absl::StatusOr RegisterMacro(absl::string_view sql, std::string macro_name = define_macro_statement->name()->GetAsString(); ZETASQL_RETURN_IF_ERROR( - writer.unparsed(absl::StrFormat("Macro registered: %s", macro_name))); + writer.log(absl::StrFormat("Macro registered: %s", macro_name))); return true; } @@ -517,16 +584,18 @@ static absl::StatusOr ExpandMacros( std::string expanded_sql = parser::macros::TokensToString(expansion_output.expanded_tokens); for (const absl::Status& warning : expansion_output.warnings) { - ZETASQL_RETURN_IF_ERROR( - writer.unparsed(absl::StrCat("Warning: ", warning.message()))); + ZETASQL_RETURN_IF_ERROR(writer.log(absl::StrCat("Warning: ", warning.message()))); + } + if (sql != expanded_sql) { + ZETASQL_RETURN_IF_ERROR(writer.log("Expanded SQL:")); + ZETASQL_RETURN_IF_ERROR(writer.log(expanded_sql)); } - ZETASQL_RETURN_IF_ERROR(writer.unparsed("Expanded SQL:")); - ZETASQL_RETURN_IF_ERROR(writer.unparsed(expanded_sql)); return expanded_sql; } static absl::StatusOr ParseSql( absl::string_view sql, const ExecuteQueryConfig& config, + ParseResumeLocation* parse_resume_location, bool* at_end_of_input, std::unique_ptr* parser_output) { ParserOptions parser_options; @@ -534,7 +603,10 @@ static absl::StatusOr ParseSql( switch (config.sql_mode()) { case SqlMode::kQuery: { parser_options.set_language_options(config.analyzer_options().language()); - ZETASQL_RETURN_IF_ERROR(ParseStatement(sql, parser_options, parser_output)); + std::string input(sql); + ZETASQL_RETURN_IF_ERROR(ParseNextStatement(parse_resume_location, parser_options, + parser_output, at_end_of_input)); + ZETASQL_RET_CHECK(at_end_of_input); root = (*parser_output)->statement(); break; } @@ -542,6 +614,8 @@ static absl::StatusOr ParseSql( parser_options.set_language_options(config.analyzer_options().language()); ZETASQL_RETURN_IF_ERROR(ParseExpression(sql, parser_options, parser_output)); root = (*parser_output)->expression(); + // Expressions are always just a single parsable item. + *at_end_of_input = true; break; } } @@ -580,16 +654,14 @@ static absl::StatusOr AnalyzeSql( case SqlMode::kQuery: { ZETASQL_RETURN_IF_ERROR(AnalyzeStatementFromParserAST( *ast->GetAsOrDie(), config.analyzer_options(), sql, - &config.mutable_catalog(), config.mutable_catalog().type_factory(), - analyzer_output)); + config.catalog(), config.type_factory(), analyzer_output)); resolved_node = (*analyzer_output)->resolved_statement(); break; } case SqlMode::kExpression: { ZETASQL_RETURN_IF_ERROR(AnalyzeExpressionFromParserAST( *ast->GetAsOrDie(), config.analyzer_options(), sql, - config.mutable_catalog().type_factory(), &config.mutable_catalog(), - analyzer_output)); + config.type_factory(), config.catalog(), analyzer_output)); resolved_node = (*analyzer_output)->resolved_expr(); break; } @@ -608,7 +680,7 @@ static absl::Status UnanalyzeQuery(const ResolvedNode* resolved_node, ExecuteQueryWriter& writer) { SQLBuilder::SQLBuilderOptions sql_builder_options; sql_builder_options.language_options = config.analyzer_options().language(); - sql_builder_options.catalog = &config.mutable_catalog(); + sql_builder_options.catalog = config.catalog(); SQLBuilder builder(sql_builder_options); ZETASQL_RETURN_IF_ERROR(builder.Process(*resolved_node)); @@ -618,16 +690,86 @@ static absl::Status UnanalyzeQuery(const ResolvedNode* resolved_node, return writer.unanalyze(formatted_sql); } -static absl::Status RegisterFunction(absl::string_view sql, - ExecuteQueryConfig& config, - ExecuteQueryWriter& writer) { - std::unique_ptr function_artifacts; - ZETASQL_RET_CHECK_OK(AddFunctionFromCreateFunction( - sql, config.analyzer_options(), - /*allow_persistent_function=*/true, /*function_options=*/std::nullopt, - function_artifacts, config.mutable_catalog())); - config.AddFunctionArtifacts(std::move(function_artifacts)); - return writer.unparsed("Function registered."); +// If `resolved_node` is a DDL CREATE statement, try to add the created +// object to the catalog. We do this even if not in execute mode since later +// statements may depend on these objects for analysis. +static absl::Status HandleDDL( + const ResolvedNode* resolved_node, ExecuteQueryConfig& config, + ExecuteQueryWriter& writer, std::unique_ptr* parser_output, + std::unique_ptr* analyzer_output, + bool* executed_as_ddl) { + const bool is_ctas = resolved_node->Is(); + + switch (resolved_node->node_kind()) { + case RESOLVED_CREATE_FUNCTION_STMT: { + const auto* stmt = resolved_node->GetAs(); + ZETASQL_ASSIGN_OR_RETURN(auto function, MakeFunctionFromCreateFunction(*stmt)); + + if (!config.wrapper_catalog()->AddOwnedFunctionIfNotPresent(&function)) { + return zetasql_base::InvalidArgumentErrorBuilder() << "Function already exists"; + } + ZETASQL_RETURN_IF_ERROR(writer.log("Function registered.")); + break; + } + case RESOLVED_CREATE_TABLE_FUNCTION_STMT: { + const ResolvedCreateTableFunctionStmt* stmt = + resolved_node->GetAs(); + stmt->create_scope(); // Mark accessed so TEMP can be ignored. + ZETASQL_ASSIGN_OR_RETURN(auto tvf, MakeTVFFromCreateTableFunction(*stmt)); + + if (!config.wrapper_catalog()->AddOwnedTableValuedFunctionIfNotPresent( + &tvf)) { + return zetasql_base::InvalidArgumentErrorBuilder() << "TVF already exists"; + } + ZETASQL_RETURN_IF_ERROR(writer.log("TVF registered.")); + break; + } + case RESOLVED_CREATE_TABLE_AS_SELECT_STMT: + case RESOLVED_CREATE_TABLE_STMT: { + const ResolvedCreateTableStmtBase* stmt = + resolved_node->GetAs(); + stmt->create_scope(); // Mark accessed so TEMP can be ignored. + + if (is_ctas) { + // Mark query-related fields accessed so we don't get an error. + // We should undo this if we start to support executing CTAS. + const auto* ctas = stmt->GetAs(); + for (auto& col : ctas->output_column_list()) { + col->MarkFieldsAccessed(); + } + ctas->query()->MarkFieldsAccessed(); + } + + ZETASQL_ASSIGN_OR_RETURN(auto table, MakeTableFromCreateTable(*stmt)); + + if (!is_ctas) { + // The table will return zero rows when queried. + table->SetContents({}); + } else { + // Execution will fail scanning tables created with CTAS because there's + // no data. To fix this, we could return the SimpleTable*, pass it to + // ExplainAndOrExecuteSql, and have that run the contained query and + // capture its result into this table. + } + + const std::string name = table->Name(); + if (!config.wrapper_catalog()->AddOwnedTableIfNotPresent( + name, std::move(table))) { + return zetasql_base::InvalidArgumentErrorBuilder() << "Table already exists"; + } + ZETASQL_RETURN_IF_ERROR(writer.log("Table registered.")); + break; + } + default: + // Not a DDL statement so we don't need AddFunctionArtifacts below. + return absl::OkStatus(); + } + + // Common code after handling any DDL CREATE successfully. + config.AddArtifacts(std::move(*parser_output), std::move(*analyzer_output)); + *executed_as_ddl = !is_ctas; + + return absl::OkStatus(); } static absl::Status ExplainAndOrExecuteSql(const ResolvedNode* resolved_node, @@ -640,7 +782,7 @@ static absl::Status ExplainAndOrExecuteSql(const ResolvedNode* resolved_node, PreparedQuery query{resolved_node->GetAs(), config.evaluator_options()}; ZETASQL_RETURN_IF_ERROR( - query.Prepare(config.analyzer_options(), &config.mutable_catalog())); + query.Prepare(config.analyzer_options(), config.catalog())); if (config.has_tool_mode(ToolMode::kExplain)) { ZETASQL_ASSIGN_OR_RETURN(std::string explain, query.ExplainAfterPrepare()); @@ -662,8 +804,8 @@ static absl::Status ExplainAndOrExecuteSql(const ResolvedNode* resolved_node, PreparedExpression expression{resolved_node->GetAs(), config.evaluator_options()}; - ZETASQL_RETURN_IF_ERROR(expression.Prepare(config.analyzer_options(), - &config.mutable_catalog())); + ZETASQL_RETURN_IF_ERROR( + expression.Prepare(config.analyzer_options(), config.catalog())); if (config.has_tool_mode(ToolMode::kExplain)) { ZETASQL_ASSIGN_OR_RETURN(const std::string explain, @@ -684,35 +826,194 @@ static absl::Status ExplainAndOrExecuteSql(const ResolvedNode* resolved_node, } } -absl::Status ExecuteQuery(absl::string_view sql, ExecuteQueryConfig& config, - ExecuteQueryWriter& writer) { - bool enable_macros = - config.sql_mode() == SqlMode::kQuery && - config.analyzer_options().language().LanguageFeatureEnabled( - FEATURE_V_1_4_SQL_MACROS); +// This is a rudimentary implementation of +// DESCRIBE [object_type] name; +// It can print a simple description of tables, functions and TVFs. +// If object_type is one of those, it'll print objects just of that type. +// If object_type isn't requested, it'll print found objects of all types. +// +// This is implemented in execute_query rather than in the reference +// implementation because DESCRIBE is engine-defined behavior and does not +// have a specified correct answer that could be compliance tested. +static absl::Status ExecuteDescribe(const ResolvedNode* resolved_node, + ExecuteQueryConfig& config, + ExecuteQueryWriter& writer) { + ZETASQL_RET_CHECK_EQ(resolved_node->node_kind(), RESOLVED_DESCRIBE_STMT); + const ResolvedDescribeStmt* describe = + resolved_node->GetAs(); + + const std::string object_type = + absl::AsciiStrToLower(describe->object_type()); + const bool find_any_object_type = object_type.empty(); + bool called_any_find = false; + bool found_any_object = false; + + const std::vector& name_path = describe->name_path(); + ZETASQL_RETURN_IF_ERROR(describe->CheckFieldsAccessed()); + + std::ostringstream output; + Catalog* catalog = config.catalog(); + const ProductMode product_mode = + config.analyzer_options().language().product_mode(); + + // Common block around the lookup for each object type, to handle errors and + // track whether we found anything so far. + // Call it like + // IF_FOUND(catalog->FindObject(...)) { + // // handle the object + // } +#define IF_FOUND(FindCall) \ + absl::Status find_status = (FindCall); \ + called_any_find = true; \ + bool found_this_object = false; \ + if (absl::IsNotFound(find_status)) { \ + if (!find_any_object_type) { \ + return find_status; \ + } else { \ + /* Do nothing and continue to other object types. */ \ + } \ + } else if (!find_status.ok()) { \ + return find_status; \ + } else { \ + if (found_any_object) { \ + output << std::endl; \ + } \ + found_any_object = true; \ + found_this_object = true; \ + } \ + if (found_this_object) + + if (find_any_object_type || object_type == "table") { + const Table* table; + IF_FOUND(catalog->FindTable(name_path, &table)) { + output << "Table: " << table->FullName() + << (table->IsValueTable() ? " (value table)" : "") << std::endl; + int first_column = 0; + if (table->IsValueTable()) { + ZETASQL_RET_CHECK_GE(table->NumColumns(), 1); + output << "Row type: " + << table->GetColumn(0)->GetType()->ShortTypeName(product_mode) + << std::endl; + ++first_column; + } + if (table->NumColumns() > first_column) { + output << "Columns:" << std::endl; + size_t max_column_length = 0; + for (int idx = first_column; idx < table->NumColumns(); ++idx) { + max_column_length = + std::max(max_column_length, table->GetColumn(idx)->Name().size()); + } + for (int idx = first_column; idx < table->NumColumns(); ++idx) { + const Column* column = table->GetColumn(idx); + output << absl::StrFormat(" %-*s ", max_column_length, + column->Name()) + << column->GetType()->ShortTypeName(product_mode) + << (column->IsPseudoColumn() ? " (pseudo-column)" : "") + << std::endl; + } + } + // This could include more of the other metadata that's available on + // Table or Column. It currently just has the main schema details. + } + } - std::string expanded_sql; // Defined outside of if() to stay in scope. - if (enable_macros) { - // macro registration - if (config.has_tool_mode(ToolMode::kExecute)) { - ZETASQL_ASSIGN_OR_RETURN(bool macro_registered, - RegisterMacro(sql, config, writer)); - if (macro_registered) { - return absl::OkStatus(); + if (find_any_object_type || object_type == "function") { + const Function* function; + IF_FOUND(catalog->FindFunction(name_path, &function)) { + ZETASQL_RET_CHECK(function != nullptr); + + // This function generates signatures in one string split by "; ". + // There isn't a method that returns them in a vector. + // We split it because we want to print them on separate lines. + int num_signatures; + const std::string signatures = + function->GetSupportedSignaturesUserFacingText( + config.analyzer_options().language(), + FunctionArgumentType::NamePrintingStyle::kIfNotPositionalOnly, + &num_signatures, + /*print_template_details=*/true); + const std::vector split_signatures = + absl::StrSplit(signatures, "; "); + + // This generates something like: + // Function XYZ + // Signature: XYZ(STRING) -> STRING + // Signature: XYZ(INT64) -> INT64 + // There are many ways to improve this but code for formatting signatures + // is scattered and complex. + output << function->QualifiedSQLName(/*capitalize_qualifier=*/true) + << std::endl; + ZETASQL_RET_CHECK_EQ(split_signatures.size(), function->NumSignatures()); + for (int i = 0; i < split_signatures.size(); ++i) { + // The method above doesn't include the return type so we add it. + output << "Signature: " << split_signatures[i] << " -> " + << function->GetSignature(i)->result_type().UserFacingName( + product_mode, /*print_template_details=*/true) + << std::endl; } } + } - // macro expansion - ZETASQL_ASSIGN_OR_RETURN(expanded_sql, ExpandMacros(sql, config, writer)); - sql = expanded_sql; + if (find_any_object_type || object_type == "tvf") { + const TableValuedFunction* tvf; + IF_FOUND(catalog->FindTableValuedFunction(name_path, &tvf)) { + ZETASQL_RET_CHECK(tvf != nullptr); + ZETASQL_RET_CHECK_EQ(tvf->NumSignatures(), 1); + const FunctionArgumentType& result_type = + tvf->GetSignature(0)->result_type(); + + output << "Table-valued function: " + << tvf->GetSupportedSignaturesUserFacingText( + config.analyzer_options().language(), + /*print_template_and_name_details=*/true) + << " -> " + // Result types with and without table schemas are available + // from different methods. + << (result_type.options().has_relation_input_schema() + ? result_type.options() + .relation_input_schema() + .GetSQLDeclaration(product_mode) + : result_type.UserFacingName( + product_mode, + /*print_template_details=*/true)) + << std::endl; + } + } + + if (!called_any_find) { + return zetasql_base::InvalidArgumentErrorBuilder() + << "Unsupported object type " << object_type; + } + if (!found_any_object) { + return zetasql_base::InvalidArgumentErrorBuilder() << "Object not found"; } + ZETASQL_RETURN_IF_ERROR(writer.executed(output.str())); + + return absl::OkStatus(); +} + +// Process the next statement from script, updating `parse_result_location` +// and `at_end_of_input` on success. +static absl::Status ExecuteOneQuery(absl::string_view script, + ExecuteQueryConfig& config, + ExecuteQueryWriter& writer, + ParseResumeLocation* parse_resume_location, + bool* at_end_of_input) { std::unique_ptr parser_output; - ZETASQL_ASSIGN_OR_RETURN(const ASTNode* ast, ParseSql(sql, config, &parser_output)); + absl::StatusOr ast = ParseSql( + script, config, parse_resume_location, at_end_of_input, &parser_output); + if (!ast.ok()) { + return MaybeUpdateErrorFromPayload( + ErrorMessageOptions{ + .mode = ErrorMessageMode::ERROR_MESSAGE_MULTI_LINE_WITH_CARET, + .attach_error_location_payload = false}, + script, ast.status()); + } if (config.has_tool_mode(ToolMode::kParse) || config.has_tool_mode(ToolMode::kUnparse)) { - ZETASQL_RETURN_IF_ERROR(WriteParsedAndOrUnparsedAst(ast, config, writer)); + ZETASQL_RETURN_IF_ERROR(WriteParsedAndOrUnparsedAst(*ast, config, writer)); } if (!IsAnalysisRequired(config)) { @@ -721,7 +1022,7 @@ absl::Status ExecuteQuery(absl::string_view sql, ExecuteQueryConfig& config, std::unique_ptr analyzer_output; ZETASQL_ASSIGN_OR_RETURN(const ResolvedNode* resolved_node, - AnalyzeSql(sql, ast, config, writer, &analyzer_output)); + AnalyzeSql(script, *ast, config, writer, &analyzer_output)); ZETASQL_RET_CHECK_NE(resolved_node, nullptr); const ExecuteQueryConfig::ExamineResolvedASTCallback callback = @@ -730,19 +1031,84 @@ absl::Status ExecuteQuery(absl::string_view sql, ExecuteQueryConfig& config, ZETASQL_RETURN_IF_ERROR(callback(resolved_node)); } - if (resolved_node->node_kind() == RESOLVED_CREATE_FUNCTION_STMT) { - ZETASQL_RETURN_IF_ERROR(RegisterFunction(sql, config, writer)); - } + // This transfers ownership of `parser_output` and `analyzer_output` and + // clears the copies here if this creates any DDL objects. + bool executed_as_ddl = false; + ZETASQL_RETURN_IF_ERROR(HandleDDL(resolved_node, config, writer, &parser_output, + &analyzer_output, &executed_as_ddl)); if (config.has_tool_mode(ToolMode::kUnAnalyze)) { ZETASQL_RETURN_IF_ERROR(UnanalyzeQuery(resolved_node, config, writer)); } - if ((resolved_node->node_kind() == RESOLVED_QUERY_STMT || - resolved_node->IsExpression()) && - (config.has_tool_mode(ToolMode::kExplain) || - config.has_tool_mode(ToolMode::kExecute))) { - ZETASQL_RETURN_IF_ERROR(ExplainAndOrExecuteSql(resolved_node, config, writer)); + if (config.has_tool_mode(ToolMode::kExplain) || + config.has_tool_mode(ToolMode::kExecute)) { + if (resolved_node->node_kind() == RESOLVED_QUERY_STMT || + resolved_node->IsExpression()) { + ZETASQL_RETURN_IF_ERROR(ExplainAndOrExecuteSql(resolved_node, config, writer)); + } else if (config.has_tool_mode(ToolMode::kExplain)) { + return absl::InvalidArgumentError( + absl::StrCat("The statement ", resolved_node->node_kind_string(), + " is not supported for explanation.")); + } else { + ZETASQL_RET_CHECK(config.has_tool_mode(ToolMode::kExecute)); + if (resolved_node->node_kind() == RESOLVED_DESCRIBE_STMT) { + ZETASQL_RETURN_IF_ERROR(ExecuteDescribe(resolved_node, config, writer)); + } else if (executed_as_ddl) { + // Execution handled in HandleDDL above. + } else { + return absl::InvalidArgumentError( + absl::StrCat("The statement ", resolved_node->node_kind_string(), + " is not supported for execution.")); + } + } + } + + return absl::OkStatus(); +} + +absl::Status ExecuteQuery(absl::string_view sql, ExecuteQueryConfig& config, + ExecuteQueryWriter& writer) { + std::string script(sql); + + // Expand macros. This currently happens for the whole script at once, + // handling the script if it is just one DEFINE MACRO statement. + // Otherwise, it applies expansion on the whole script, failing execution if + // macro expansion fails. + // We don't have statement-at-a-time macro definition and expansion + // implemented. + bool enable_macros = + config.sql_mode() == SqlMode::kQuery && + config.analyzer_options().language().LanguageFeatureEnabled( + FEATURE_V_1_4_SQL_MACROS); + + if (enable_macros) { + // macro registration + if (config.has_tool_mode(ToolMode::kExecute)) { + ZETASQL_ASSIGN_OR_RETURN(bool macro_registered, + RegisterMacro(script, config, writer)); + if (macro_registered) { + return absl::OkStatus(); + } + } + + // macro expansion + ZETASQL_ASSIGN_OR_RETURN(script, ExpandMacros(script, config, writer)); + } + + auto parse_resume_location = ParseResumeLocation::FromString(script); + bool at_end_of_input = false; + + bool is_first = true; + while (!at_end_of_input) { + ZETASQL_RETURN_IF_ERROR(writer.StartStatement(is_first)); + is_first = false; + + // This currently stops execution on the first statement with an error. + // Recovering after finding the next parse_resume_location requires some + // new code. + ZETASQL_RETURN_IF_ERROR(ExecuteOneQuery(script, config, writer, + &parse_resume_location, &at_end_of_input)); } return absl::OkStatus(); diff --git a/zetasql/tools/execute_query/execute_query_tool.h b/zetasql/tools/execute_query/execute_query_tool.h index 1d7ca20e6..15680144a 100644 --- a/zetasql/tools/execute_query/execute_query_tool.h +++ b/zetasql/tools/execute_query/execute_query_tool.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -30,16 +31,22 @@ #include "zetasql/common/options_utils.h" #include "zetasql/parser/macros/macro_expander.h" #include "zetasql/public/analyzer_options.h" +#include "zetasql/public/catalog.h" #include "zetasql/public/evaluator.h" +#include "zetasql/public/multi_catalog.h" #include "zetasql/public/simple_catalog.h" #include "zetasql/public/types/proto_type.h" +#include "zetasql/public/types/type_factory.h" #include "zetasql/resolved_ast/resolved_node.h" #include "zetasql/tools/execute_query/execute_query_writer.h" +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/flags/declare.h" #include "absl/flags/flag.h" +#include "zetasql/base/check.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/ascii.h" #include "absl/strings/string_view.h" namespace zetasql { @@ -88,6 +95,58 @@ class ExecuteQueryConfig { bool has_tool_mode(ToolMode tool_mode) const { return tool_modes_.contains(tool_mode); } + const absl::flat_hash_set& tool_modes() const { + return tool_modes_; + } + void set_tool_modes(const absl::flat_hash_set& tool_modes) { + tool_modes_ = tool_modes; + } + + // Returns the tool mode if the mode string matches one of the tool modes. + static std::optional parse_tool_mode(absl::string_view mode) { + static const auto* tool_mode_map = + new absl::flat_hash_map({ + {"parse", ToolMode::kParse}, + {"parser", ToolMode::kParse}, + {"unparse", ToolMode::kUnparse}, + {"unparser", ToolMode::kUnparse}, + {"resolve", ToolMode::kResolve}, + {"resolver", ToolMode::kResolve}, + {"analyze", ToolMode::kResolve}, + {"analyzer", ToolMode::kResolve}, + {"sql_builder", ToolMode::kUnAnalyze}, + {"sqlbuilder", ToolMode::kUnAnalyze}, + {"unanalyze", ToolMode::kUnAnalyze}, + {"unanalyzer", ToolMode::kUnAnalyze}, + {"unresolve", ToolMode::kUnAnalyze}, + {"unresolver", ToolMode::kUnAnalyze}, + {"explain", ToolMode::kExplain}, + {"execute", ToolMode::kExecute}, + }); + + std::string mode_lower{absl::AsciiStrToLower(mode)}; + if (tool_mode_map->contains(mode_lower)) { + return tool_mode_map->at(mode_lower); + } + return std::nullopt; + } + + // Returns the name of the tool mode. + static absl::string_view tool_mode_name(ToolMode tool_mode) { + static const auto* tool_mode_names = + new absl::flat_hash_map({ + {ToolMode::kParse, "parse"}, + {ToolMode::kUnparse, "unparse"}, + {ToolMode::kResolve, "analyze"}, + {ToolMode::kUnAnalyze, "unanalyze"}, + {ToolMode::kExplain, "explain"}, + {ToolMode::kExecute, "execute"}, + }); + + ABSL_CHECK(tool_mode_names->contains(tool_mode)) + << "Unknown tool mode: " << static_cast(tool_mode); + return tool_mode_names->at(tool_mode); + } void set_sql_mode(SqlMode sql_mode) { sql_mode_ = sql_mode; } SqlMode sql_mode() const { return sql_mode_; } @@ -108,9 +167,22 @@ class ExecuteQueryConfig { return query_parameter_values_; } - // Defaults matches SimpleCatalog(""). - SimpleCatalog& mutable_catalog() { return catalog_; } - const SimpleCatalog& catalog() const { return catalog_; } + // This is the Catalog to use for lookups. It's a MultiCatalog containing + // the wrapper_catalog, base_catalog and builtins_catalog. + Catalog* catalog() { return catalog_.get(); } + + // Set the base catalog, which is used to find tables, custom functions, etc. + // It doesn't need to include builtin functions since those are provided by + // the builtins_catalog. + // nullptr is allowed if there is no base catalog. + void SetBaseCatalog(Catalog* catalog); + + Catalog* base_catalog() { return base_catalog_; } + SimpleCatalog* builtins_catalog() { return &builtins_catalog_; } + SimpleCatalog* wrapper_catalog() { return &wrapper_catalog_; } + + // A TypeFactory that can be used for creating tables for this request. + TypeFactory* type_factory() { return &type_factory_; } using ExamineResolvedASTCallback = std::function; @@ -126,6 +198,8 @@ class ExecuteQueryConfig { examine_resolved_ast_callback_ = std::move(callback); } + absl::Status SetCatalogFromString(const std::string& value); + // Set the google::protobuf::DescriptorPool to use when resolving types. // The DescriptorPool can only be set once and cannot be changed. void SetDescriptorPool(const google::protobuf::DescriptorPool* pool); @@ -147,9 +221,10 @@ class ExecuteQueryConfig { const std::list& macro_sources() const { return macro_sources_; } std::list& mutable_macro_sources() { return macro_sources_; } - void AddFunctionArtifacts( - std::unique_ptr function_artifact) { - function_artifacts_.push_back(std::move(function_artifact)); + void AddArtifacts(std::unique_ptr parser_output, + std::unique_ptr analyzer_output) { + parser_artifacts_.push_back(std::move(parser_output)); + analyzer_artifacts_.push_back(std::move(analyzer_output)); } private: @@ -158,7 +233,19 @@ class ExecuteQueryConfig { absl::flat_hash_set tool_modes_ = {ToolMode::kExecute}; SqlMode sql_mode_ = SqlMode::kQuery; AnalyzerOptions analyzer_options_; - SimpleCatalog catalog_; + + // The effective Catalog is a MultiCatalog with + // wrapper_catalog - any tables or types added based on flags + // base_catalog - the Catalog of tables, etc from SelectableCatalogs. + // builtins_catalog - the Catalog providing built-in functions, set up + // based on LanguageOptions inferred from config. + SimpleCatalog builtins_catalog_; + Catalog* base_catalog_ = nullptr; // Not owned, may be nullptr. + SimpleCatalog wrapper_catalog_; + std::unique_ptr catalog_; + + TypeFactory type_factory_; + EvaluatorOptions evaluator_options_; ParameterValueMap query_parameter_values_; const google::protobuf::DescriptorPool* descriptor_pool_ = nullptr; @@ -168,7 +255,9 @@ class ExecuteQueryConfig { // std::list, not a vector, because we need stability. The entries in // `macro_catalog_` have string_views into these sources. std::list macro_sources_; - std::vector> function_artifacts_; + // These are used to keep parsing and analysis artifacts alive. + std::vector> parser_artifacts_; + std::vector> analyzer_artifacts_; }; absl::Status SetToolModeFromFlags(ExecuteQueryConfig& config); @@ -205,6 +294,9 @@ absl::Status SetEvaluatorOptionsFromFlags(ExecuteQueryConfig& config); // Set query parameters in analyzer options as well as for use in the evaluator. absl::Status SetQueryParametersFromFlags(ExecuteQueryConfig& config); +// Initialize an ExecuteQueryConfig with default values and values from flags. +absl::Status InitializeExecuteQueryConfig(ExecuteQueryConfig& config); + // Execute the query according to `config`. `config` is logically const, but due // to ZetaSQL calling conventions related to Catalog objects, must be // non-const. @@ -218,7 +310,9 @@ ABSL_DECLARE_FLAG(std::vector, mode); ABSL_DECLARE_FLAG(zetasql::internal::EnabledAstRewrites, enabled_ast_rewrites); ABSL_DECLARE_FLAG(std::string, product_mode); +ABSL_DECLARE_FLAG(std::string, catalog); ABSL_DECLARE_FLAG(bool, strict_name_resolution_mode); +ABSL_DECLARE_FLAG(bool, fold_literal_cast); ABSL_DECLARE_FLAG(std::string, sql_mode); ABSL_DECLARE_FLAG(std::string, table_spec); ABSL_DECLARE_FLAG(std::string, descriptor_pool); diff --git a/zetasql/tools/execute_query/execute_query_tool_test.cc b/zetasql/tools/execute_query/execute_query_tool_test.cc index b54257a2f..3107ed3b9 100644 --- a/zetasql/tools/execute_query/execute_query_tool_test.cc +++ b/zetasql/tools/execute_query/execute_query_tool_test.cc @@ -28,6 +28,7 @@ #include "google/protobuf/text_format.h" #include "zetasql/base/testing/status_matchers.h" #include "zetasql/public/analyzer_options.h" +#include "zetasql/public/builtin_function_options.h" #include "zetasql/public/catalog.h" #include "zetasql/public/options.pb.h" #include "zetasql/public/types/type_factory.h" @@ -44,9 +45,12 @@ #include "absl/flags/reflection.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "absl/strings/strip.h" #include "absl/types/span.h" +#include "file_based_test_driver/file_based_test_driver.h" +#include "file_based_test_driver/test_case_options.h" namespace zetasql { namespace { @@ -55,6 +59,7 @@ using zetasql_test__::EmptyMessage; using zetasql_test__::KitchenSinkPB; using testing::HasSubstr; using testing::IsEmpty; +using testing::MatchesRegex; using testing::Not; using testing::NotNull; using zetasql_base::testing::StatusIs; @@ -69,13 +74,22 @@ absl::Status ExecuteQuery(absl::string_view sql, ExecuteQueryConfig& config, } TEST(ExecuteQueryDefaults, AllRewritesEnabledByDefault) { + absl::FlagSaver fs; ExecuteQueryConfig config; ZETASQL_ASSERT_OK(SetAnalyzerOptionsFromFlags(config)); EXPECT_EQ(config.analyzer_options().enabled_rewrites(), internal::GetAllRewrites()); } +TEST(ExecuteQueryDefaults, FoldLiteralCastEnabledByDefault) { + absl::FlagSaver fs; + ExecuteQueryConfig config; + ZETASQL_ASSERT_OK(SetAnalyzerOptionsFromFlags(config)); + EXPECT_TRUE(config.analyzer_options().fold_literal_cast()); +} + TEST(SetToolModeFromFlags, SingleToolMode) { + absl::FlagSaver fs; auto CheckFlag = [](std::string name, ToolMode expected_mode) { absl::SetFlag(&FLAGS_mode, std::vector{name}); ExecuteQueryConfig config; @@ -91,6 +105,7 @@ TEST(SetToolModeFromFlags, SingleToolMode) { } TEST(SetToolModeFromFlags, MultipleToolModes) { + absl::FlagSaver fs; auto CheckFlag = [](const std::vector& names, const absl::flat_hash_set& expected_modes, const absl::flat_hash_set& unexpected_modes) { @@ -127,6 +142,7 @@ TEST(SetToolModeFromFlags, MultipleToolModes) { } TEST(SetToolModeFromFlags, BadToolMode) { + absl::FlagSaver fs; absl::SetFlag(&FLAGS_mode, {"bad-mode"}); ExecuteQueryConfig config; EXPECT_THAT(SetToolModeFromFlags(config), @@ -134,6 +150,7 @@ TEST(SetToolModeFromFlags, BadToolMode) { } TEST(SetSqlModeFromFlags, SqlMode) { + absl::FlagSaver fs; auto CheckFlag = [](absl::string_view name, SqlMode expected_mode) { absl::SetFlag(&FLAGS_sql_mode, name); ExecuteQueryConfig config; @@ -145,6 +162,7 @@ TEST(SetSqlModeFromFlags, SqlMode) { } TEST(SetSqlModeFromFlags, BadSqlMode) { + absl::FlagSaver fs; absl::SetFlag(&FLAGS_sql_mode, "bad-mode"); ExecuteQueryConfig config; EXPECT_THAT(SetSqlModeFromFlags(config), @@ -152,6 +170,7 @@ TEST(SetSqlModeFromFlags, BadSqlMode) { } TEST(SetLanguageOptionsFromFlags, BadProductMode) { + absl::FlagSaver fs; absl::SetFlag(&FLAGS_product_mode, "bad-mode"); ExecuteQueryConfig config; EXPECT_THAT(SetLanguageOptionsFromFlags(config), @@ -159,6 +178,7 @@ TEST(SetLanguageOptionsFromFlags, BadProductMode) { } TEST(SetLanguageOptionsFromFlags, ProductMode) { + absl::FlagSaver fs; auto CheckFlag = [](absl::string_view name, ProductMode expected_mode) { absl::SetFlag(&FLAGS_product_mode, name); ExecuteQueryConfig config; @@ -171,6 +191,7 @@ TEST(SetLanguageOptionsFromFlags, ProductMode) { } TEST(SetLanguageOptionsFromFlags, NameResolutionMode) { + absl::FlagSaver fs; auto CheckFlag = [](bool flag_value, NameResolutionMode expected_mode) { absl::SetFlag(&FLAGS_strict_name_resolution_mode, flag_value); ExecuteQueryConfig config; @@ -183,6 +204,7 @@ TEST(SetLanguageOptionsFromFlags, NameResolutionMode) { } TEST(SetAnalyzerOptionsFromFlags, EnabledAstRewrites) { + absl::FlagSaver fs; auto CheckFlag = [](absl::string_view str, absl::Span expected_enabled, absl::Span expected_disabled) { @@ -219,7 +241,20 @@ TEST(SetAnalyzerOptionsFromFlags, EnabledAstRewrites) { {REWRITE_INVALID_DO_NOT_USE}); } +TEST(SetFoldLiteralCastFromFlags, FoldLiteralCast) { + absl::FlagSaver fs; + auto CheckFlag = [](bool flag_value, bool expected_value) { + absl::SetFlag(&FLAGS_fold_literal_cast, flag_value); + ExecuteQueryConfig config; + ZETASQL_EXPECT_OK(SetAnalyzerOptionsFromFlags(config)); + EXPECT_EQ(config.analyzer_options().fold_literal_cast(), expected_value); + }; + CheckFlag(false, false); + CheckFlag(true, true); +} + TEST(SetLanguageOptionsFromFlags, EnabledLanguageFeatures) { + absl::FlagSaver fs; auto CheckFlag = [](absl::string_view str, absl::Span expected_enabled, absl::Span expected_disabled) { @@ -292,6 +327,7 @@ TEST(SetEvaluatorOptionsFromFlagsTest, Options) { } TEST(SetQueryParameterValuesFromFlagsTest, NoOp) { + absl::FlagSaver fs; ExecuteQueryConfig config; ZETASQL_EXPECT_OK(SetQueryParametersFromFlags(config)); EXPECT_THAT(config.analyzer_options().query_parameters(), IsEmpty()); @@ -401,8 +437,9 @@ TEST(SetDescriptorPoolFromFlags, DescriptorPool) { ZETASQL_EXPECT_OK(SetDescriptorPoolFromFlags(config)); const Type* type = nullptr; - ZETASQL_EXPECT_OK( - config.mutable_catalog().GetType("zetasql_test__.KitchenSinkPB", &type)); + EXPECT_THAT( + config.catalog()->FindType({"zetasql_test__.KitchenSinkPB"}, &type), + StatusIs(absl::StatusCode::kNotFound)); EXPECT_EQ(type, nullptr); } @@ -476,6 +513,7 @@ static std::string TextProtoFilePath() { } TEST(AddTablesFromFlags, BadFlags) { + absl::FlagSaver fs; auto ExpectTableSpecIsInvalid = [](absl::string_view table_spec) { ExecuteQueryConfig config; absl::SetFlag(&FLAGS_table_spec, table_spec); @@ -501,9 +539,9 @@ TEST(AddTablesFromFlags, BadFlags) { } TEST(AddTablesFromFlags, GoodFlags) { + absl::FlagSaver fs; ExecuteQueryConfig config; - config.mutable_catalog().SetDescriptorPool( - google::protobuf::DescriptorPool::generated_pool()); + config.SetDescriptorPool(google::protobuf::DescriptorPool::generated_pool()); absl::SetFlag(&FLAGS_table_spec, absl::StrCat( @@ -515,26 +553,26 @@ TEST(AddTablesFromFlags, GoodFlags) { ZETASQL_EXPECT_OK(AddTablesFromFlags(config)); absl::flat_hash_set tables; - ZETASQL_EXPECT_OK(config.catalog().GetTables(&tables)); + ZETASQL_EXPECT_OK(config.wrapper_catalog()->GetTables(&tables)); EXPECT_EQ(tables.size(), // 2); const Table* csv_table = nullptr; - ZETASQL_EXPECT_OK(config.mutable_catalog().GetTable("CsvTable", &csv_table)); + ZETASQL_EXPECT_OK(config.wrapper_catalog()->GetTable("CsvTable", &csv_table)); EXPECT_NE(csv_table, nullptr); EXPECT_EQ(csv_table->NumColumns(), 3); const Table* textproto_table = nullptr; ZETASQL_EXPECT_OK( - config.mutable_catalog().GetTable("TextProtoTable", &textproto_table)); + config.wrapper_catalog()->GetTable("TextProtoTable", &textproto_table)); EXPECT_NE(textproto_table, nullptr); EXPECT_EQ(textproto_table->NumColumns(), 1); } TEST(ExecuteQuery, ReadCsvTableFileEndToEnd) { + absl::FlagSaver fs; ExecuteQueryConfig config; - config.mutable_catalog().SetDescriptorPool( - google::protobuf::DescriptorPool::generated_pool()); + config.SetDescriptorPool(google::protobuf::DescriptorPool::generated_pool()); absl::SetFlag(&FLAGS_table_spec, absl::StrCat("CsvTable=csv:", CsvFilePath())); @@ -553,6 +591,7 @@ TEST(ExecuteQuery, ReadCsvTableFileEndToEnd) { } TEST(ExecuteQuery, ParseQuery) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kParse); @@ -569,6 +608,7 @@ TEST(ExecuteQuery, ParseQuery) { } TEST(ExecuteQuery, UnparseQuery) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kUnparse); @@ -581,6 +621,7 @@ TEST(ExecuteQuery, UnparseQuery) { } TEST(ExecuteQuery, ResolveQuery) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kResolve); @@ -601,6 +642,7 @@ TEST(ExecuteQuery, ResolveQuery) { } TEST(ExecuteQuery, UnAnalyzeQuery) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kUnAnalyze); @@ -613,21 +655,22 @@ TEST(ExecuteQuery, UnAnalyzeQuery) { } TEST(ExecuteQuery, ExplainQuery) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExplain); std::ostringstream output; ZETASQL_EXPECT_OK(ExecuteQuery("select 1", config, output)); - EXPECT_EQ(output.str(), R"(RootOp( -+-input: ComputeOp( - +-map: { - | +-$col1 := ConstExpr(1)}, - +-input: EnumerateOp(ConstExpr(1)))) + EXPECT_EQ(output.str(), R"(ComputeOp( ++-map: { +| +-$col1 := ConstExpr(1)}, ++-input: EnumerateOp(ConstExpr(1))) )"); } TEST(ExecuteQuery, ExecuteQuery) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); @@ -643,6 +686,7 @@ TEST(ExecuteQuery, ExecuteQuery) { } TEST(ExecuteQuery, ExecuteQueryWithMacroExpansion) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); @@ -658,9 +702,7 @@ TEST(ExecuteQuery, ExecuteQueryWithMacroExpansion) { absl::StatusCode::kInvalidArgument, HasSubstr( "Syntax error: Expected macro name but got end of statement"))); - EXPECT_EQ(output.str(), R"(Expanded SQL: -define macro -)"); + EXPECT_EQ(output.str(), ""); output.str(""); ZETASQL_EXPECT_OK(ExecuteQuery("define macro repeat $1, $1, $2, $2", config, output)); @@ -675,8 +717,6 @@ define macro R"(Warning: Macro 'absent' not found. [at :1:8] select $absent ^ -Expanded SQL: -select $absent )"); output.str(""); @@ -689,10 +729,134 @@ select 1, 1, (2), (2) | 1 | 1 | 2 | 2 | +---+---+---+---+ +)"); + + // Macros in multi-statement scripts aren't supported, with macros + // either first or non-first. + output.str(""); + EXPECT_THAT( + ExecuteQuery("define macro abc 123;\n" + "select 123;", + config, output), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Statement not supported: DefineMacroStatement"))); + + output.str(""); + EXPECT_THAT( + ExecuteQuery("select 123;\n" + "define macro abc 123;", + config, output), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Statement not supported: DefineMacroStatement"))); + + // DefineMacro statements are allowed to parse. + config.clear_tool_modes(); + config.add_tool_mode(ToolMode::kParse); + output.str(""); + ZETASQL_EXPECT_OK( + ExecuteQuery("select 123;\n" + "define macro abc 123;", + config, output)); + EXPECT_EQ(output.str(), + R"(QueryStatement [0-10] + Query [0-10] + Select [0-10] + SelectList [7-10] + SelectColumn [7-10] + IntLiteral(123) [7-10] + +DefineMacroStatement [12-32] + Identifier(abc) [25-28] + MacroBody(123) [29-32] + +)"); + + // A query without any macros doesn't print Expanded SQL. + output.str(""); + ZETASQL_EXPECT_OK(ExecuteQuery("select 1 a", config, output)); + EXPECT_EQ(output.str(), + R"(QueryStatement [0-10] + Query [0-10] + Select [0-10] + SelectList [7-10] + SelectColumn [7-10] + IntLiteral(1) [7-8] + Alias [9-10] + Identifier(a) [9-10] + +)"); +} + +TEST(ExecuteQuery, ExecuteQueryWithMacroExpansion_StrictMode) { + absl::FlagSaver fs; + ExecuteQueryConfig config; + config.clear_tool_modes(); + config.add_tool_mode(ToolMode::kExecute); + config.mutable_analyzer_options().mutable_language()->EnableLanguageFeature( + FEATURE_V_1_4_SQL_MACROS); + config.mutable_analyzer_options().mutable_language()->EnableLanguageFeature( + FEATURE_V_1_4_ENFORCE_STRICT_MACROS); + config.mutable_analyzer_options().set_error_message_mode( + ErrorMessageMode::ERROR_MESSAGE_MULTI_LINE_WITH_CARET); + + std::ostringstream output; + ZETASQL_EXPECT_OK(ExecuteQuery("define macro abc 123;", config, output)); + EXPECT_EQ(output.str(), "Macro registered: abc\n"); + output.str(""); + + ZETASQL_EXPECT_OK(ExecuteQuery("select $abc()", config, output)); + EXPECT_EQ(output.str(), R"(Expanded SQL: +select 123 ++-----+ +| | ++-----+ +| 123 | ++-----+ + +)"); + + output.str(""); + EXPECT_THAT(ExecuteQuery("select $abc", config, output), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("Invocation of macro 'abc' missing argument " + "list. [at :1:12]"))); + EXPECT_EQ(output.str(), ""); +} + +// This tests invoking ExecuteQuery multiple times with the same Config, +// carrying state (DDL definitions) from previous calls to the next. +// This simulates how --interactive mode calls ExecuteQuery. +TEST(ExecuteQuery, ExecuteQueryStateful) { + absl::FlagSaver fs; + ExecuteQueryConfig config; + ZETASQL_ASSERT_OK(InitializeExecuteQueryConfig(config)); + config.clear_tool_modes(); + config.add_tool_mode(ToolMode::kExecute); + config.mutable_analyzer_options().set_error_message_mode( + ErrorMessageMode::ERROR_MESSAGE_MULTI_LINE_WITH_CARET); + + std::ostringstream output; + ZETASQL_EXPECT_OK(ExecuteQuery("create function f() AS (10*2)", config, output)); + EXPECT_EQ(output.str(), "Function registered.\n"); + + output.str(""); + ZETASQL_EXPECT_OK(ExecuteQuery("create function g() AS (f()*3)", config, output)); + EXPECT_EQ(output.str(), "Function registered.\n"); + + output.str(""); + ZETASQL_EXPECT_OK(ExecuteQuery("select f() f, g() g", config, output)); + EXPECT_EQ(output.str(), + R"(+----+----+ +| f | g | ++----+----+ +| 20 | 60 | ++----+----+ + )"); } TEST(ExecuteQuery, ParseExpression) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kParse); @@ -705,6 +869,7 @@ TEST(ExecuteQuery, ParseExpression) { } TEST(ExecuteQuery, ResolveExpression) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kResolve); @@ -717,6 +882,7 @@ TEST(ExecuteQuery, ResolveExpression) { } TEST(ExecuteQuery, ExplainExpression) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExplain); @@ -724,11 +890,12 @@ TEST(ExecuteQuery, ExplainExpression) { std::ostringstream output; ZETASQL_EXPECT_OK(ExecuteQuery("1", config, output)); - EXPECT_EQ(output.str(), R"(RootExpr(ConstExpr(1)) + EXPECT_EQ(output.str(), R"(ConstExpr(1) )"); } TEST(ExecuteQuery, ExecuteExpression) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); @@ -740,6 +907,7 @@ TEST(ExecuteQuery, ExecuteExpression) { } TEST(ExecuteQuery, ExecuteError) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); @@ -749,10 +917,11 @@ TEST(ExecuteQuery, ExecuteError) { } TEST(ExecuteQuery, RespectEvaluatorOptions) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); - config.mutable_catalog().AddBuiltinFunctions( + config.builtins_catalog()->AddBuiltinFunctions( BuiltinFunctionOptions(config.analyzer_options().language())); { @@ -773,11 +942,12 @@ TEST(ExecuteQuery, RespectEvaluatorOptions) { } TEST(ExecuteQuery, RespectEvaluatorOptionsExpressions) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); config.set_sql_mode(SqlMode::kExpression); - config.mutable_catalog().AddBuiltinFunctions( + config.builtins_catalog()->AddBuiltinFunctions( BuiltinFunctionOptions(config.analyzer_options().language())); { @@ -798,10 +968,11 @@ TEST(ExecuteQuery, RespectEvaluatorOptionsExpressions) { } TEST(ExecuteQuery, RespectQueryParameters) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); - config.mutable_catalog().AddBuiltinFunctions( + config.builtins_catalog()->AddBuiltinFunctions( BuiltinFunctionOptions(config.analyzer_options().language())); ZETASQL_ASSERT_OK(config.mutable_analyzer_options().AddQueryParameter( "p1", types::Int64Type())); @@ -818,11 +989,12 @@ TEST(ExecuteQuery, RespectQueryParameters) { } TEST(ExecuteQuery, RespectQueryParametersExpression) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); config.set_sql_mode(SqlMode::kExpression); - config.mutable_catalog().AddBuiltinFunctions( + config.builtins_catalog()->AddBuiltinFunctions( BuiltinFunctionOptions(config.analyzer_options().language())); ZETASQL_ASSERT_OK(config.mutable_analyzer_options().AddQueryParameter( "p1", types::Int64Type())); @@ -833,6 +1005,7 @@ TEST(ExecuteQuery, RespectQueryParametersExpression) { } TEST(ExecuteQuery, ExamineResolvedASTCallback) { + absl::FlagSaver fs; ExecuteQueryConfig config; config.clear_tool_modes(); config.add_tool_mode(ToolMode::kExecute); @@ -847,5 +1020,116 @@ TEST(ExecuteQuery, ExamineResolvedASTCallback) { EXPECT_THAT(output.str(), IsEmpty()); } +TEST(SetLanguageOptionsFromFlags, SelectedCatalog_None) { + absl::FlagSaver fs; + ExecuteQueryConfig config; + ZETASQL_ASSERT_OK(InitializeExecuteQueryConfig(config)); + config.clear_tool_modes(); + config.add_tool_mode(ToolMode::kResolve); + + // Referencing built-in functions works. + std::ostringstream output; + ZETASQL_EXPECT_OK(ExecuteQuery("select sqrt(1)", config, output)); + + // There are no tables to reference. + EXPECT_THAT(ExecuteQuery("select sqrt(1) from TestTable", config, output), + StatusIs(absl::StatusCode::kInvalidArgument, + MatchesRegex(".*Table not found.*"))); +} + +TEST(SetLanguageOptionsFromFlags, SelectedCatalog_Sample) { + absl::FlagSaver fs; + absl::SetFlag(&FLAGS_catalog, "sample"); + + ExecuteQueryConfig config; + ZETASQL_ASSERT_OK(InitializeExecuteQueryConfig(config)); + config.clear_tool_modes(); + config.add_tool_mode(ToolMode::kResolve); + + // Built-ins work, and tables from SampleCatalog work. + std::ostringstream output; + ZETASQL_EXPECT_OK(ExecuteQuery("select sqrt(1) from TestTable", config, output)); +} + +// Regression test for a case where the query defines a function that refers to +// a builtin function in a different catalog. ExecuteQueryConfig has different +// catalogs for builtins and newly defined objects. This case was triggering a +// ZETASQL_RET_CHECK previously. +TEST(ExecuteQuery, ResolveFunction_DifferentCatalogs) { + absl::FlagSaver fs; + ExecuteQueryConfig config; + config.clear_tool_modes(); + config.add_tool_mode(ToolMode::kResolve); + config.mutable_analyzer_options() + .mutable_language() + ->SetSupportedStatementKinds({zetasql::RESOLVED_CREATE_FUNCTION_STMT}); + ZETASQL_ASSERT_OK(config.builtins_catalog()->AddBuiltinFunctionsAndTypes( + BuiltinFunctionOptions(config.analyzer_options().language()))); + std::ostringstream output; + + // Here, the IF() function is provided by the builtins catalog. + const auto& query = R"( + CREATE TEMP FUNCTION NewFunction(id STRING) RETURNS INT64 + AS ( + IF(LENGTH(id) > 0, 1, 0) + );)"; + ZETASQL_EXPECT_OK(ExecuteQuery(query, config, output)); +} + +static absl::Status RunFileBasedTestImpl( + absl::string_view test_case_input, + file_based_test_driver::RunTestCaseResult* test_result, + std::ostringstream* output) { + file_based_test_driver::TestCaseOptions test_case_options; + // `mode` and `catalog` options correspond to flags of the same name. + test_case_options.RegisterString("mode", "execute"); + test_case_options.RegisterString("catalog", "sample"); + + std::string test_case = std::string(test_case_input); + ZETASQL_RETURN_IF_ERROR(test_case_options.ParseTestCaseOptions(&test_case)); + + absl::SetFlag(&FLAGS_mode, + absl::StrSplit(test_case_options.GetString("mode"), ",")); + absl::SetFlag(&FLAGS_catalog, test_case_options.GetString("catalog")); + + ExecuteQueryConfig config; + ZETASQL_RETURN_IF_ERROR(InitializeExecuteQueryConfig(config)); + config.mutable_analyzer_options().set_error_message_mode( + ERROR_MESSAGE_MULTI_LINE_WITH_CARET); + + return ExecuteQuery(test_case, config, *output); +} + +// Wrapper around RunFileBasedTestImpl that turns returned errors into +// test output. +static void RunFileBasedTest( + absl::string_view test_case_input, + file_based_test_driver::RunTestCaseResult* test_result) { + std::ostringstream output; + absl::Status status = + RunFileBasedTestImpl(test_case_input, test_result, &output); + if (!status.ok()) { + status.ErasePayload(kErrorMessageModeUrl); + + // Show both the output so far plus the error. + output << "ERROR: " << status.ToString() << std::endl; + } + // String-replace is a hack because some catalog->FindX errors have a trailing + // space when there's no catalog name and then the linter blocks the CL. + test_result->AddTestOutput( + absl::StrReplaceAll(output.str(), {{" \n", "\n"}})); +} + +TEST(ExecuteQuery, FileBasedTest) { + absl::FlagSaver fs; + const std::string pattern = + zetasql_base::JoinPath(::testing::SrcDir(), + "com_google_zetasql/zetasql/tools/execute_query/" + "testdata/execute_query_tool.test"); + + EXPECT_TRUE(file_based_test_driver::RunTestCasesFromFiles(pattern, + &RunFileBasedTest)); +} + } // namespace } // namespace zetasql diff --git a/zetasql/tools/execute_query/execute_query_web.cc b/zetasql/tools/execute_query/execute_query_web.cc new file mode 100644 index 000000000..07951f480 --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web.cc @@ -0,0 +1,188 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/tools/execute_query/execute_query_web.h" + +#include +#include +#include +#include +#include + +#include "zetasql/tools/execute_query/execute_query_web_handler.h" +#include "zetasql/tools/execute_query/execute_query_web_server.h" +#include "zetasql/tools/execute_query/web/embedded_resources.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/notification.h" +#include "external/civetweb/civetweb/include/CivetServer.h" +#include "external/civetweb/civetweb/include/civetweb.h" + +namespace zetasql { + +namespace { + +constexpr absl::string_view kHttpHeaders = + "HTTP/1.1 200 OK\r\nContent-Type: " + "text/html\r\nConnection: close\r\n\r\n"; + +// HTTP handler for the Web UI. +class RootHandler : public CivetHandler { + public: + bool handleGet(CivetServer *server, struct mg_connection *conn) override { + return handleAll("GET", server, conn); + } + bool handlePost(CivetServer *server, struct mg_connection *conn) override { + return handleAll("POST", server, conn); + } + + private: + bool handleAll(const char *method, CivetServer *server, + struct mg_connection *conn) { + absl::string_view request_uri = mg_get_request_info(conn)->request_uri; + if (request_uri != "/") { + ABSL_LOG(WARNING) << "Request " << request_uri << " not handled."; + return false; + } + + std::unique_ptr request = ParseRequest(conn); + if (!request) { + ABSL_LOG(WARNING) << "Failed to parse request."; + return false; + } + + ExecuteQueryWebHandler::Writer writer = + [conn](absl::string_view rendered_html) { + mg_write(conn, rendered_html.data(), rendered_html.size()); + return rendered_html.size(); + }; + + if (!writer(kHttpHeaders)) { + ABSL_LOG(WARNING) << "Error writing HTTP headers."; + return false; + } + + ExecuteQueryWebHandler handler(QueryWebTemplates::Default()); + return handler.HandleRequest(*request, writer); + } + + std::unique_ptr ParseRequest( + struct mg_connection *conn) { + std::string query; + CivetServer::getParam(conn, "query", query); + + std::string catalog; + CivetServer::getParam(conn, "catalog", catalog); + + return std::make_unique(GetModesParams(conn), query, + catalog); + } + + // Gets all the modes currently checked in the form. + std::vector GetModesParams(struct mg_connection *conn) { + std::vector modes; + for (int i = 0; i < 10; i++) { + std::string mode; + if (!CivetServer::getParam(conn, "mode", mode, i)) { + break; + } + modes.push_back(mode); + } + return modes; + } +}; + +// Used as a callback for the CivetServer.. +int CivetLogMessage(const struct mg_connection *, const char *message) { + ABSL_LOG(INFO) << "CivetWeb: " << message; + return 0; +} + +constexpr char kBanner[] = R"txt( + ╔══════════════════════════════════════════╗ + ║ ┌─┐─┐ ┬┌─┐┌─┐┬ ┬┌┬┐┌─┐ ┌─┐ ┬ ┬┌─┐┬─┐┬ ┬ ║ + ║ ├┤ ┌┴┬┘├┤ │ │ │ │ ├┤ │─┼┐│ │├┤ ├┬┘└┬┘ ║ + ║ └─┘┴ └─└─┘└─┘└─┘ ┴ └─┘ └─┘└└─┘└─┘┴└─ ┴ ║ + ╚══════════════════════════════════════════╝ + + Ready! + http://%s:%i + +)txt"; + +class WebServer : public ExecuteQueryWebServerInterface { + public: + WebServer() = default; + ~WebServer() override = default; + + bool Start(int32_t port) override { + mg_init_library(0); + // Using + as the listening port allows it to bind to both IPV4 and + // IPV6 addresses. + std::vector options = {"listening_ports", + absl::StrCat("+", port)}; + CivetCallbacks callbacks; + callbacks.log_message = CivetLogMessage; + civet_server_ = std::make_unique(options, &callbacks); + + civet_server_->addHandler("/", &query_handler_); + return true; + } + + void Wait() override { + absl::Notification notification; + notification.WaitForNotification(); + } + + void Stop() override { + civet_server_->close(); + mg_exit_library(); + } + + private: + std::unique_ptr civet_server_; + RootHandler query_handler_; +}; + +std::unique_ptr CreateWebServer() { + + return std::make_unique(); +} + +std::string GetHostname() { + char hostname[1024]; + int ret = gethostname(hostname, sizeof(hostname)); + return std::string((ret == 0) ? hostname : "localhost"); +} + +} // namespace + +absl::Status RunExecuteQueryWebServer(int32_t port) { + std::unique_ptr server = CreateWebServer(); + if (!server->Start(port)) { + return absl::InternalError("Failed to start web server."); + } + + std::string hostname = GetHostname(); + std::printf(kBanner, hostname.c_str(), port); + server->Wait(); + server->Stop(); + return absl::OkStatus(); +} + +} // namespace zetasql diff --git a/zetasql/tools/execute_query/execute_query_web.h b/zetasql/tools/execute_query/execute_query_web.h new file mode 100644 index 000000000..18c1b2e3c --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web.h @@ -0,0 +1,31 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_H_ +#define ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_H_ + +#include + +#include "absl/status/status.h" + +namespace zetasql { + +// Runs a local Web server that can be used to execute queries. +absl::Status RunExecuteQueryWebServer(int32_t port); + +} // namespace zetasql + +#endif // ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_H_ diff --git a/zetasql/tools/execute_query/execute_query_web_handler.cc b/zetasql/tools/execute_query/execute_query_web_handler.cc new file mode 100644 index 000000000..c822efab6 --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web_handler.cc @@ -0,0 +1,157 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/tools/execute_query/execute_query_web_handler.h" + +#include +#include +#include +#include + +#include "zetasql/public/evaluator_table_iterator.h" +#include "zetasql/public/value.h" +#include "zetasql/resolved_ast/resolved_node.h" +#include "zetasql/tools/execute_query/execute_query_tool.h" +#include "zetasql/tools/execute_query/execute_query_web_writer.h" +#include "zetasql/tools/execute_query/execute_query_writer.h" +#include "zetasql/tools/execute_query/web/embedded_resources.h" +#include "absl/flags/flag.h" +#include "absl/log/log.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" +#include "absl/types/span.h" +#include "external/mstch/mstch/include/mstch/mstch.hpp" + +namespace zetasql { + +namespace { + +ModeSet ModeSetFromStrings(absl::Span mode_strings) { + ModeSet modes; + for (const auto &mode : mode_strings) { + std::optional tool_mode = + ExecuteQueryConfig::parse_tool_mode(mode); + if (tool_mode.has_value()) { + modes.insert(*tool_mode); + } + } + + if (modes.empty()) { + modes.insert(ExecuteQueryConfig::ToolMode::kExecute); + } + + return modes; +} + +} // namespace + +ExecuteQueryWebRequest::ExecuteQueryWebRequest( + const std::vector &str_modes, std::string query, + std::string catalog) + : modes_(ModeSetFromStrings(str_modes)), + query_(std::move(query)), + catalog_(std::move(catalog)) { + // The query input sometimes contains non-breaking spaces (or the HTML entity + //  ). This is 0xA0 (encoded in UTF-8 as \xc2\xa0). A bare 0xa0 is + // sometimes seen when a query is copied from an HTML editor. + // We explicitly replace these sequences with a normal space character. + absl::StrReplaceAll({{"\xc2\xa0", " "}, {"\xa0", " "}}, &query_); + + if (catalog_.empty()) { + // Get the value from the flag on the initial page load. + catalog_ = absl::GetFlag(FLAGS_catalog); + } +} + +bool ExecuteQueryWebHandler::HandleRequest( + const ExecuteQueryWebRequest &request, const Writer &writer) { + mstch::map template_params = {{"query", request.query()}, + {"css", templates_.GetWebPageCSS()}}; + + mstch::array catalogs; + for (const auto *selectable_catalog : GetSelectableCatalogs()) { + mstch::map entry = { + {"name", selectable_catalog->name()}, + {"label", absl::StrCat(selectable_catalog->name(), " - ", + selectable_catalog->description())}}; + if (selectable_catalog->name() == request.catalog()) { + entry["selected"] = std::string("selected"); + } + catalogs.push_back(entry); + } + + template_params.insert(std::pair("catalogs", catalogs)); + + if (!request.query().empty()) { + std::string error_msg; + ExecuteQueryWebWriter params_writer(template_params); + ExecuteQuery(request, error_msg, params_writer); + params_writer.FlushStatement(/*at_end=*/true, error_msg); + } + + // Add the selected modes back into the template so that the same checkboxes + // are checked when the page is rendered again. + for (const auto &mode : request.modes()) { + template_params[absl::StrCat( + "mode_", ExecuteQueryConfig::tool_mode_name(mode))] = "1"; + } + + // Render the page. + std::string rendered = + mstch::render(templates_.GetWebPageContents(), template_params, + {{"body", templates_.GetWebPageBody()}, + {"table", templates_.GetTable()}}); + + if (writer(rendered) <= 0) { + ABSL_LOG(WARNING) << "Error writing rendered HTML."; + return false; + } + + return true; +} + +absl::Status ExecuteQueryWebHandler::ExecuteQueryImpl( + const ExecuteQueryWebRequest &request, + ExecuteQueryWriter &exec_query_writer) { + // TODO: Try to avoid creating a new config each time + ExecuteQueryConfig config; + ZETASQL_RETURN_IF_ERROR(zetasql::InitializeExecuteQueryConfig(config)); + + config.set_tool_modes(request.modes()); + config.mutable_analyzer_options().set_error_message_mode( + ERROR_MESSAGE_MULTI_LINE_WITH_CARET); + + ZETASQL_RETURN_IF_ERROR(config.SetCatalogFromString(request.catalog())); + + ZETASQL_RETURN_IF_ERROR( + zetasql::ExecuteQuery(request.query(), config, exec_query_writer)); + return absl::OkStatus(); +} + +bool ExecuteQueryWebHandler::ExecuteQuery( + const ExecuteQueryWebRequest &request, std::string &error_msg, + ExecuteQueryWriter &exec_query_writer) { + absl::Status st = ExecuteQueryImpl(request, exec_query_writer); + if (!st.ok()) { + error_msg = st.message(); + } + return st.ok(); +} + +} // namespace zetasql diff --git a/zetasql/tools/execute_query/execute_query_web_handler.h b/zetasql/tools/execute_query/execute_query_web_handler.h new file mode 100644 index 000000000..1cc4021ab --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web_handler.h @@ -0,0 +1,77 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_HANDLER_H_ +#define ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_HANDLER_H_ + +#include +#include +#include + +#include "zetasql/tools/execute_query/execute_query_tool.h" +#include "zetasql/tools/execute_query/execute_query_writer.h" +#include "zetasql/tools/execute_query/selectable_catalog.h" +#include "zetasql/tools/execute_query/web/embedded_resources.h" +#include "absl/container/flat_hash_set.h" +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" + +namespace zetasql { + +using ModeSet = absl::flat_hash_set; + +// Encapsulates request parameters. +class ExecuteQueryWebRequest { + public: + ExecuteQueryWebRequest(const std::vector& str_modes, + std::string query, std::string catalog); + + const std::string& query() const { return query_; } + const ModeSet& modes() const { return modes_; } + const std::string& catalog() const { return catalog_; } + + private: + ModeSet modes_; + std::string query_; + std::string catalog_; +}; + +// Handler for a web request. This class takes an incoming request, executes +// the query, and writes the results in HTML. +class ExecuteQueryWebHandler { + public: + explicit ExecuteQueryWebHandler(const QueryWebTemplates& templates) + : templates_(templates) {} + ~ExecuteQueryWebHandler() = default; + + using Writer = absl::AnyInvocable; + + bool HandleRequest(const ExecuteQueryWebRequest& request, + const Writer& writer); + + private: + absl::Status ExecuteQueryImpl(const ExecuteQueryWebRequest& request, + ExecuteQueryWriter& exec_query_writer); + bool ExecuteQuery(const ExecuteQueryWebRequest& request, + std::string& error_msg, + ExecuteQueryWriter& exec_query_writer); + + const QueryWebTemplates& templates_; +}; + +} // namespace zetasql + +#endif // ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_HANDLER_H_ diff --git a/zetasql/tools/execute_query/execute_query_web_handler_test.cc b/zetasql/tools/execute_query/execute_query_web_handler_test.cc new file mode 100644 index 000000000..17fc2ceb0 --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web_handler_test.cc @@ -0,0 +1,209 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/tools/execute_query/execute_query_web_handler.h" + +#include +#include +#include +#include +#include + +#include "zetasql/tools/execute_query/web/embedded_resources.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/algorithm/container.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" + +using testing::Eq; +using testing::HasSubstr; +using testing::StartsWith; + +namespace zetasql { + +namespace { + +class FakeQueryWebTemplates : public QueryWebTemplates { + public: + FakeQueryWebTemplates(std::string contents, std::string css, std::string body) + : contents_(std::move(contents)), + css_(std::move(css)), + body_(std::move(body)) {} + ~FakeQueryWebTemplates() override = default; + + const std::string& GetWebPageContents() const override { return contents_; } + const std::string& GetWebPageCSS() const override { return css_; } + const std::string& GetWebPageBody() const override { return body_; } + + private: + std::string contents_; + std::string css_; + std::string body_; +}; + +bool HandleRequest(const ExecuteQueryWebRequest& request, + const QueryWebTemplates& templates, std::string& result) { + ExecuteQueryWebHandler handler(templates); + return handler.HandleRequest(request, [&result](absl::string_view s) { + result = s; + return true; + }); +} + +} // namespace + +TEST(ExecuteQueryWebHandlerTest, TestCSS) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({""}, "", "none"), + FakeQueryWebTemplates("CSS: {{css}}", "some_css", ""), result)); + EXPECT_THAT(result, Eq("CSS: some_css")); +} + +TEST(ExecuteQueryWebHandlerTest, TestQueryPreserved) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({""}, "foo bar", "none"), + FakeQueryWebTemplates("{{> body}}", "", "Query: {{query}}"), result)); + EXPECT_THAT(result, Eq("Query: foo bar")); +} + +TEST(ExecuteQueryWebHandlerTest, TestModesPreserved) { + std::vector modes = {"analyze", "explain", "parse"}; + std::string mode_template = + "{{mode_analyze}}-{{mode_explain}}-{{mode_parse}}"; + std::string result; + for (int i = 0; i < 20; i++) { + for (int subset_size = 0; subset_size <= modes.size(); subset_size++) { + std::vector subset = modes; + std::shuffle(subset.begin(), subset.end(), std::mt19937{}); + subset.resize(subset_size); + + std::vector expected_v; + for (const std::string& s : modes) { + expected_v.push_back(absl::c_linear_search(subset, s) ? "true" : ""); + } + std::string expected = absl::StrJoin(expected_v, "-"); + + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest(subset, "foo bar", "none"), + FakeQueryWebTemplates("{{> body}}", "", mode_template), result)); + EXPECT_THAT(result, Eq(expected)) + << "Failed for subset [" << absl::StrJoin(subset, ",") << "]"; + } + } +} + +TEST(ExecuteQueryWebHandlerTest, TestQueryEscaped) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({""}, " Exploit!", "none"), + FakeQueryWebTemplates("{{> body}}", "", "Query: {{query}}"), result)); + EXPECT_THAT(result, Eq("Query: </select> Exploit!")); +} + +TEST(ExecuteQueryWebHandlerTest, TestQueryResultPresent) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({"execute"}, "SELECT 1", "none"), + FakeQueryWebTemplates("{{> body}}", "", + "{{#statements}}" + "{{result}}-{{error}}-{{result_or_error}}" + "{{/statements}}"), + result)); + EXPECT_THAT(result, Eq("true--true")); +} + +TEST(ExecuteQueryWebHandlerTest, TestQueryExecutedResult) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({"execute"}, "SELECT 1, 'foo'", "none"), + FakeQueryWebTemplates("{{> body}}", "", + "{{#statements}}" + "{{#result_executed}}" + "{{#result_executed_table}}" + "{{> table}}" + "{{/result_executed_table}}" + "{{/result_executed}}" + "{{/statements}}"), + result)); + + const auto& expected = R"(
+ + + + + + + + + + + + +
1foo
)"; + EXPECT_THAT(result, Eq(expected)); +} + +TEST(ExecuteQueryWebHandlerTest, TestQueryExecutedSimpleResult) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({"execute"}, "DESCRIBE RAND", "none"), + FakeQueryWebTemplates("{{> body}}", "", + "{{#statements}}" + "{{result_executed_text}}" + "{{/statements}}"), + result)); + EXPECT_THAT(result, Eq("Function RAND\nSignature: RAND() -> DOUBLE\n")); +} + +TEST(ExecuteQueryWebHandlerTest, TestQueryErrorPresent) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({"execute"}, "bad request", "none"), + FakeQueryWebTemplates("{{> body}}", "", + "{{#statements}}" + "{{result}}-{{result_or_error}}-{{error}}" + "{{/statements}}"), + result)); + EXPECT_THAT(result, StartsWith("-true-")); + EXPECT_THAT(result, HasSubstr("Syntax error")); +} + +TEST(ExecuteQueryWebHandlerTest, TestCatalogUsed) { + std::string result; + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({"execute"}, "DESCRIBE Value", "none"), + FakeQueryWebTemplates("{{> body}}", "", + "{{#statements}}" + "{{error}}{{result_executed_text}}" + "{{/statements}}"), + result)); + EXPECT_THAT(result, Eq("Object not found")); + + EXPECT_TRUE(HandleRequest( + ExecuteQueryWebRequest({"execute"}, "DESCRIBE Value", "sample"), + FakeQueryWebTemplates("{{> body}}", "", + "{{#statements}}" + "{{error}}{{result_executed_text}}" + "{{/statements}}"), + result)); + EXPECT_THAT(result, + Eq("Table: Value\nColumns:\n Value INT64\n Value_1 INT64\n")); +} + +} // namespace zetasql diff --git a/zetasql/tools/execute_query/execute_query_web_server.h b/zetasql/tools/execute_query/execute_query_web_server.h new file mode 100644 index 000000000..54fc2a46d --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web_server.h @@ -0,0 +1,36 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_SERVER_H_ +#define ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_SERVER_H_ + +#include +#include + +namespace zetasql { + +class ExecuteQueryWebServerInterface { + public: + virtual ~ExecuteQueryWebServerInterface() = default; + + virtual bool Start(int32_t port) = 0; + virtual void Wait() = 0; + virtual void Stop() = 0; +}; + +} // namespace zetasql + +#endif // ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_SERVER_H_ diff --git a/zetasql/tools/execute_query/execute_query_web_writer.cc b/zetasql/tools/execute_query/execute_query_web_writer.cc new file mode 100644 index 000000000..70ff4f8ad --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web_writer.cc @@ -0,0 +1,83 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/tools/execute_query/execute_query_web_writer.h" + +#include +#include +#include + +#include "zetasql/public/evaluator_table_iterator.h" +#include "zetasql/public/value.h" +#include "zetasql/resolved_ast/resolved_node.h" +#include "zetasql/tools/execute_query/output_query_result.h" +#include "absl/status/status.h" +#include "external/mstch/mstch/include/mstch/mstch.hpp" +#include "zetasql/base/status_macros.h" + +namespace zetasql { + +namespace { + +// Prints the result of executing a query as an HTML table. +absl::Status RenderResultsAsTable(std::unique_ptr iter, + mstch::map &table_params) { + std::vector columnNames; + columnNames.reserve(iter->NumColumns()); + for (int i = 0; i < iter->NumColumns(); ++i) { + columnNames.push_back(iter->GetColumnName(i)); + } + + std::vector rows; + while (iter->NextRow()) { + std::vector row_values; + row_values.reserve(iter->NumColumns()); + for (int i = 0; i < iter->NumColumns(); ++i) { + row_values.push_back(zetasql::ValueToOutputString( + iter->GetValue(i), /*escape_strings=*/false)); + } + rows.push_back(std::move(row_values)); + } + table_params = mstch::map({ + {"columnNames", mstch::node(std::move(columnNames))}, + {"rows", mstch::node(std::move(rows))}, + }); + return iter->Status(); +} + +} // namespace + +absl::Status ExecuteQueryWebWriter::executed( + const ResolvedNode &ast, std::unique_ptr iter) { + current_statement_params_["result_executed"] = true; + + mstch::map table_params; + ZETASQL_RETURN_IF_ERROR(RenderResultsAsTable(std::move(iter), table_params)); + current_statement_params_["result_executed_table"] = std::move(table_params); + got_results_ = true; + return absl::OkStatus(); +} + +absl::Status ExecuteQueryWebWriter::ExecutedExpression(const ResolvedNode &ast, + const Value &value) { + current_statement_params_["result_executed"] = true; + current_statement_params_["result_executed_text"] = + OutputPrettyStyleExpressionResult(value, /*include_box=*/false); + got_results_ = true; + return absl::OkStatus(); +} + +} // namespace zetasql diff --git a/zetasql/tools/execute_query/execute_query_web_writer.h b/zetasql/tools/execute_query/execute_query_web_writer.h new file mode 100644 index 000000000..b09a982c3 --- /dev/null +++ b/zetasql/tools/execute_query/execute_query_web_writer.h @@ -0,0 +1,142 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_WRITER_H_ +#define ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_WRITER_H_ + +#include +#include + +#include "zetasql/tools/execute_query/execute_query_writer.h" +#include "absl/status/status.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "external/mstch/mstch/include/mstch/mstch.hpp" + +namespace zetasql { + +// Writer for an ExecuteQuery call that populates template params used for the +// HTML output. +class ExecuteQueryWebWriter : public ExecuteQueryWriter { + public: + explicit ExecuteQueryWebWriter(mstch::map &template_params) + : template_params_(template_params) { + // The `statements` param is an array of maps, containing the `error` + // and `result_*` keys for each statmement. + template_params_["statements"] = mstch::array(); + } + + ExecuteQueryWebWriter(const ExecuteQueryWebWriter &) = delete; + ExecuteQueryWebWriter &operator=(const ExecuteQueryWebWriter &) = delete; + + absl::Status log(absl::string_view message) override { + absl::StrAppend(&log_messages_, message, "\n"); + current_statement_params_["result_log"] = std::string(log_messages_); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status parsed(absl::string_view parse_debug_string) override { + current_statement_params_["result_parsed"] = + std::string(parse_debug_string); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status unparsed(absl::string_view unparse_string) override { + current_statement_params_["result_unparsed"] = std::string(unparse_string); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status resolved(const ResolvedNode &ast) override { + current_statement_params_["result_analyzed"] = ast.DebugString(); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status unanalyze(absl::string_view unanalyze_string) override { + current_statement_params_["result_unanalyzed"] = + std::string(unanalyze_string); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status explained(const ResolvedNode &ast, + absl::string_view explain) override { + current_statement_params_["result_explained"] = std::string(explain); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status executed(absl::string_view output) override { + current_statement_params_["result_executed"] = true; + current_statement_params_["result_executed_text"] = std::string(output); + got_results_ = true; + return absl::OkStatus(); + } + + absl::Status executed(const ResolvedNode &ast, + std::unique_ptr iter) override; + + absl::Status ExecutedExpression(const ResolvedNode &ast, + const Value &value) override; + + absl::Status StartStatement(bool is_first) override { + if (!is_first) { + FlushStatement(/*at_end=*/false); + } + return absl::OkStatus(); + } + + void FlushStatement(bool at_end, std::string error_msg = "") { + if (GotResults()) { + current_statement_params_["result"] = "1"; + } + if (!error_msg.empty()) { + current_statement_params_["error"] = error_msg; + } + current_statement_params_["result_or_error"] = + GotResults() || !error_msg.empty(); + if (!at_end) { + current_statement_params_["not_is_last"] = true; + } + + // This would be preferred, but I can't get it work on these boost + // variant types, so I'm maintaining the array separately and copying it + // back into the map every time it gets updated. + // template_params_["statements"]).push_back(current_statement_params_); + statement_params_array_.push_back(current_statement_params_); + template_params_["statements"] = mstch::array(statement_params_array_); + + got_results_ = false; + current_statement_params_.clear(); + log_messages_.clear(); + } + + bool GotResults() const { return got_results_; } + + private: + mstch::map &template_params_; + mstch::map current_statement_params_; + mstch::array statement_params_array_; + std::string log_messages_; + bool got_results_{false}; +}; + +} // namespace zetasql + +#endif // ZETASQL_TOOLS_EXECUTE_QUERY_EXECUTE_QUERY_WEB_WRITER_H_ diff --git a/zetasql/tools/execute_query/execute_query_writer.h b/zetasql/tools/execute_query/execute_query_writer.h index a91b98332..db8f0413f 100644 --- a/zetasql/tools/execute_query/execute_query_writer.h +++ b/zetasql/tools/execute_query/execute_query_writer.h @@ -32,9 +32,18 @@ class ExecuteQueryWriter { public: virtual ~ExecuteQueryWriter() = default; + // Write textual logging messages. A newline will be added on the end. + // This can be called multiple times for the same statement. + virtual absl::Status log(absl::string_view message) { + return WriteOperationString("log", message); + } + virtual absl::Status parsed(absl::string_view parse_debug_string) { return WriteOperationString("parsed", parse_debug_string); } + // Note: This is being abused in some cases to send text directly as output. + // This doesn't work as expected in web mode. At most one of those outputs + // shows up and it goes in the "Unparsed" section. virtual absl::Status unparsed(absl::string_view unparse_string) { return WriteOperationString("unparsed", unparse_string); } @@ -52,17 +61,29 @@ class ExecuteQueryWriter { return absl::UnimplementedError( "ExecuteQueryWriter::explained is not implemented"); } + + // This is used for commands that directly produce output as a string. + virtual absl::Status executed(absl::string_view output) { + return WriteOperationString("executed", output); + } + // This is used for commands that produce a table as output. virtual absl::Status executed(const ResolvedNode& ast, std::unique_ptr iter) { return absl::UnimplementedError( "ExecuteQueryWriter::executed is not implemented"); } + virtual absl::Status ExecutedExpression(const ResolvedNode& ast, const Value& value) { return absl::UnimplementedError( "ExecuteQueryWriter::executed is not implemented"); } + // Called at the start of a statement. `is_first` is true for the first one. + virtual absl::Status StartStatement(bool is_first) { + return absl::OkStatus(); + } + protected: virtual absl::Status WriteOperationString(absl::string_view operation_name, absl::string_view str) { @@ -71,6 +92,9 @@ class ExecuteQueryWriter { } }; +absl::Status PrintResults(std::unique_ptr iter, + std::ostream& out); + // Writes a human-readable representation of the query result to an output // stream. class ExecuteQueryStreamWriter : public ExecuteQueryWriter { diff --git a/zetasql/tools/execute_query/execute_query_writer_test.cc b/zetasql/tools/execute_query/execute_query_writer_test.cc index dd41b88e4..47828fdd6 100644 --- a/zetasql/tools/execute_query/execute_query_writer_test.cc +++ b/zetasql/tools/execute_query/execute_query_writer_test.cc @@ -29,6 +29,13 @@ namespace zetasql { +TEST(ExecuteQueryStreamWriterTest, Log) { + std::ostringstream output; + ZETASQL_EXPECT_OK(ExecuteQueryStreamWriter{output}.log("message1")); + ZETASQL_EXPECT_OK(ExecuteQueryStreamWriter{output}.log("message2\nmessage3")); + EXPECT_EQ(output.str(), "message1\nmessage2\nmessage3\n"); +} + TEST(ExecuteQueryStreamWriterTest, Parsed) { std::ostringstream output; ZETASQL_EXPECT_OK(ExecuteQueryStreamWriter{output}.parsed("short parser string")); diff --git a/zetasql/tools/execute_query/output_query_result.cc b/zetasql/tools/execute_query/output_query_result.cc index 53d0170db..bcf339c29 100644 --- a/zetasql/tools/execute_query/output_query_result.cc +++ b/zetasql/tools/execute_query/output_query_result.cc @@ -26,6 +26,7 @@ #include "zetasql/public/strings.h" #include "zetasql/reference_impl/type_helpers.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/types/span.h" @@ -247,6 +248,23 @@ std::string GetEscapedString(const Value& value) { return literal.substr(1, literal.length() - 2); } +// Returns a string to separate rows in an output table. A "+" indicates +// a column boundary, and "-" appears above or below a column. +// +// Example: +// +------+----+-----+------------+------------------------+---+---+ +std::string GetRowSeparator(absl::Span max_column_lengths) { + std::string separator = "+"; + for (int col_idx = 0; col_idx < max_column_lengths.size(); ++col_idx) { + absl::StrAppend(&separator, + std::string(max_column_lengths[col_idx] + 2, '-'), "+"); + } + absl::StrAppend(&separator, "\n"); + return separator; +} + +} // namespace + // Converts 'value' to an output string and returns it. std::string ValueToOutputString(const Value& value, bool escape_strings) { if (value.is_null()) return "NULL"; @@ -285,23 +303,6 @@ std::string ValueToOutputString(const Value& value, bool escape_strings) { } } -// Returns a string to separate rows in an output table. A "+" indicates -// a column boundary, and "-" appears above or below a column. -// -// Example: -// +------+----+-----+------------+------------------------+---+---+ -std::string GetRowSeparator(absl::Span max_column_lengths) { - std::string separator = "+"; - for (int col_idx = 0; col_idx < max_column_lengths.size(); ++col_idx) { - absl::StrAppend(&separator, - std::string(max_column_lengths[col_idx] + 2, '-'), "+"); - } - absl::StrAppend(&separator, "\n"); - return separator; -} - -} // namespace - std::string ToPrettyOutputStyle(const zetasql::Value& result, bool is_value_table, absl::Span column_names) { diff --git a/zetasql/tools/execute_query/output_query_result.h b/zetasql/tools/execute_query/output_query_result.h index a4e870431..cb5ad8cd9 100644 --- a/zetasql/tools/execute_query/output_query_result.h +++ b/zetasql/tools/execute_query/output_query_result.h @@ -70,6 +70,9 @@ std::string OutputPrettyStyleQueryResult( std::string OutputPrettyStyleExpressionResult(const zetasql::Value& result, bool include_box = true); +// Converts 'value' to an output string and returns it. +std::string ValueToOutputString(const Value& value, bool escape_strings); + } // namespace zetasql #endif // ZETASQL_TOOLS_EXECUTE_QUERY_OUTPUT_QUERY_RESULT_H_ diff --git a/zetasql/tools/execute_query/selectable_catalog.cc b/zetasql/tools/execute_query/selectable_catalog.cc new file mode 100644 index 000000000..3ec2d01db --- /dev/null +++ b/zetasql/tools/execute_query/selectable_catalog.cc @@ -0,0 +1,98 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/tools/execute_query/selectable_catalog.h" + +#include +#include +#include + +#include "zetasql/examples/tpch/catalog/tpch_catalog.h" +#include "zetasql/public/builtin_function_options.h" +#include "zetasql/public/catalog.h" +#include "zetasql/public/language_options.h" +#include "zetasql/public/simple_catalog.h" +#include "zetasql/testdata/sample_catalog_impl.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "zetasql/base/status_builder.h" +#include "zetasql/base/status_macros.h" + +namespace zetasql { + +// Get an initialized SampleCatalogImpl or an error. +static absl::StatusOr> GetSampleCatalog() { + auto catalog = std::make_unique(); + ZETASQL_RETURN_IF_ERROR(catalog->LoadCatalogImpl( + ZetaSQLBuiltinFunctionOptions(LanguageOptions()))); + return catalog; +} + +static std::vector* InitSelectableCatalogsVector() { + auto catalogs = std::make_unique>(); + + catalogs->push_back(new SelectableCatalog("none", "Empty catalog", [] { + static auto catalog = new SimpleCatalog("simple_catalog"); + return catalog; + })); + + catalogs->push_back(new SelectableCatalog( + "sample", "Analyzer-test schema", []() -> absl::StatusOr { + static auto* catalog = + new absl::StatusOr>( + GetSampleCatalog()); + ZETASQL_RETURN_IF_ERROR(catalog->status()); + return catalog->value()->catalog(); + })); + + catalogs->push_back(new SelectableCatalog( + "tpch", "TPCH tables (1MB)", []() -> absl::StatusOr { + static auto* catalog = + new absl::StatusOr>(MakeTpchCatalog()); + + ZETASQL_RETURN_IF_ERROR(catalog->status()); + return catalog->value().get(); + })); + + return catalogs.release(); +} + +const std::vector& GetSelectableCatalogs() { + static const auto* catalogs = InitSelectableCatalogsVector(); + return *catalogs; +} + +std::string GetSelectableCatalogDescriptionsForFlag() { + std::vector values; + for (const SelectableCatalog* catalog : GetSelectableCatalogs()) { + values.push_back( + absl::StrCat(catalog->name(), ": ", catalog->description())); + } + return absl::StrJoin(values, "\n"); +} + +absl::StatusOr FindSelectableCatalog( + const std::string& name) { + for (SelectableCatalog* catalog : GetSelectableCatalogs()) { + if (catalog->name() == name) { + return catalog; + } + } + return zetasql_base::InvalidArgumentErrorBuilder() << "Catalog not found: " << name; +} + +} // namespace zetasql diff --git a/zetasql/tools/execute_query/selectable_catalog.h b/zetasql/tools/execute_query/selectable_catalog.h new file mode 100644 index 000000000..1f45c043e --- /dev/null +++ b/zetasql/tools/execute_query/selectable_catalog.h @@ -0,0 +1,78 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#ifndef ZETASQL_TOOLS_EXECUTE_QUERY_SELECTABLE_CATALOG_H_ +#define ZETASQL_TOOLS_EXECUTE_QUERY_SELECTABLE_CATALOG_H_ + +#include +#include +#include + +#include "zetasql/public/catalog.h" +#include "zetasql/base/status.h" + +namespace zetasql { + +// This describes a Catalog that can be selected in execute_query. +class SelectableCatalog { + public: + // This is a callback that returns a Catalog. + // The callback retains ownership of the returned Catalog. + using GetCatalogCallback = std::function()>; + + SelectableCatalog(const std::string& name, const std::string& description, + GetCatalogCallback callback) + : name_(name), + description_(description), + get_catalog_callback_(callback) {} + virtual ~SelectableCatalog() = default; + + SelectableCatalog(const SelectableCatalog&) = delete; + + // The name can be used to select this catalog in flags, options, etc. + virtual std::string name() const { return name_; } + + // This is a description of this catalog. + virtual std::string description() const { return description_; } + + // This callback returns the actual Catalog object. + // It may be lazily created, and then shared across multiple requests. + // The caller does not take ownership, so this Catalog must stay alive. + virtual absl::StatusOr GetCatalog() { + return get_catalog_callback_(); + } + + private: + std::string name_; + std::string description_; + + GetCatalogCallback get_catalog_callback_; +}; + +// Get all known SelectableCatalogs. +const std::vector& GetSelectableCatalogs(); + +// Get descriptions of the SelectableCatalogs, for use in flag help. +// Formatted like "name: description\n" for each flag. +std::string GetSelectableCatalogDescriptionsForFlag(); + +// Find the SelectableCatalog called `name` and return it, or an error. +absl::StatusOr FindSelectableCatalog( + const std::string& name); + +} // namespace zetasql + +#endif // ZETASQL_TOOLS_EXECUTE_QUERY_SELECTABLE_CATALOG_H_ diff --git a/zetasql/tools/execute_query/selectable_catalog_test.cc b/zetasql/tools/execute_query/selectable_catalog_test.cc new file mode 100644 index 000000000..31db4e20a --- /dev/null +++ b/zetasql/tools/execute_query/selectable_catalog_test.cc @@ -0,0 +1,77 @@ +// +// Copyright 2019 Google LLC +// +// Licensed 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 +// +// 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. +// + +#include "zetasql/tools/execute_query/selectable_catalog.h" + +#include + +#include "zetasql/base/testing/status_matchers.h" +#include "zetasql/public/catalog.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace zetasql { + +TEST(SelectableCatalog, GetSelectableCatalogs) { + auto selectable_catalogs = GetSelectableCatalogs(); + EXPECT_GE(selectable_catalogs.size(), 2); + + EXPECT_EQ(selectable_catalogs[0]->name(), "none"); + EXPECT_EQ(selectable_catalogs[1]->name(), "sample"); +} + +TEST(SelectableCatalog, FindSelectableCatalog) { + EXPECT_FALSE(FindSelectableCatalog("bad").ok()); +} + +TEST(SelectableCatalog, FindSelectableCatalog_none) { + auto found = FindSelectableCatalog("none"); + ZETASQL_ASSERT_OK(found); + SelectableCatalog* selectable = found.value(); + + EXPECT_EQ(selectable->name(), "none"); + auto get_catalog_result = selectable->GetCatalog(); + ZETASQL_ASSERT_OK(get_catalog_result); + // There are no tables in this catalog to look up. +} + +// Test that catalog `catalog_name` can be found, and that it includes a table +// called `table_name`. +static void TestCatalog(const std::string& catalog_name, + const std::string& table_name) { + auto found = FindSelectableCatalog(catalog_name); + ZETASQL_ASSERT_OK(found); + SelectableCatalog* selectable = found.value(); + + EXPECT_EQ(selectable->name(), catalog_name); + auto get_catalog_result = selectable->GetCatalog(); + ZETASQL_ASSERT_OK(get_catalog_result); + Catalog* catalog = get_catalog_result.value(); + + const Table* table; + ZETASQL_EXPECT_OK(catalog->FindTable({table_name}, &table)); + EXPECT_EQ(table->Name(), table_name); +} + +TEST(SelectableCatalog, FindSelectableCatalog_sample) { + TestCatalog("sample", "TestTable"); +} + +TEST(SelectableCatalog, FindSelectableCatalog_tpch) { + TestCatalog("tpch", "LineItem"); +} + +} // namespace zetasql diff --git a/zetasql/tools/execute_query/testdata/execute_query_tool.test b/zetasql/tools/execute_query/testdata/execute_query_tool.test new file mode 100644 index 000000000..2db119a14 --- /dev/null +++ b/zetasql/tools/execute_query/testdata/execute_query_tool.test @@ -0,0 +1,1182 @@ +SELECT 1 x +-- ++---+ +| x | ++---+ +| 1 | ++---+ +\ +== + +[mode={{bad||parse|analyze|execute|explain|unparse|unanalyze|parse,analyze}}] +select 1 x +-- +ALTERNATION GROUP: bad +-- +ERROR: INVALID_ARGUMENT: Invalid --mode: 'bad' +-- +ALTERNATION GROUP: +-- +ERROR: INVALID_ARGUMENT: Invalid --mode: '' +-- +ALTERNATION GROUP: parse +-- +QueryStatement [0-10] + Query [0-10] + Select [0-10] + SelectList [7-10] + SelectColumn [7-10] + IntLiteral(1) [7-8] + Alias [9-10] + Identifier(x) [9-10] +\ +-- +ALTERNATION GROUP: analyze +-- +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +\ +-- +ALTERNATION GROUP: execute +-- ++---+ +| x | ++---+ +| 1 | ++---+ +\ +-- +ALTERNATION GROUP: explain +-- +ComputeOp( ++-map: { +| +-$x := ConstExpr(1)}, ++-input: EnumerateOp(ConstExpr(1))) +\ +-- +ALTERNATION GROUP: unparse +-- +SELECT + 1 AS x +\ +-- +ALTERNATION GROUP: unanalyze +-- +SELECT + 1 AS x; +\ +-- +ALTERNATION GROUP: parse,analyze +-- +QueryStatement [0-10] + Query [0-10] + Select [0-10] + SelectList [7-10] + SelectColumn [7-10] + IntLiteral(1) [7-8] + Alias [9-10] + Identifier(x) [9-10] + +QueryStmt ++-output_column_list= +| +-$query.x#1 AS x [INT64] ++-query= + +-ProjectScan + +-column_list=[$query.x#1] + +-expr_list= + | +-x#1 := Literal(type=INT64, value=1) + +-input_scan= + +-SingleRowScan +\ +== + +# Make sure ExplainAndOrExecuteSql runs just once, so we get one output +# for each of the two modes. +[mode=explain,execute] +select 1 x, 2 y +-- +ComputeOp( ++-map: { +| +-$x := ConstExpr(1), +| +-$y := ConstExpr(2)}, ++-input: EnumerateOp(ConstExpr(1))) + ++---+---+ +| x | y | ++---+---+ +| 1 | 2 | ++---+---+ +\ +== + +COMMIT +-- +ERROR: INVALID_ARGUMENT: The statement CommitStmt is not supported for execution. +== + +DESCRIBE bad_table +-- +ERROR: INVALID_ARGUMENT: Object not found +== + +DESCRIBE TaBlE bad_table +-- +ERROR: NOT_FOUND: Table not found: bad_table not found in catalog +== + +[mode={{analyze|explain|execute}}] +DESCRIBE TABLE Keyvalue +-- +ALTERNATION GROUP: analyze +-- +DescribeStmt(object_type="TABLE", name_path=Keyvalue) +\ +-- +ALTERNATION GROUP: explain +-- +ERROR: INVALID_ARGUMENT: The statement DescribeStmt is not supported for explanation. +-- +ALTERNATION GROUP: execute +-- +Table: KeyValue +Columns: + Key INT64 + Value STRING +\ +== + +[mode={{analyze|explain|execute}}] +EXPLAIN DESCRIBE TABLE Keyvalue +-- +ALTERNATION GROUP: analyze +-- +ExplainStmt ++-statement= + +-DescribeStmt(object_type="TABLE", name_path=Keyvalue) +\ +-- +ALTERNATION GROUP: explain +-- +ERROR: INVALID_ARGUMENT: The statement ExplainStmt is not supported for explanation. +-- +ALTERNATION GROUP: execute +-- +ERROR: INVALID_ARGUMENT: The statement ExplainStmt is not supported for execution. +== + +DESCRIBE KeyValue +-- +Table: KeyValue +Columns: + Key INT64 + Value STRING +\ +== + +[catalog={{none|bad||sample}}] +DESCRIBE KeyValue +-- +ALTERNATION GROUP: none +-- +ERROR: INVALID_ARGUMENT: Object not found +-- +ALTERNATION GROUP: bad +-- +ERROR: INVALID_ARGUMENT: Catalog not found: bad +-- +ALTERNATION GROUP: +-- +ERROR: INVALID_ARGUMENT: Catalog not found: +-- +ALTERNATION GROUP: sample +-- +Table: KeyValue +Columns: + Key INT64 + Value STRING +\ +== + +DESCRIBE {{|taBLE}} TestExtraValueTable +-- +Table: TestExtraValueTable (value table) +Row type: zetasql_test__.TestExtraPB +Columns: + Filename STRING (pseudo-column) + RowId BYTES (pseudo-column) +\ +== + +DESCRIBE nested_catalog.NestedKeyValue +-- +Table: KeyValue +Columns: + Key INT64 + Value STRING +\ +== + +DESCRIBE {{|fUNCtion}} bad_function +-- +ALTERNATION GROUP: +-- +ERROR: INVALID_ARGUMENT: Object not found +-- +ALTERNATION GROUP: fUNCtion +-- +ERROR: NOT_FOUND: Function not found: bad_function not found in catalog +== + +DESCRIBE {{|fUNCtion}} sqrt +-- +Function SQRT +Signature: SQRT(DOUBLE) -> DOUBLE +\ +== + +DESCRIBE {{|fUNCtion}} conCAT +-- +Function CONCAT +Signature: CONCAT(STRING, [STRING, ...]) -> STRING +Signature: CONCAT(BYTES, [BYTES, ...]) -> BYTES +\ +== + +DESCRIBE array_transform +-- +Function ARRAY_TRANSFORM +Signature: ARRAY_TRANSFORM(ARRAY, FUNCTIONT2>) -> ARRAY +Signature: ARRAY_TRANSFORM(ARRAY, FUNCTION<(T1, INT64)->T2>) -> ARRAY +\ +== + +DESCRIBE rank +-- +Analytic function RANK +Signature: RANK() -> INT64 +\ +== + +[catalog={{none|sample}}] +DESCRIBE sum +-- +ALTERNATION GROUP: none +-- +Aggregate function SUM +Signature: SUM(INT64) -> INT64 +Signature: SUM(UINT64) -> UINT64 +Signature: SUM(DOUBLE) -> DOUBLE +Signature: SUM(NUMERIC) -> NUMERIC +Signature: SUM(BIGNUMERIC) -> BIGNUMERIC +\ +-- +ALTERNATION GROUP: sample +-- +Aggregate function SUM +Signature: SUM(INT64) -> INT64 +Signature: SUM(UINT64) -> UINT64 +Signature: SUM(DOUBLE) -> DOUBLE +\ +== + +DESCRIBE {{|tVf}} bad_tvf +-- +ALTERNATION GROUP: +-- +ERROR: INVALID_ARGUMENT: Object not found +-- +ALTERNATION GROUP: tVf +-- +ERROR: NOT_FOUND: Table function not found: bad_tvf not found in catalog +== + +describe {{|tvf}} nested_catalog.nested_tvf_one +-- +Table-valued function: NESTED_CATALOG.NESTED_TVF_ONE(TABLE) -> TABLE +\ +== + +describe tvf_optional_named_default_args +-- +Table-valued function: TVF_OPTIONAL_NAMED_DEFAULT_ARGS(relation => TABLE, r1 => BOOL, [o0 => STRING], [o1 => DOUBLE], [o2 => UINT32]) -> TABLE +\ +== + +describe tvf_one_relation_arg_with_fixed_output +-- +Table-valued function: TVF_ONE_RELATION_ARG_WITH_FIXED_OUTPUT(TABLE) -> TABLE +\ +== + +describe tvf_no_arg_returning_value_table_with_pseudo_columns +-- +Table-valued function: TVF_NO_ARG_RETURNING_VALUE_TABLE_WITH_PSEUDO_COLUMNS() -> TABLE<`zetasql_test__.TestExtraPB`, RowId INT64, PartitionName STRING> +\ +== + +describe tvf_two_models_with_fixed_output +-- +Table-valued function: TVF_TWO_MODELS_WITH_FIXED_OUTPUT(MODEL, MODEL) -> TABLE + +
+
+

Query

+ + +
+
+
+ Mode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+
+ + +
+
+ +
+ {{#statements}} + {{#result_or_error}} +
+ {{#error}} +

Error

+
{{error}}
+ {{/error}} + {{#result}} +

Result

+ + {{#result_executed}} +

Execute

+ {{#result_executed_table}} +
{{> table}}
+ {{/result_executed_table}} + {{#result_executed_text}} +
{{result_executed_text}}
+ {{/result_executed_text}} + {{/result_executed}} + + {{#result_analyzed}} +

Analyze

+
{{result_analyzed}}
+ {{/result_analyzed}} + + {{#result_parsed}} +

Parse

+
{{result_parsed}}
+ {{/result_parsed}} + + {{#result_explained }} +

Explain

+
{{result_explained}}
+ {{/result_explained}} + + {{#result_unanalyzed}} +

Unanalyze

+
{{result_unanalyzed}}
+ {{/result_unanalyzed}} + + {{#result_unparsed}} +

Unparse

+
{{result_unparsed}}
+ {{/result_unparsed}} + + {{#result_log}} +

Log

+
{{result_log}}
+ {{/result_log}} + {{/result}} +
+ {{/result_or_error}} + {{#not_is_last}}
{{/not_is_last}} + {{/statements}} +
+ +
diff --git a/zetasql/tools/execute_query/web/page_template.html b/zetasql/tools/execute_query/web/page_template.html new file mode 100644 index 000000000..a840f3996 --- /dev/null +++ b/zetasql/tools/execute_query/web/page_template.html @@ -0,0 +1,14 @@ + + + ZetaSQL Execute Query + + + + + + + + + {{> body}} + + diff --git a/zetasql/tools/execute_query/web/style.css b/zetasql/tools/execute_query/web/style.css new file mode 100644 index 000000000..5afc5e871 --- /dev/null +++ b/zetasql/tools/execute_query/web/style.css @@ -0,0 +1,199 @@ +body { + background-color: #f0f8ff; + margin: 0; +} + +#header { + margin-bottom: 0.5rem; + background-color: #1597ec; + font-size: 2.25em; + font-family: Roboto, Arial, sans-serif; + font-weight: 700; + padding: 0.5rem 1rem; +} + +#header a { + text-decoration: none; + color: #fff; +} + +body, p { + font-family: system-ui, + -apple-system, BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen-Sans, + Ubuntu, + Cantarell, + 'Helvetica Neue', + Arial, sans-serif; +} + +body, input, textarea, select { + font-size: 1rem; +} + +select { + padding: 0.3em; + border-radius: 0.3em; +} + +main { + margin: 1em; +} + +textarea, pre { + font-family: ui-monospace, + Menlo, Monaco, + 'Cascadia Mono', 'Segoe UI Mono', + 'Roboto Mono', + 'Oxygen Mono', + 'Ubuntu Monospace', + 'Source Code Pro', + Consolas, + 'Courier New', monospace; +} + +main { + display: grid; + grid-template-columns: calc(40% - 25px) calc(60% - 25px); + gap: 20px 50px; + min-width: 650px; +} + +@media (max-width: 1600px) { + main { + display: block; + } +} + +textarea { + width: 100%; + min-height: 30em; + height: 60vh; + margin: 0; + padding: 0.7em; + outline: none; + border: 1px solid #ccc; + border-radius: 0.5rem; + margin-bottom: 0.7em; + resize: none; +} + +@media (max-width: 1600px) { + textarea { + height: auto; + } +} + +fieldset { + display: inline-block; + margin-bottom: 0.5em; + width: fit-content; + border-radius: 0.5rem; +} + +label { + margin-right: 0.5em; +} + +input[type='checkbox'] { + vertical-align: bottom; +} + +input[type='submit'] { + background-color: #0f73b5;; + border-radius: 0.5em; + border: 1px solid #0f73b5; + color: #fff; + cursor: pointer; + font-size: 1.1em; + margin-left: 1em; + margin-top: 0.5em; + max-height: 2.5em; + max-width: 6em; + min-width: 5em; + padding: 0.5em; +} + +input[type='submit']:hover { + background-color: #1597ec; + border-color: #1597ec; +} + +form { + container-type: inline-size; +} + +.options { + display: flex; +} + +.mode-option { + white-space: nowrap; +} + +.result > h3 { + margin-top: 2.5em; + margin-bottom: 0; + background-color: #1597ec; + color: #fff; + width: fit-content; + padding: 0.25em 0.5em; + border-radius: 0.5em 0.5em 0 0; +} + +.result > h3:first-of-type { + margin-top: auto; +} + +pre.output { + background-color: #fafafa; + border: 1px dashed #aaa; + padding: 0.5em; + margin-top: 0; + overflow: auto; +} + +pre.output code { + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + user-select: all; +} + +pre.output code.hljs { + background-color: transparent; + padding: 0; +} + +#error { + display: inline-block; + background-color: #f1a7a7; + margin: 0; + padding: 1em; +} + +pre.output > table { + border-collapse: collapse; + border: 1px solid #066eb3; + cursor: default; +} + +pre.output th { + background-color: #1597ec; + color: #fff; +} + +pre.output th, pre.output td { + border-right: 1px solid #066eb3; + padding: 0.25em 0.5em; +} + +pre.output tbody tr:nth-child(even) { + background: #f0f8ff; +} + +pre.output tbody tr:hover { + background: #eee; +} diff --git a/zetasql/tools/execute_query/web/table.html b/zetasql/tools/execute_query/web/table.html new file mode 100644 index 000000000..f615069b8 --- /dev/null +++ b/zetasql/tools/execute_query/web/table.html @@ -0,0 +1,18 @@ + + + + {{#columnNames}} + + {{/columnNames}} + + + + {{#rows}} + + {{#.}} + + {{/.}} + + {{/rows}} + +
{{.}}‌
{{.}}
\ No newline at end of file diff --git a/zetasql/tools/formatter/internal/BUILD b/zetasql/tools/formatter/internal/BUILD index 226793be0..7ea03d98d 100644 --- a/zetasql/tools/formatter/internal/BUILD +++ b/zetasql/tools/formatter/internal/BUILD @@ -100,6 +100,7 @@ cc_library( "//zetasql/public:parse_helpers", "//zetasql/public:parse_location", "//zetasql/public:parse_resume_location", + "//zetasql/public:type_cc_proto", "//zetasql/public:value", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/log", diff --git a/zetasql/tools/formatter/internal/chunk.cc b/zetasql/tools/formatter/internal/chunk.cc index ed5510696..1b8f42523 100644 --- a/zetasql/tools/formatter/internal/chunk.cc +++ b/zetasql/tools/formatter/internal/chunk.cc @@ -661,7 +661,8 @@ bool Chunk::IsTopLevelWith() const { bool Chunk::OpensParenBlock() const { return !Empty() && LastKeyword() == "("; } bool Chunk::OpensParenOrBracketBlock() const { - return IsOpenParenOrBracket(LastKeyword()); + return LastToken().Is(Token::Type::OPEN_BRACKET) || + IsOpenParenOrBracket(LastKeyword()); } bool Chunk::ClosesParenBlock() const { @@ -669,7 +670,8 @@ bool Chunk::ClosesParenBlock() const { } bool Chunk::ClosesParenOrBracketBlock() const { - return IsCloseParenOrBracket(FirstKeyword()); + return FirstToken().Is(Token::Type::CLOSE_BRACKET) || + IsCloseParenOrBracket(FirstKeyword()); } bool Chunk::IsFromKeyword() const { @@ -700,7 +702,7 @@ bool Chunk::IsTypeDeclarationStart() const { bool Chunk::IsTypeDeclarationEnd() const { return absl::c_any_of(tokens_.WithoutComments(), [](const Token* t) { - return t->Is(Token::Type::CLOSE_BRACKET); + return t->IsCloseAngleBracket(); }); } @@ -1006,10 +1008,16 @@ bool IsPartOfSameChunk(const Chunk& chunk, const std::vector& tokens, return true; } } - if (token_index > 0 && tokens[token_index - 1]->IsComment() && - tokens[token_index - 1]->GetLineBreaksAfter() > 0) { - // Previous token is either a stand-alone or end-of-line comment. - return false; + if (token_index > 0 && tokens[token_index - 1]->IsComment()) { + if (tokens[token_index - 1]->Is(Token::Type::STRING_ANNOTATION_COMMENT) && + tokens[token_index]->Is(Token::Type::OPEN_BRACKET)) { + // Group together annotation comment and the opening quotes of the + // string literal, e.g.: /*sql*/''' + return true; + } else if (tokens[token_index - 1]->GetLineBreaksAfter() > 0) { + // Previous token is either a stand-alone or end-of-line comment. + return false; + } } // All comment-related cases are handled above. If previous index is < 0 it // means there are no tokens before. @@ -1027,6 +1035,9 @@ bool IsPartOfSameChunk(const Chunk& chunk, const std::vector& tokens, // the chunk, otherwise no. if (current_token->IsComment()) { return current_token->GetLineBreaksBefore() == 0 && + // Annotation comments like /*sql*/ should be attached to the next + // token not to the previous one. + !current_token->Is(Token::Type::STRING_ANNOTATION_COMMENT) && // Special case: '/*arg_name=*/value' should be attached to the // value, not to the previous token. !IsArgumentNameInlineComment(*current_token); @@ -1073,6 +1084,15 @@ bool IsPartOfSameChunk(const Chunk& chunk, const std::vector& tokens, return true; } + // Never combine pipe operator with anything else. + if (current == "|>") { + return false; + } + if (previous == "|>") { + current_token->SetType(Token::Type::TOP_LEVEL_KEYWORD); + return false; + } + // For module declaration statements and imports, we basically ignore the line // length limit, so everything between before the ";" is a single chunk. if (chunk.IsModuleDeclaration() || chunk.IsImport()) { @@ -1157,20 +1177,21 @@ bool IsPartOfSameChunk(const Chunk& chunk, const std::vector& tokens, previous_token->Is(Token::Type::COMPLEX_TOKEN_CONTINUATION)) && CanBeFusedWithOpenParenOrBracket(chunk); } - if (current == "[") { - return CanBeFusedWithOpenParenOrBracket(chunk); - } if (current == ")") { return current_token->Is(Token::Type::CLOSE_PROTO_EXTENSION_PARENTHESIS); } - if (current == "]") { - return false; + if (current == "[" || current == "{" || + current_token->Is(Token::Type::OPEN_BRACKET)) { + return CanBeFusedWithOpenParenOrBracket(chunk); } - if (current == "}") { - return false; + if (current_token->IsCloseAngleBracket()) { + // Type declarations have closing bracket always attached to the previous + // token. + return true; } - if (current == "{") { - return CanBeFusedWithOpenParenOrBracket(chunk); + if (current == "]" || current == "}" || + current_token->Is(Token::Type::CLOSE_BRACKET)) { + return false; } // * is tricky. It's the multiplication operator, but it can also be part of @@ -1191,11 +1212,6 @@ bool IsPartOfSameChunk(const Chunk& chunk, const std::vector& tokens, // Special handling for <> signs, since these could be both comparison // operators and part of a type declaration, like ARRAY. - if (current_token->IsOneOf( - {Token::Type::OPEN_BRACKET, Token::Type::CLOSE_BRACKET})) { - // This is opening or closing bracket of a type declaration. - return true; - } if (previous == ">") { if (previous_token->Is(Token::Type::CLOSE_BRACKET)) { // Group ">" together in a nested type declaration, @@ -2463,7 +2479,7 @@ void MarkAllAngleBracketPairs(std::vector* chunks) { if (tokens.empty()) { continue; } - if (tokens.back()->Is(Token::Type::OPEN_BRACKET)) { + if (tokens.back()->IsOpenAngleBracket()) { open_brackets.push_back(chunk); continue; } @@ -2471,7 +2487,7 @@ void MarkAllAngleBracketPairs(std::vector* chunks) { continue; } for (const auto* token : tokens) { - if (token->Is(Token::Type::CLOSE_BRACKET)) { + if (token->IsCloseAngleBracket()) { Chunk* open_bracket = open_brackets.back(); open_brackets.pop_back(); open_bracket->SetMatchingClosingChunk(chunk); diff --git a/zetasql/tools/formatter/internal/chunk_grouping_strategy.cc b/zetasql/tools/formatter/internal/chunk_grouping_strategy.cc index c7e8c31b2..2bdc4cba0 100644 --- a/zetasql/tools/formatter/internal/chunk_grouping_strategy.cc +++ b/zetasql/tools/formatter/internal/chunk_grouping_strategy.cc @@ -40,7 +40,8 @@ void AddBlockForTopLevelClause(ChunkBlock* const chunk_block, } Chunk* chunk = (*i)->Chunk(); // Found an opening parenthesis -> indent inside parentheses. - if ((chunk->OpensParenBlock() && !chunk->HasMatchingClosingChunk()) || + if ((chunk->OpensParenOrBracketBlock() && + !chunk->HasMatchingClosingChunk()) || // Sometimes, user omit parentheses after DEFINE MACRO, but we should // indent nevertheless. chunk->IsStartOfDefineMacroStatement()) { @@ -50,6 +51,10 @@ void AddBlockForTopLevelClause(ChunkBlock* const chunk_block, // Found another top-level clause -> group together. t->AddChildChunk(top_level_chunk); return; + } else if (chunk->FirstKeyword() == "|>") { + // Pipe operator introduces indent of 3. + (*i)->AddIndentedChunk(top_level_chunk, /*offset=*/3); + return; } } t = t->Parent(); @@ -58,6 +63,34 @@ void AddBlockForTopLevelClause(ChunkBlock* const chunk_block, t->AddChildChunk(top_level_chunk); } +void AddBlockForPipeOperator(ChunkBlock* const chunk_block, Chunk* pipe_chunk) { + ChunkBlock* t = chunk_block; + + while (!t->IsTopLevel()) { + for (auto i = t->children().rbegin(); i != t->children().rend(); ++i) { + if (!(*i)->IsLeaf() || (*i)->Chunk()->Empty()) { + continue; + } + Chunk* chunk = (*i)->Chunk(); + // Found an opening parenthesis -> indent inside parentheses. + if ((chunk->OpensParenBlock() && !chunk->HasMatchingClosingChunk()) || + // Sometimes, users omit parentheses after DEFINE MACRO, but we should + // indent nevertheless. + chunk->IsStartOfDefineMacroStatement()) { + (*i)->AddIndentedChunk(pipe_chunk); + return; + } else if (chunk->FirstKeyword() == "|>") { + // Found another |> -> group together. + t->AddChildChunk(pipe_chunk); + return; + } + } + t = t->Parent(); + } + + t->AddChildChunk(pipe_chunk); +} + void AddBlockForSelectOrWith(Chunk* const previous_chunk, Chunk* select_or_with) { // Special handling for CREATE ... AS SELECT statement. @@ -927,6 +960,8 @@ absl::Status ComputeChunkBlocksForChunks(ChunkBlockFactory* block_factory, AddBlockForCreateOrExportIndentedClause(&previous_chunk, &chunk); } else if (chunk.IsTopLevelClauseChunk()) { AddBlockForTopLevelClause(chunk_block, &chunk); + } else if (chunk.FirstKeyword() == "|>") { + AddBlockForPipeOperator(chunk_block, &chunk); } else if (chunk.IsJoin()) { AddBlockForJoinClause(chunk_block, &chunk); } else if (chunk.FirstKeyword() == "ON" || diff --git a/zetasql/tools/formatter/internal/fusible_tokens.cc b/zetasql/tools/formatter/internal/fusible_tokens.cc index 1beb2785a..e283576a7 100644 --- a/zetasql/tools/formatter/internal/fusible_tokens.cc +++ b/zetasql/tools/formatter/internal/fusible_tokens.cc @@ -99,6 +99,10 @@ FusibleMatchVector FindLongestFusibleMatch( // like "(" are also keywords) - no match. continue; } + } else if (fused_keyword == FusibleTokens::kString) { + if (!tokens[match_pos]->IsStringLiteral()) { + continue; + } } else if (tokens[match_pos]->GetKeyword() != fused_keyword || (next_group.type != Token::Type::UNKNOWN && !tokens[match_pos]->Is(next_group.type)) || diff --git a/zetasql/tools/formatter/internal/fusible_tokens.h b/zetasql/tools/formatter/internal/fusible_tokens.h index cad346b3a..14dded066 100644 --- a/zetasql/tools/formatter/internal/fusible_tokens.h +++ b/zetasql/tools/formatter/internal/fusible_tokens.h @@ -53,6 +53,8 @@ struct FusibleTokens { // parts of the identifier will be appended later by the default logic in // IsPartOfSameChunk. static constexpr char kIdentifier[] = ""; + // kString matches any string literal, e.g. 'a string'. + static constexpr char kString[] = ""; // Contains the list of tokens. The name is short to make initialization list // less verbose. diff --git a/zetasql/tools/formatter/internal/layout.cc b/zetasql/tools/formatter/internal/layout.cc index 244f75778..27fe4392d 100644 --- a/zetasql/tools/formatter/internal/layout.cc +++ b/zetasql/tools/formatter/internal/layout.cc @@ -1034,7 +1034,11 @@ void StmtLayout::PruneLineBreaks() { if (curr_line_level > prev_line_level && // Next line should have smaller indent, otherwise we create indent // jump by 4. - curr_line_level >= next_line_level) { + (curr_line_level >= next_line_level || + // We allow jump by 5 after the pipe operator, e.g.: + // |> WHERE + // condition = FALSE + prev_chunk.FirstKeyword() == "|>")) { size_t prev_chunk_length = prev_chunk.PrintableLength(false); if (first_chunk.StartsWithSpace()) { ++prev_chunk_length; @@ -1355,6 +1359,10 @@ int StmtLayout::BestBreakpoint(const Line& line) const { // further. best_chunk = position; skip_until = best_chunk; + } else if (chunk->FirstKeyword() == "|>") { + // Prefer breaking query at pipe operators. + best_chunk = position; + skip_until = best_chunk; } } else if (chunk->FirstToken().Is( Token::Type::JOIN_CONDITION_KEYWORD)) { diff --git a/zetasql/tools/formatter/internal/parsed_file.cc b/zetasql/tools/formatter/internal/parsed_file.cc index de78cb385..5a3758ff2 100644 --- a/zetasql/tools/formatter/internal/parsed_file.cc +++ b/zetasql/tools/formatter/internal/parsed_file.cc @@ -44,6 +44,7 @@ #include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "unicode/unistr.h" +#include "unicode/utypes.h" #include "zetasql/base/status_macros.h" namespace zetasql::formatter::internal { @@ -144,9 +145,8 @@ absl::StatusOr> TokenizedStmt::ParseFrom( const ParseLocationTranslator& location_translator, const FormatterOptions& options) { const int start_offset = parse_location->byte_position(); - ZETASQL_ASSIGN_OR_RETURN( - std::vector tokens, - TokenizeNextStatement(parse_location, options.IsAllowedInvalidTokens())); + ZETASQL_ASSIGN_OR_RETURN(std::vector tokens, + TokenizeNextStatement(parse_location, options)); auto parsed_sql = std::make_unique( parse_location->input(), start_offset, parse_location->byte_position(), std::move(tokens), options); diff --git a/zetasql/tools/formatter/internal/token.cc b/zetasql/tools/formatter/internal/token.cc index fb33775bc..c31f52106 100644 --- a/zetasql/tools/formatter/internal/token.cc +++ b/zetasql/tools/formatter/internal/token.cc @@ -32,6 +32,7 @@ #include "zetasql/public/parse_location.h" #include "zetasql/public/parse_resume_location.h" #include "zetasql/public/parse_tokens.h" +#include "zetasql/public/type.pb.h" #include "zetasql/public/value.h" #include "absl/container/flat_hash_map.h" #include "absl/log/log.h" @@ -615,6 +616,8 @@ enum class GroupingType { // In exceptional cases we need to drop the remaining parsed tokens and // reparse the rest of sql. kNeedRetokenizing, + // Embedded SQL inside a string literal. + kEmbeddedSql, }; // Holds a state of grouping multiple ZetaSQL tokens into a single formatter @@ -758,6 +761,18 @@ bool IsDoubleSlashComment(const ParseToken& parse_token, !SpaceBetweenTokensInInput(prev_token, parse_token); } +// Returns true if the given `parse_token` is a string literal annotated as an +// embedded SQL string, e.g.: /*sql*/"SELECT 1;" +bool IsEmbeddedSqlStart(const ParseToken& parse_token, + const Token& prev_token) { + return parse_token.IsValue() && + parse_token.GetValue().type_kind() == TYPE_STRING && + prev_token.IsSlashStarComment() && + StrContainsIgnoreCase(prev_token.GetImage(), "/*sql*/") && + !absl::StripAsciiWhitespace(parse_token.GetValue().string_value()) + .empty(); +} + // Updates grouping state with the end of no format region if the given // `parse_token` ends it. void MaybeUpdateEndOfNoFormatRegion(const ParseToken& parse_token, @@ -1013,6 +1028,31 @@ void UpdateGroupingStateForDoubleSlashComment( FindLineEnd(sql, TokenStartOffset(comment_start) + 2); } +// Updates grouping state for an embedded SQL string. This is a special case +// when we need to re-tokenize the contents of the string, so this function +// calculates the range to be re-tokenized. +void UpdateGroupingStateForEmbeddedSql(const ParseToken& embedded_sql, + TokenGroupingState* grouping_state) { + grouping_state->type = GroupingType::kEmbeddedSql; + const int quotes_length = + static_cast(embedded_sql.GetImage().size() - + embedded_sql.GetValue().string_value().size()); + // Find the start and end of the embedded SQL string within quotes. This + // handles single quotes, triple quotes and string literal prefixes, e.g.: + // r'raw string'. + int content_start = TokenStartOffset(embedded_sql); + int content_end = TokenEndOffset(embedded_sql); + if (quotes_length >= 6) { // Triple quotes ''', """ or r'''. + content_start += quotes_length - 3; + content_end -= 3; + } else { // Single quotes '' or "", or r''. + content_start += quotes_length - 1; + content_end -= 1; + } + grouping_state->start_position = content_start; + grouping_state->end_position = content_end; +} + // Creates a zetasql::ParseToken. ParseToken CreateParseToken(absl::string_view sql, int start_position, int end_position, ParseToken::Kind kind, @@ -1149,6 +1189,11 @@ TokenGroupingState MaybeMoveParseTokenIntoTokens( // comment token. tokens.pop_back(); return grouping_state; + } else if (!tokens.empty() && + IsEmbeddedSqlStart(parse_token, tokens.back())) { + UpdateGroupingStateForEmbeddedSql(parse_token, &grouping_state); + tokens.back().SetType(Token::Type::STRING_ANNOTATION_COMMENT); + return grouping_state; } break; } @@ -1361,6 +1406,10 @@ TokenGroupingState MaybeMoveParseTokenIntoTokens( case GroupingType::kNeedRetokenizing: { return grouping_state; } + + case GroupingType::kEmbeddedSql: { + return grouping_state; + } } // Default action: copy the token to the result as is. @@ -1417,6 +1466,67 @@ void ConvertEnclosingDoubleQuotesToSingleIfSafe(std::string& image) { enclosing_quotes_length, '\''); } +// Creates a token that represents opening or closing quotes of a string +// literal, whose content is parsed as SQL. In this case, quotes of the string +// act as parentheses for the contents inside. +Token CreateTokenForQuotes(absl::string_view sql, int start_position, + int end_position, Token::Type type) { + // Omit 'r' or 'b' that might appear before opening quotes of a string + // literal, so that Token::GetKeyword() returns quotes without this prefix, + // but Token::GetImage() still contains the full string. + int quotes_start = start_position; + while (quotes_start < end_position && sql[quotes_start] != '\'' && + sql[quotes_start] != '"') { + quotes_start++; + } + absl::string_view quotes = + sql.substr(quotes_start, end_position - quotes_start); + Token token( + CreateParseToken(sql, start_position, end_position, ParseToken::KEYWORD), + quotes); + token.SetType(type); + return token; +} + +// Parses contents of a string literal as SQL and adds the resulting tokens to +// the `tokens` vector. +absl::Status ParseEmbeddedSql(const ParseToken& string_literal, + absl::string_view sql, + const TokenGroupingState& grouping_state, + const FormatterOptions& options, + std::vector& tokens) { + // Create a token that represents opening quotes of the string literal. + tokens.push_back(CreateTokenForQuotes(sql, TokenStartOffset(string_literal), + grouping_state.start_position, + Token::Type::OPEN_BRACKET)); + + // Parse substring inside the quotes. We preserve all bytes before the string + // start to get the correct byte offsets. + absl::string_view embedded_sql = sql.substr(0, grouping_state.end_position); + ParseResumeLocation embedded_location = + ParseResumeLocation::FromStringView(embedded_sql); + embedded_location.set_byte_position(grouping_state.start_position); + // Do not format structured strings inside structured strings recursively. + FormatterOptions embedded_options = options; + embedded_options.FormatStructuredStrings(false); + while (embedded_location.byte_position() < grouping_state.end_position) { + ZETASQL_ASSIGN_OR_RETURN( + std::vector embedded_tokens, + TokenizeNextStatement(&embedded_location, embedded_options)); + if (embedded_tokens.empty()) { + break; + } + for (auto&& token : embedded_tokens) { + tokens.push_back(std::move(token)); + } + } + // Create a token that represents closing quotes of the string literal. + tokens.push_back(CreateTokenForQuotes(sql, grouping_state.end_position, + TokenEndOffset(string_literal), + Token::Type::CLOSE_BRACKET)); + return absl::OkStatus(); +} + } // namespace Token ParseInvalidToken(ParseResumeLocation* resume_location, @@ -1470,11 +1580,10 @@ Token ParseInvalidToken(ParseResumeLocation* resume_location, } absl::StatusOr> TokenizeStatement( - absl::string_view sql, bool allow_invalid_tokens) { + absl::string_view sql, const FormatterOptions& options) { ParseResumeLocation parse_location = ParseResumeLocation::FromStringView(sql); - ZETASQL_ASSIGN_OR_RETURN( - std::vector tokens, - TokenizeNextStatement(&parse_location, allow_invalid_tokens)); + ZETASQL_ASSIGN_OR_RETURN(std::vector tokens, + TokenizeNextStatement(&parse_location, options)); if (parse_location.byte_position() < sql.size()) { return absl::InvalidArgumentError(absl::StrCat( "TokenizeStatement(absl::string_view sql, bool allow_invalid_tokens) " @@ -1488,7 +1597,7 @@ absl::StatusOr> TokenizeStatement( } absl::StatusOr> TokenizeNextStatement( - ParseResumeLocation* parse_location, bool allow_invalid_tokens) { + ParseResumeLocation* parse_location, const FormatterOptions& options) { LanguageOptions language_options; language_options.EnableMaximumLanguageFeaturesForDevelopment(); // TODO Allow V_1_4_SQL_MACROS as well @@ -1518,9 +1627,19 @@ absl::StatusOr> TokenizeNextStatement( grouping_state.Reset(); break; } + if (grouping_state.type == GroupingType::kEmbeddedSql) { + if (options.IsFormattingStructuredStrings()) { + ZETASQL_RETURN_IF_ERROR(ParseEmbeddedSql(token, parse_location->input(), + grouping_state, options, tokens)); + } else { + // Do not reparse the string literal. + tokens.emplace_back(std::move(token)); + } + grouping_state.Reset(); + } } if (!parse_status.ok()) { - if (!allow_invalid_tokens) { + if (!options.IsAllowedInvalidTokens()) { return parse_status; } // ZetaSQL tokenizer couldn't parse any tokens at all. Unblock it by @@ -1630,6 +1749,19 @@ bool Token::IsCaseExprKeyword() const { kCaseClauseKeywords->contains(GetKeyword()); } +bool Token::IsStringLiteral() const { + return IsValue() && + GetValue().type_kind() == zetasql::TypeKind::TYPE_STRING; +} + +bool Token::IsOpenAngleBracket() const { + return Is(Type::OPEN_BRACKET) && GetKeyword() == "<"; +} + +bool Token::IsCloseAngleBracket() const { + return Is(Type::CLOSE_BRACKET) && GetKeyword() == ">"; +} + bool Token::MayBeIdentifier() const { return !UsedAsKeyword() && (IsIdentifier() || Is(Token::Type::KEYWORD_AS_IDENTIFIER_FRAGMENT) || @@ -1799,7 +1931,7 @@ bool IsCloseParenOrBracket(absl::string_view keyword) { absl::string_view CorrespondingOpenBracket(absl::string_view keyword) { if (keyword.empty()) { - return ""; + return ""; } switch (keyword[0]) { case ')': @@ -1808,8 +1940,16 @@ absl::string_view CorrespondingOpenBracket(absl::string_view keyword) { return "["; case '}': return "{"; + case '>': + return "<"; + // Quotes behave as parentheses when the contents of a string literal is + // parsed as SQL. + case '\'': + return keyword.length() == 1 ? "'" : "'''"; + case '"': + return keyword.length() == 1 ? "\"" : "\"\"\""; default: - return ""; + return ""; } } diff --git a/zetasql/tools/formatter/internal/token.h b/zetasql/tools/formatter/internal/token.h index be432f378..9f708ff6c 100644 --- a/zetasql/tools/formatter/internal/token.h +++ b/zetasql/tools/formatter/internal/token.h @@ -44,6 +44,8 @@ class Token : public ParseToken { public: enum class Type { UNKNOWN, + // Some tokens can act as brackets depending on the context. For instance, + // < >. OPEN_BRACKET, CLOSE_BRACKET, OPEN_PROTO_EXTENSION_PARENTHESIS, @@ -125,10 +127,15 @@ class Token : public ParseToken { // Marks the beginning of a field name in braced constructor. It can be '(' // for proto extensions. BRACED_CONSTR_FIELD, + // Marks an inline comment that annotates the following string literal. + // Example: /*sql*/'''select 1''' + STRING_ANNOTATION_COMMENT, }; explicit Token(ParseToken t) : ParseToken(std::move(t)), keyword_(ParseToken::GetKeyword()) {} + Token(ParseToken t, absl::string_view keyword) + : ParseToken(std::move(t)), keyword_(keyword) {} // Sets the additional semantic type of the token. void SetType(Type type) { type_ = type; } @@ -167,6 +174,16 @@ class Token : public ParseToken { // (one of: "WHEN", "THEN", "ELSE", "END", but not "CASE" itself). bool IsCaseExprKeyword() const; + // Returns true if the current token is a string literal. + bool IsStringLiteral() const; + + // Returns true if the current token is marked as open angle bracket: "<" (but + // not a comparison operator). + bool IsOpenAngleBracket() const; + // Returns true if the current token is marked as close angle bracket: ">" + // (but not a comparison operator). + bool IsCloseAngleBracket() const; + // Checks if the current token may be an identifier (but may be also a non // reserved keyword). bool MayBeIdentifier() const; @@ -235,12 +252,12 @@ class TokensView { // Parses given `sql` into tokens using ZetaSQL tokenizer. The string must // contain a single SQL statement, otherwise InvalidArgument error is returned. -absl::StatusOr> TokenizeStatement(absl::string_view sql, - bool allow_invalid_tokens); +absl::StatusOr> TokenizeStatement( + absl::string_view sql, const FormatterOptions& options); // Parses next statement from the input into tokens using given // `parse_location`. Updates the location to point to the end of parsed input. absl::StatusOr> TokenizeNextStatement( - ParseResumeLocation* parse_location, bool allow_invalid_tokens); + ParseResumeLocation* parse_location, const FormatterOptions& options); // Parses a token that ZetaSQL tokenizer was unable to parse. Updates // `resume_location` to point after the parsed token. Optional end_position can